alpha
This commit is contained in:
@@ -0,0 +1,281 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
internal class AnimationsHelper
|
||||
{
|
||||
public static void ShowWithFadeIn(UIElement element, double duration = 0.15)
|
||||
{
|
||||
if (element.Visibility == Visibility.Visible) return;
|
||||
|
||||
if (element == null)
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
|
||||
var sb = new Storyboard();
|
||||
|
||||
// 渐变动画
|
||||
var fadeInAnimation = new DoubleAnimation
|
||||
{
|
||||
From = 0.5,
|
||||
To = 1,
|
||||
Duration = TimeSpan.FromSeconds(duration)
|
||||
};
|
||||
Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath(UIElement.OpacityProperty));
|
||||
|
||||
sb.Children.Add(fadeInAnimation);
|
||||
|
||||
element.Visibility = Visibility.Visible;
|
||||
|
||||
sb.Begin((FrameworkElement)element);
|
||||
}
|
||||
|
||||
public static void ShowWithSlideFromBottomAndFade(UIElement element, double duration = 0.15)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (element.Visibility == Visibility.Visible) return;
|
||||
|
||||
if (element == null)
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
|
||||
var sb = new Storyboard();
|
||||
|
||||
// 渐变动画
|
||||
var fadeInAnimation = new DoubleAnimation
|
||||
{
|
||||
From = 0.5,
|
||||
To = 1,
|
||||
Duration = TimeSpan.FromSeconds(duration)
|
||||
};
|
||||
fadeInAnimation.EasingFunction = new CubicEase();
|
||||
|
||||
Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath(UIElement.OpacityProperty));
|
||||
|
||||
// 滑动动画
|
||||
var slideAnimation = new DoubleAnimation
|
||||
{
|
||||
From = element.RenderTransform.Value.OffsetY + 10, // 滑动距离
|
||||
To = 0,
|
||||
Duration = TimeSpan.FromSeconds(duration)
|
||||
};
|
||||
Storyboard.SetTargetProperty(slideAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.Y)"));
|
||||
|
||||
slideAnimation.EasingFunction = new CubicEase();
|
||||
|
||||
sb.Children.Add(fadeInAnimation);
|
||||
sb.Children.Add(slideAnimation);
|
||||
|
||||
element.Visibility = Visibility.Visible;
|
||||
element.RenderTransform = new TranslateTransform();
|
||||
|
||||
sb.Begin((FrameworkElement)element);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public static void ShowWithSlideFromLeftAndFade(UIElement element, double duration = 0.25)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (element.Visibility == Visibility.Visible) return;
|
||||
|
||||
if (element == null)
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
|
||||
var sb = new Storyboard();
|
||||
|
||||
// 渐变动画
|
||||
var fadeInAnimation = new DoubleAnimation
|
||||
{
|
||||
From = 0.5,
|
||||
To = 1,
|
||||
Duration = TimeSpan.FromSeconds(duration)
|
||||
};
|
||||
Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath(UIElement.OpacityProperty));
|
||||
|
||||
// 滑动动画
|
||||
var slideAnimation = new DoubleAnimation
|
||||
{
|
||||
From = element.RenderTransform.Value.OffsetX - 20, // 滑动距离
|
||||
To = 0,
|
||||
Duration = TimeSpan.FromSeconds(duration)
|
||||
};
|
||||
Storyboard.SetTargetProperty(slideAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)"));
|
||||
|
||||
sb.Children.Add(fadeInAnimation);
|
||||
sb.Children.Add(slideAnimation);
|
||||
|
||||
element.Visibility = Visibility.Visible;
|
||||
element.RenderTransform = new TranslateTransform();
|
||||
|
||||
sb.Begin((FrameworkElement)element);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public static void ShowWithScaleFromLeft(UIElement element, double duration = 0.2)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (element.Visibility == Visibility.Visible) return;
|
||||
|
||||
if (element == null)
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
|
||||
var sb = new Storyboard();
|
||||
|
||||
// 水平方向的缩放动画
|
||||
var scaleXAnimation = new DoubleAnimation
|
||||
{
|
||||
From = 0,
|
||||
To = 1,
|
||||
Duration = TimeSpan.FromSeconds(duration)
|
||||
};
|
||||
Storyboard.SetTargetProperty(scaleXAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)"));
|
||||
|
||||
// 垂直方向的缩放动画
|
||||
var scaleYAnimation = new DoubleAnimation
|
||||
{
|
||||
From = 0,
|
||||
To = 1,
|
||||
Duration = TimeSpan.FromSeconds(duration)
|
||||
};
|
||||
scaleYAnimation.EasingFunction = new CubicEase();
|
||||
scaleXAnimation.EasingFunction = new CubicEase();
|
||||
Storyboard.SetTargetProperty(scaleYAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)"));
|
||||
|
||||
sb.Children.Add(scaleXAnimation);
|
||||
sb.Children.Add(scaleYAnimation);
|
||||
|
||||
element.Visibility = Visibility.Visible;
|
||||
element.RenderTransformOrigin = new Point(0, 0.5); // 左侧中心点为基准
|
||||
element.RenderTransform = new ScaleTransform(0, 0);
|
||||
|
||||
sb.Begin((FrameworkElement)element);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public static void ShowWithScaleFromRight(UIElement element, double duration = 0.2)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (element.Visibility == Visibility.Visible) return;
|
||||
|
||||
if (element == null)
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
|
||||
var sb = new Storyboard();
|
||||
|
||||
// 水平方向的缩放动画
|
||||
var scaleXAnimation = new DoubleAnimation
|
||||
{
|
||||
From = 0,
|
||||
To = 1,
|
||||
Duration = TimeSpan.FromSeconds(duration)
|
||||
};
|
||||
Storyboard.SetTargetProperty(scaleXAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)"));
|
||||
|
||||
// 垂直方向的缩放动画
|
||||
var scaleYAnimation = new DoubleAnimation
|
||||
{
|
||||
From = 0,
|
||||
To = 1,
|
||||
Duration = TimeSpan.FromSeconds(duration)
|
||||
};
|
||||
Storyboard.SetTargetProperty(scaleYAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)"));
|
||||
|
||||
scaleYAnimation.EasingFunction = new CubicEase();
|
||||
scaleXAnimation.EasingFunction = new CubicEase();
|
||||
|
||||
sb.Children.Add(scaleXAnimation);
|
||||
sb.Children.Add(scaleYAnimation);
|
||||
|
||||
element.Visibility = Visibility.Visible;
|
||||
element.RenderTransformOrigin = new Point(1, 0.5); // 右侧中心点为基准
|
||||
element.RenderTransform = new ScaleTransform(0, 0);
|
||||
|
||||
sb.Begin((FrameworkElement)element);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public static void HideWithSlideAndFade(UIElement element, double duration = 0.15)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (element.Visibility == Visibility.Collapsed) return;
|
||||
|
||||
if (element == null)
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
|
||||
var sb = new Storyboard();
|
||||
|
||||
// 渐变动画
|
||||
var fadeOutAnimation = new DoubleAnimation
|
||||
{
|
||||
From = 1,
|
||||
To = 0,
|
||||
Duration = TimeSpan.FromSeconds(duration)
|
||||
};
|
||||
fadeOutAnimation.EasingFunction = new CubicEase();
|
||||
Storyboard.SetTargetProperty(fadeOutAnimation, new PropertyPath(UIElement.OpacityProperty));
|
||||
|
||||
// 滑动动画
|
||||
var slideAnimation = new DoubleAnimation
|
||||
{
|
||||
From = 0,
|
||||
To = element.RenderTransform.Value.OffsetY + 10, // 滑动距离
|
||||
Duration = TimeSpan.FromSeconds(duration)
|
||||
};
|
||||
slideAnimation.EasingFunction = new CubicEase();
|
||||
|
||||
Storyboard.SetTargetProperty(slideAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.Y)"));
|
||||
|
||||
sb.Children.Add(fadeOutAnimation);
|
||||
sb.Children.Add(slideAnimation);
|
||||
|
||||
sb.Completed += (s, e) =>
|
||||
{
|
||||
element.Visibility = Visibility.Collapsed;
|
||||
};
|
||||
|
||||
element.RenderTransform = new TranslateTransform();
|
||||
sb.Begin((FrameworkElement)element);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public static void HideWithFadeOut(UIElement element, double duration = 0.15)
|
||||
{
|
||||
if (element.Visibility == Visibility.Collapsed) return;
|
||||
|
||||
if (element == null)
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
|
||||
var sb = new Storyboard();
|
||||
|
||||
// 渐变动画
|
||||
var fadeOutAnimation = new DoubleAnimation
|
||||
{
|
||||
From = 1,
|
||||
To = 0,
|
||||
Duration = TimeSpan.FromSeconds(duration)
|
||||
};
|
||||
Storyboard.SetTargetProperty(fadeOutAnimation, new PropertyPath(UIElement.OpacityProperty));
|
||||
|
||||
sb.Children.Add(fadeOutAnimation);
|
||||
|
||||
sb.Completed += (s, e) =>
|
||||
{
|
||||
element.Visibility = Visibility.Collapsed;
|
||||
};
|
||||
|
||||
sb.Begin((FrameworkElement)element);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
internal class AutoUpdateHelper
|
||||
{
|
||||
public static async Task<string> CheckForUpdates(string proxy = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
string localVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
string remoteAddress = proxy;
|
||||
remoteAddress += "https://gitea.bliemhax.com/kriastans/InkCanvasForClass/raw/branch/master/AutomaticUpdateVersionControl.txt";
|
||||
string remoteVersion = await GetRemoteVersion(remoteAddress);
|
||||
|
||||
if (remoteVersion != null)
|
||||
{
|
||||
Version local = new Version(localVersion);
|
||||
Version remote = new Version(remoteVersion);
|
||||
if (remote > local)
|
||||
{
|
||||
LogHelper.WriteLogToFile("AutoUpdate | New version Availble: " + remoteVersion);
|
||||
return remoteVersion;
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile("Failed to retrieve remote version.", LogHelper.LogType.Error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"AutoUpdate | Error: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<string> GetRemoteVersion(string fileUrl)
|
||||
{
|
||||
using (HttpClient client = new HttpClient())
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpResponseMessage response = await client.GetAsync(fileUrl);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | HTTP request error: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Error: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static string updatesFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Ink Canvas Annotation", "AutoUpdate");
|
||||
private static string statusFilePath = null;
|
||||
|
||||
public static async Task<bool> DownloadSetupFileAndSaveStatus(string version, string proxy = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
statusFilePath = Path.Combine(updatesFolderPath, $"DownloadV{version}Status.txt");
|
||||
|
||||
if (File.Exists(statusFilePath) && File.ReadAllText(statusFilePath).Trim().ToLower() == "true")
|
||||
{
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Setup file already downloaded.");
|
||||
return true;
|
||||
}
|
||||
|
||||
string downloadUrl = $"{proxy}https://github.com/ChangSakura/Ink-Canvas/releases/download/v{version}/Ink.Canvas.Annotation.V{version}.Setup.exe";
|
||||
|
||||
SaveDownloadStatus(false);
|
||||
await DownloadFile(downloadUrl, $"{updatesFolderPath}\\Ink.Canvas.Annotation.V{version}.Setup.exe");
|
||||
SaveDownloadStatus(true);
|
||||
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Setup file successfully downloaded.");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Error downloading and installing update: {ex.Message}", LogHelper.LogType.Error);
|
||||
|
||||
SaveDownloadStatus(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task DownloadFile(string fileUrl, string destinationPath)
|
||||
{
|
||||
using (HttpClient client = new HttpClient())
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpResponseMessage response = await client.GetAsync(fileUrl);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
using (FileStream fileStream = File.Create(destinationPath))
|
||||
{
|
||||
await response.Content.CopyToAsync(fileStream);
|
||||
fileStream.Close();
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
Console.WriteLine($"AutoUpdate | HTTP request error: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"AutoUpdate | Error: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SaveDownloadStatus(bool isSuccess)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (statusFilePath == null) return;
|
||||
|
||||
string directory = Path.GetDirectoryName(statusFilePath);
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
File.WriteAllText(statusFilePath, isSuccess.ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Error saving download status: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
public static void InstallNewVersionApp(string version, bool isInSilence)
|
||||
{
|
||||
try
|
||||
{
|
||||
string setupFilePath = Path.Combine(updatesFolderPath, $"Ink.Canvas.Annotation.V{version}.Setup.exe");
|
||||
|
||||
if (!File.Exists(setupFilePath))
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Setup file not found: {setupFilePath}", LogHelper.LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
string InstallCommand = $"\"{setupFilePath}\" /SILENT";
|
||||
if (isInSilence) InstallCommand += " /VERYSILENT";
|
||||
ExecuteCommandLine(InstallCommand);
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
Application.Current.Shutdown();
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Error installing update: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void ExecuteCommandLine(string command)
|
||||
{
|
||||
try
|
||||
{
|
||||
ProcessStartInfo processStartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "cmd.exe",
|
||||
Arguments = $"/c {command}",
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using (Process process = new Process { StartInfo = processStartInfo })
|
||||
{
|
||||
process.Start();
|
||||
Application.Current.Shutdown();
|
||||
/*process.WaitForExit();
|
||||
int exitCode = process.ExitCode;*/
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public static void DeleteUpdatesFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(updatesFolderPath))
|
||||
{
|
||||
Directory.Delete(updatesFolderPath, true);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate clearing| Error deleting updates folder: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class AutoUpdateWithSilenceTimeComboBox
|
||||
{
|
||||
public static ObservableCollection<string> Hours { get; set; } = new ObservableCollection<string>();
|
||||
public static ObservableCollection<string> Minutes { get; set; } = new ObservableCollection<string>();
|
||||
|
||||
public static void InitializeAutoUpdateWithSilenceTimeComboBoxOptions(ComboBox startTimeComboBox, ComboBox endTimeComboBox)
|
||||
{
|
||||
for (int hour = 0; hour <= 23; ++hour)
|
||||
{
|
||||
Hours.Add(hour.ToString("00"));
|
||||
}
|
||||
for (int minute = 0; minute <= 59; minute += 20)
|
||||
{
|
||||
Minutes.Add(minute.ToString("00"));
|
||||
}
|
||||
startTimeComboBox.ItemsSource = Hours.SelectMany(h => Minutes.Select(m => $"{h}:{m}"));
|
||||
endTimeComboBox.ItemsSource = Hours.SelectMany(h => Minutes.Select(m => $"{h}:{m}"));
|
||||
}
|
||||
|
||||
public static bool CheckIsInSilencePeriod(string startTime, string endTime)
|
||||
{
|
||||
if (startTime == endTime) return true;
|
||||
DateTime currentTime = DateTime.Now;
|
||||
|
||||
DateTime StartTime = DateTime.ParseExact(startTime, "HH:mm", null);
|
||||
DateTime EndTime = DateTime.ParseExact(endTime, "HH:mm", null);
|
||||
if (StartTime <= EndTime)
|
||||
{ // 单日时间段
|
||||
return currentTime >= StartTime && currentTime <= EndTime;
|
||||
}
|
||||
else
|
||||
{ // 跨越两天的时间段
|
||||
return currentTime >= StartTime || currentTime <= EndTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
public class ColorUtilities {
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个颜色的人眼感知亮度,并以 0~1 之间的小数表示。
|
||||
/// </summary>
|
||||
public static double GetGrayLevel(Color color) {
|
||||
return (0.299 * color.R + 0.587 * color.G + 0.114 * color.B) / 255;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据人眼感知亮度返回前景色到底是黑色还是白色
|
||||
/// </summary>
|
||||
/// <param name="grayLevel"><c>GetGrayLevel</c>返回的人眼感知亮度</param>
|
||||
/// <returns>Color</returns>
|
||||
public static Color GetReverseForegroundColor(double grayLevel) => grayLevel > 0.5 ? Colors.Black : Colors.White;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using Ink_Canvas.Resources.ICCConfiguration;
|
||||
using Tomlyn;
|
||||
using Tomlyn.Model;
|
||||
|
||||
namespace Ink_Canvas.Helpers {
|
||||
public static class ConfigurationHelper {
|
||||
public static ICCConfiguration ReadConfiguration() {
|
||||
try {
|
||||
if (File.Exists(App.RootPath + "icc.toml")) {
|
||||
try {
|
||||
string text = File.ReadAllText(App.RootPath + "icc.toml");
|
||||
var tomlTable = Toml.ToModel(text);
|
||||
var conf = new ICCConfiguration();
|
||||
|
||||
// FloatingBar
|
||||
var fb = tomlTable["FloatingBar"] as TomlTable;
|
||||
if (fb != null) {
|
||||
if (fb["SemiTransparent"] is bool) conf.FloatingBar.SemiTransparent = (bool)fb["SemiTransparent"];
|
||||
if (fb["NearSnap"] is bool) conf.FloatingBar.NearSnap = (bool)fb["NearSnap"];
|
||||
InitialPositionTypes _InitialPositionType;
|
||||
ElementCornerRadiusTypes _ElementCornerRadiusType;
|
||||
if (fb["InitialPosition"] is TomlArray) {
|
||||
var arr = ((TomlArray)fb["InitialPosition"]);
|
||||
conf.FloatingBar.InitialPosition = InitialPositionTypes.Custom;
|
||||
if ((arr[0] is double || arr[0] is long) &&
|
||||
(arr[1] is double || arr[1] is long))
|
||||
conf.FloatingBar.InitialPositionPoint = new Point(Math.Min(Math.Max(0,Convert.ToDouble(arr[0])),65535),
|
||||
Math.Min(Math.Max(0,Convert.ToDouble(arr[1])),65535));
|
||||
} else if (fb["InitialPosition"] is string &&
|
||||
Enum.TryParse<InitialPositionTypes>((string)fb["InitialPosition"],
|
||||
out _InitialPositionType)) {
|
||||
conf.FloatingBar.InitialPosition = _InitialPositionType;
|
||||
}
|
||||
if (fb["ElementCornerRadius"] is double || fb["ElementCornerRadius"] is long) {
|
||||
conf.FloatingBar.ElementCornerRadiusType = ElementCornerRadiusTypes.Custom;
|
||||
conf.FloatingBar.ElementCornerRadiusValue = Math.Min(Math.Max(0, Convert.ToDouble(fb["ElementCornerRadius"])),24);
|
||||
} else if (fb["ElementCornerRadius"] is string &&
|
||||
Enum.TryParse<ElementCornerRadiusTypes>((string)fb["ElementCornerRadius"],
|
||||
out _ElementCornerRadiusType)) {
|
||||
conf.FloatingBar.ElementCornerRadiusType = _ElementCornerRadiusType;
|
||||
}
|
||||
if (fb["ParallaxEffect"] is bool) conf.FloatingBar.ParallaxEffect = (bool)fb["ParallaxEffect"];
|
||||
if (fb["MiniMode"] is bool) conf.FloatingBar.MiniMode = (bool)fb["MiniMode"];
|
||||
if (fb["ClearButtonColor"] is TomlArray) {
|
||||
var arr = (TomlArray)fb["ClearButtonColor"];
|
||||
conf.FloatingBar.ClearButtonColor = Color.FromRgb(Convert.ToByte(arr[0]), Convert.ToByte(arr[1]), Convert.ToByte(arr[2]));
|
||||
}
|
||||
if (fb["ClearButtonPressColor"] is TomlArray) {
|
||||
var arr = (TomlArray)fb["ClearButtonPressColor"];
|
||||
conf.FloatingBar.ClearButtonPressColor = Color.FromRgb(Convert.ToByte(arr[0]), Convert.ToByte(arr[1]), Convert.ToByte(arr[2]));
|
||||
}
|
||||
if (fb["ToolButtonSelectedBgColor"] is TomlArray) {
|
||||
var arr = (TomlArray)fb["ToolButtonSelectedBgColor"];
|
||||
conf.FloatingBar.ToolButtonSelectedBgColor = Color.FromRgb(Convert.ToByte(arr[0]), Convert.ToByte(arr[1]), Convert.ToByte(arr[2]));
|
||||
}
|
||||
if (fb["MovingLimitationNoSnap"] is long || fb["MovingLimitationNoSnap"] is double)
|
||||
conf.FloatingBar.MovingLimitationNoSnap = Math.Min(Math.Max(0, Convert.ToDouble(fb["MovingLimitationNoSnap"])),32);
|
||||
if (fb["MovingLimitationSnapped"] is long || fb["MovingLimitationSnapped"] is double)
|
||||
conf.FloatingBar.MovingLimitationSnapped = Math.Min(Math.Max(0, Convert.ToDouble(fb["MovingLimitationSnapped"])),32);
|
||||
if (fb["NearSnapAreaSize"] is TomlTable) {
|
||||
var _tb = fb["NearSnapAreaSize"] as TomlTable;
|
||||
if (_tb["TopLeft"] is TomlArray) {
|
||||
var _arr = _tb["TopLeft"] as TomlArray;
|
||||
conf.FloatingBar.NearSnapAreaSize.TopLeft = new []{
|
||||
Math.Min(Math.Max(0,Convert.ToDouble(_arr[0])),64), Math.Min(Math.Max(0,Convert.ToDouble(_arr[1])),64)
|
||||
};
|
||||
}
|
||||
if (_tb["TopRight"] is TomlArray) {
|
||||
var _arr = _tb["TopRight"] as TomlArray;
|
||||
conf.FloatingBar.NearSnapAreaSize.TopRight = new []{
|
||||
Math.Min(Math.Max(0,Convert.ToDouble(_arr[0])),64), Math.Min(Math.Max(0,Convert.ToDouble(_arr[1])),64)
|
||||
};
|
||||
}
|
||||
if (_tb["BottomLeft"] is TomlArray) {
|
||||
var _arr = _tb["BottomLeft"] as TomlArray;
|
||||
conf.FloatingBar.NearSnapAreaSize.BottomLeft = new []{
|
||||
Math.Min(Math.Max(0,Convert.ToDouble(_arr[0])),64), Math.Min(Math.Max(0,Convert.ToDouble(_arr[1])),64)
|
||||
};
|
||||
}
|
||||
if (_tb["BottomRight"] is TomlArray) {
|
||||
var _arr = _tb["BottomRight"] as TomlArray;
|
||||
conf.FloatingBar.NearSnapAreaSize.BottomRight = new []{
|
||||
Math.Min(Math.Max(0,Convert.ToDouble(_arr[0])),64), Math.Min(Math.Max(0,Convert.ToDouble(_arr[1])),64)
|
||||
};
|
||||
}
|
||||
if (_tb["TopCenter"] is long || _tb["TopCenter"] is double)
|
||||
conf.FloatingBar.NearSnapAreaSize.TopCenter = Math.Min(Math.Max(0, Convert.ToDouble(_tb["TopCenter"])), 64);
|
||||
if (_tb["BottomCenter"] is long || _tb["BottomCenter"] is double)
|
||||
conf.FloatingBar.NearSnapAreaSize.BottomCenter = Math.Min(Math.Max(0, Convert.ToDouble(_tb["BottomCenter"])), 64);
|
||||
}
|
||||
if (fb["ToolBarItems"] is TomlTable) {
|
||||
var _tb = fb["ToolBarItems"] as TomlTable;
|
||||
if (_tb["CursorMode"] is TomlArray) {
|
||||
conf.FloatingBar.ToolBarItemsInCursorMode =
|
||||
Array.ConvertAll(((TomlArray)_tb["CursorMode"]).ToArray(),p=>p.ToString());
|
||||
}
|
||||
if (_tb["MiniMode"] is TomlArray) {
|
||||
conf.FloatingBar.ToolBarItemsInMiniMode =
|
||||
Array.ConvertAll(((TomlArray)_tb["MiniMode"]).ToArray(),p=>p.ToString());
|
||||
}
|
||||
if (_tb["AnnotationMode"] is TomlArray) {
|
||||
conf.FloatingBar.ToolBarItemsInAnnotationMode =
|
||||
Array.ConvertAll(((TomlArray)_tb["AnnotationMode"]).ToArray(),p=>p.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return conf;
|
||||
}
|
||||
catch {
|
||||
return new ICCConfiguration();
|
||||
}
|
||||
} else {
|
||||
return new ICCConfiguration();
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LogHelper.WriteLogToFile(ex.ToString(), LogHelper.LogType.Error);
|
||||
return new ICCConfiguration();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Ink_Canvas.Converter
|
||||
{
|
||||
public class IntNumberToString : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
if ((double)value == 0)
|
||||
{
|
||||
return "无限制";
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((double)value).ToString() + "人";
|
||||
}
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
if ((double)value == 0)
|
||||
{
|
||||
return "无限制";
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((double)value).ToString() + "人";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class IntNumberToString2 : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
if ((double)value == 0)
|
||||
{
|
||||
return "自动截图";
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((double)value).ToString() + "条";
|
||||
}
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
if ((double)value == 0)
|
||||
{
|
||||
return "自动截图";
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((double)value).ToString() + "条";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class IsEnabledToOpacityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
bool isChecked = (bool)value;
|
||||
if (isChecked == true)
|
||||
{
|
||||
return 1d;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.35;
|
||||
}
|
||||
}
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
|
||||
namespace Ink_Canvas.Helpers {
|
||||
internal class DelAutoSavedFiles {
|
||||
public static void DeleteFilesOlder(string directoryPath, int daysThreshold) {
|
||||
string[] extensionsToDel = { ".icstk", ".png" };
|
||||
if (Directory.Exists(directoryPath)) {
|
||||
// 获取目录中的所有子目录
|
||||
string[] subDirectories = Directory.GetDirectories(directoryPath, "*", SearchOption.AllDirectories);
|
||||
foreach (string subDirectory in subDirectories) {
|
||||
try {
|
||||
// 获取子目录下的所有文件
|
||||
string[] files = Directory.GetFiles(subDirectory);
|
||||
foreach (string filePath in files) {
|
||||
// 获取文件的创建日期
|
||||
DateTime creationDate = File.GetCreationTime(filePath);
|
||||
// 获取文件的扩展名
|
||||
string fileExtension = Path.GetExtension(filePath);
|
||||
// 如果文件的创建日期早于指定天数且是要删除的扩展名,则删除文件
|
||||
if (creationDate < DateTime.Now.AddDays(-daysThreshold)) {
|
||||
if (Array.Exists(extensionsToDel, ext => ext.Equals(fileExtension, StringComparison.OrdinalIgnoreCase))
|
||||
|| Path.GetFileName(filePath).Equals("Position", StringComparison.OrdinalIgnoreCase)) {
|
||||
File.Delete(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LogHelper.WriteLogToFile("DelAutoSavedFiles | 处理文件时出错: " + ex.ToString(), LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
try { // 递归删除空文件夹
|
||||
DeleteEmptyFolders(directoryPath);
|
||||
} catch (Exception ex) {
|
||||
LogHelper.WriteLogToFile("DelAutoSavedFiles | 处理文件时出错: " + ex.ToString(), LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeleteEmptyFolders(string directoryPath) {
|
||||
foreach (string dir in Directory.GetDirectories(directoryPath)) {
|
||||
DeleteEmptyFolders(dir);
|
||||
if (Directory.GetFiles(dir).Length == 0 && Directory.GetDirectories(dir).Length == 0) {
|
||||
Directory.Delete(dir, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
public class DelayAction
|
||||
{
|
||||
Timer _timerDebounce;
|
||||
|
||||
/// <summary>
|
||||
/// 防抖函式
|
||||
/// </summary>
|
||||
/// <param name="inv">同步的對象,一般傳入控件,不需要可null</param>
|
||||
public void DebounceAction(int timeMs, ISynchronizeInvoke inv, Action action)
|
||||
{
|
||||
lock (this) {
|
||||
if (_timerDebounce == null) {
|
||||
_timerDebounce = new Timer(timeMs) { AutoReset = false };
|
||||
_timerDebounce.Elapsed += (o, e) => {
|
||||
_timerDebounce.Stop(); _timerDebounce.Close(); _timerDebounce = null;
|
||||
InvokeAction(action, inv);
|
||||
};
|
||||
}
|
||||
_timerDebounce.Stop();
|
||||
_timerDebounce.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private static void InvokeAction(Action action, ISynchronizeInvoke inv)
|
||||
{
|
||||
if (inv == null)
|
||||
{
|
||||
action();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (inv.InvokeRequired)
|
||||
{
|
||||
inv.Invoke(action, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
action();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ink_Canvas.Helpers {
|
||||
public static class DirectoryUtils
|
||||
{
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
private static extern SafeFileHandle CreateFile(
|
||||
string fileName,
|
||||
uint dwDesiredAccess,
|
||||
FileShare dwShareMode,
|
||||
IntPtr securityAttrs_MustBeZero,
|
||||
FileMode dwCreationDisposition,
|
||||
uint dwFlagsAndAttributes,
|
||||
IntPtr hTemplateFile_MustBeZero);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, EntryPoint = "SetFileTime", ExactSpelling = true)]
|
||||
private static extern bool SetFileTime(
|
||||
SafeFileHandle hFile,
|
||||
IntPtr lpCreationTimeUnused,
|
||||
IntPtr lpLastAccessTimeUnused,
|
||||
ref long lpLastWriteTime);
|
||||
|
||||
private const uint FILE_ACCESS_GENERIC_READ = 0x80000000;
|
||||
private const uint FILE_ACCESS_GENERIC_WRITE = 0x40000000;
|
||||
|
||||
private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
|
||||
private const int OPEN_EXISTING = 3;
|
||||
|
||||
public static bool SetDirectoryLastWriteUtc(string dirPath, DateTime lastWriteDate) {
|
||||
using (var hDir = CreateFile(dirPath, FILE_ACCESS_GENERIC_READ | FILE_ACCESS_GENERIC_WRITE,
|
||||
FileShare.ReadWrite, IntPtr.Zero, (FileMode) OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero)) {
|
||||
var lastWriteTime = lastWriteDate.ToFileTime();
|
||||
if (!SetFileTime(hDir, IntPtr.Zero, IntPtr.Zero, ref lastWriteTime)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsWritable(string dirPath) {
|
||||
try {
|
||||
var lastWriteDate = Directory.GetLastWriteTimeUtc(dirPath);
|
||||
using (File.Create(Path.Combine(dirPath, Path.GetRandomFileName()), 1, FileOptions.DeleteOnClose)) { }
|
||||
|
||||
try {
|
||||
SetDirectoryLastWriteUtc(dirPath, lastWriteDate);
|
||||
} catch { }
|
||||
|
||||
return true;
|
||||
} catch (UnauthorizedAccessException) { } catch (Exception) { }
|
||||
return false;
|
||||
}
|
||||
|
||||
public static async Task<bool> IsWritableAsync(string dirPath) {
|
||||
var fn = new DirectoryInfo(dirPath).FullName;
|
||||
var result = await Task.Run(() => IsWritable(dirPath));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Windows;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using Pen = System.Windows.Media.Pen;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
|
||||
public class DrawingVisualCanvas : FrameworkElement
|
||||
{
|
||||
private VisualCollection _children;
|
||||
public DrawingVisual DrawingVisual = new DrawingVisual();
|
||||
|
||||
public DrawingVisualCanvas()
|
||||
{
|
||||
_children = new VisualCollection(this) {
|
||||
DrawingVisual // 初始化DrawingVisual
|
||||
};
|
||||
}
|
||||
|
||||
protected override int VisualChildrenCount => _children.Count;
|
||||
|
||||
protected override Visual GetVisualChild(int index)
|
||||
{
|
||||
if (index < 0 || index >= _children.Count) throw new ArgumentOutOfRangeException();
|
||||
return _children[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
public class DwmCompositionHelper{
|
||||
public const string LibraryName = "Dwmapi.dll";
|
||||
|
||||
[DllImport(LibraryName, ExactSpelling = true, PreserveSig = false)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool DwmIsCompositionEnabled();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.VisualBasic;
|
||||
using System.Collections;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
|
||||
public static class EdgeGestureUtil
|
||||
{
|
||||
|
||||
private static Guid DISABLE_TOUCH_SCREEN = new Guid("32CE38B2-2C9A-41B1-9BC5-B3784394AA44");
|
||||
private static Guid IID_PROPERTY_STORE = new Guid("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99");
|
||||
|
||||
private static short VT_BOOL = 11;
|
||||
#region "Structures"
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
public struct PropertyKey
|
||||
{
|
||||
public PropertyKey(Guid guid, UInt32 pid)
|
||||
{
|
||||
fmtid = guid;
|
||||
this.pid = pid;
|
||||
}
|
||||
|
||||
[MarshalAs(UnmanagedType.Struct)]
|
||||
public Guid fmtid;
|
||||
public uint pid;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct PropVariant
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public short vt;
|
||||
[FieldOffset(2)]
|
||||
private short wReserved1;
|
||||
[FieldOffset(4)]
|
||||
private short wReserved2;
|
||||
[FieldOffset(6)]
|
||||
private short wReserved3;
|
||||
[FieldOffset(8)]
|
||||
private sbyte cVal;
|
||||
[FieldOffset(8)]
|
||||
private byte bVal;
|
||||
[FieldOffset(8)]
|
||||
private short iVal;
|
||||
[FieldOffset(8)]
|
||||
public ushort uiVal;
|
||||
[FieldOffset(8)]
|
||||
private int lVal;
|
||||
[FieldOffset(8)]
|
||||
private uint ulVal;
|
||||
[FieldOffset(8)]
|
||||
private int intVal;
|
||||
[FieldOffset(8)]
|
||||
private uint uintVal;
|
||||
[FieldOffset(8)]
|
||||
private long hVal;
|
||||
[FieldOffset(8)]
|
||||
private long uhVal;
|
||||
[FieldOffset(8)]
|
||||
private float fltVal;
|
||||
[FieldOffset(8)]
|
||||
private double dblVal;
|
||||
[FieldOffset(8)]
|
||||
public bool boolVal;
|
||||
[FieldOffset(8)]
|
||||
private int scode;
|
||||
[FieldOffset(8)]
|
||||
private DateTime date;
|
||||
[FieldOffset(8)]
|
||||
private System.Runtime.InteropServices.ComTypes.FILETIME filetime;
|
||||
|
||||
[FieldOffset(8)]
|
||||
private Blob blobVal;
|
||||
[FieldOffset(8)]
|
||||
private IntPtr pwszVal;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to gets blob data
|
||||
/// </summary>
|
||||
private byte[] GetBlob()
|
||||
{
|
||||
byte[] Result = new byte[blobVal.Length];
|
||||
Marshal.Copy(blobVal.Data, Result, 0, Result.Length);
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property value
|
||||
/// </summary>
|
||||
public object Value
|
||||
{
|
||||
get
|
||||
{
|
||||
VarEnum ve = (VarEnum)vt;
|
||||
switch (ve)
|
||||
{
|
||||
case VarEnum.VT_I1:
|
||||
return bVal;
|
||||
case VarEnum.VT_I2:
|
||||
return iVal;
|
||||
case VarEnum.VT_I4:
|
||||
return lVal;
|
||||
case VarEnum.VT_I8:
|
||||
return hVal;
|
||||
case VarEnum.VT_INT:
|
||||
return iVal;
|
||||
case VarEnum.VT_UI4:
|
||||
return ulVal;
|
||||
case VarEnum.VT_LPWSTR:
|
||||
return Marshal.PtrToStringUni(pwszVal);
|
||||
case VarEnum.VT_BLOB:
|
||||
return GetBlob();
|
||||
}
|
||||
throw new NotImplementedException("PropVariant " + ve.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal struct Blob
|
||||
{
|
||||
public int Length;
|
||||
|
||||
public IntPtr Data;
|
||||
//Code Should Compile at warning level4 without any warnings,
|
||||
//However this struct will give us Warning CS0649: Field [Fieldname]
|
||||
//is never assigned to, and will always have its default value
|
||||
//You can disable CS0649 in the project options but that will disable
|
||||
//the warning for the whole project, it's a nice warning and we do want
|
||||
//it in other places so we make a nice dummy function to keep the compiler
|
||||
//happy.
|
||||
// 代码应该在警告级别 4 下编译而不会出现任何警告,但是此结构将给出警告 CS0649:字段 [Fieldname] 从未分配,并且始终具有其默认值。您可以在项目选项中禁用 CS0649,但这将禁用整个项目的警告,这是一个很好的警告,我们确实希望它在其他地方,所以我们制作了一个不错的虚拟函数来让编译器满意。
|
||||
private void FixCS0649()
|
||||
{
|
||||
Length = 0;
|
||||
Data = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region "Interfaces"
|
||||
|
||||
[ComImport(), Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IPropertyStore
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
void GetCount([Out(), In()] ref uint cProps);
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
void GetAt([In()] uint iProp, ref PropertyKey pkey);
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
void GetValue([In()] ref PropertyKey key, ref PropVariant pv);
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
void SetValue([In()] ref PropertyKey key, [In()] ref PropVariant pv);
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
void Commit();
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
void Release();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region "Methods"
|
||||
|
||||
[DllImport("shell32.dll", SetLastError = true)]
|
||||
private static extern int SHGetPropertyStoreForWindow(IntPtr handle, ref Guid riid, ref IPropertyStore propertyStore);
|
||||
|
||||
public static void DisableEdgeGestures(IntPtr hwnd, bool enable)
|
||||
{
|
||||
IPropertyStore pPropStore = null;
|
||||
int hr = 0;
|
||||
hr = SHGetPropertyStoreForWindow(hwnd, ref IID_PROPERTY_STORE, ref pPropStore);
|
||||
if (hr == 0)
|
||||
{
|
||||
PropertyKey propKey = new PropertyKey();
|
||||
propKey.fmtid = DISABLE_TOUCH_SCREEN;
|
||||
propKey.pid = 2;
|
||||
PropVariant var = new PropVariant();
|
||||
var.vt = VT_BOOL;
|
||||
var.boolVal = enable;
|
||||
pPropStore.SetValue(ref propKey, ref var);
|
||||
Marshal.FinalReleaseComObject(pPropStore);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
internal class ForegroundWindowInfo
|
||||
{
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr GetForegroundWindow();
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RECT {
|
||||
public int Left;
|
||||
public int Top;
|
||||
public int Right;
|
||||
public int Bottom;
|
||||
|
||||
public int Width => Right - Left;
|
||||
public int Height => Bottom - Top;
|
||||
}
|
||||
|
||||
public static string WindowTitle() {
|
||||
IntPtr foregroundWindowHandle = GetForegroundWindow();
|
||||
|
||||
const int nChars = 256;
|
||||
StringBuilder windowTitle = new StringBuilder(nChars);
|
||||
GetWindowText(foregroundWindowHandle, windowTitle, nChars);
|
||||
|
||||
return windowTitle.ToString();
|
||||
}
|
||||
|
||||
public static string WindowClassName() {
|
||||
IntPtr foregroundWindowHandle = GetForegroundWindow();
|
||||
|
||||
const int nChars = 256;
|
||||
StringBuilder className = new StringBuilder(nChars);
|
||||
GetClassName(foregroundWindowHandle, className, nChars);
|
||||
|
||||
return className.ToString();
|
||||
}
|
||||
|
||||
public static RECT WindowRect() {
|
||||
IntPtr foregroundWindowHandle = GetForegroundWindow();
|
||||
|
||||
RECT windowRect;
|
||||
GetWindowRect(foregroundWindowHandle, out windowRect);
|
||||
|
||||
return windowRect;
|
||||
}
|
||||
|
||||
public static string ProcessName() {
|
||||
IntPtr foregroundWindowHandle = GetForegroundWindow();
|
||||
uint processId;
|
||||
GetWindowThreadProcessId(foregroundWindowHandle, out processId);
|
||||
|
||||
try {
|
||||
Process process = Process.GetProcessById((int)processId);
|
||||
return process.ProcessName;
|
||||
} catch (ArgumentException) {
|
||||
// Process with the given ID not found
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
public static string ProcessPath()
|
||||
{
|
||||
IntPtr foregroundWindowHandle = GetForegroundWindow();
|
||||
uint processId;
|
||||
GetWindowThreadProcessId(foregroundWindowHandle, out processId);
|
||||
|
||||
try
|
||||
{
|
||||
Process process = Process.GetProcessById((int)processId);
|
||||
return process.MainModule.FileName;
|
||||
}
|
||||
catch {
|
||||
// Process with the given ID not found
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,376 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
// 由衷感謝 lindexi 提供的 《WPF 稳定的全屏化窗口方法》
|
||||
// 文章鏈接:https://blog.lindexi.com/post/WPF-%E7%A8%B3%E5%AE%9A%E7%9A%84%E5%85%A8%E5%B1%8F%E5%8C%96%E7%AA%97%E5%8F%A3%E6%96%B9%E6%B3%95.html
|
||||
// lindexi 的部落格:https://blog.lindexi.com/
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
public static partial class FullScreenHelper
|
||||
{
|
||||
static class Win32
|
||||
{
|
||||
[Flags]
|
||||
public enum ShowWindowCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// Maximizes the specified window.
|
||||
/// </summary>
|
||||
SW_MAXIMIZE = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Activates and displays the window. If the window is minimized or maximized, the system restores it to its original
|
||||
/// size and position. An application should specify this flag when restoring a minimized window.
|
||||
/// </summary>
|
||||
SW_RESTORE = 9,
|
||||
}
|
||||
|
||||
|
||||
internal static class Properties
|
||||
{
|
||||
#if !ANSI
|
||||
public const CharSet BuildCharSet = CharSet.Unicode;
|
||||
#else
|
||||
public const CharSet BuildCharSet = CharSet.Ansi;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static class Dwmapi
|
||||
{
|
||||
public const string LibraryName = "Dwmapi.dll";
|
||||
|
||||
[DllImport(LibraryName, ExactSpelling = true, PreserveSig = false)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool DwmIsCompositionEnabled();
|
||||
|
||||
[DllImport("Dwmapi.dll", ExactSpelling = true, SetLastError = true)]
|
||||
public static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE dwAttribute,
|
||||
in int pvAttribute, uint cbAttribute);
|
||||
}
|
||||
|
||||
public static class User32
|
||||
{
|
||||
public const string LibraryName = "user32";
|
||||
|
||||
[DllImport(LibraryName, CharSet = Properties.BuildCharSet)]
|
||||
public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MonitorInfo lpmi);
|
||||
|
||||
[DllImport(LibraryName, ExactSpelling = true)]
|
||||
public static extern IntPtr MonitorFromRect(in Rectangle lprc, MonitorFlag dwFlags);
|
||||
|
||||
[DllImport(LibraryName, ExactSpelling = true)]
|
||||
public static extern bool IsIconic(IntPtr hwnd);
|
||||
|
||||
[DllImport(LibraryName, ExactSpelling = true)]
|
||||
public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);
|
||||
|
||||
[DllImport(LibraryName, ExactSpelling = true)]
|
||||
public static extern bool SetWindowPlacement(IntPtr hWnd,
|
||||
[In] ref WINDOWPLACEMENT lpwndpl);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
[DllImport(LibraryName, ExactSpelling = true)]
|
||||
public static extern bool GetWindowRect(IntPtr hWnd, out Rectangle lpRect);
|
||||
|
||||
[DllImport(LibraryName, ExactSpelling = true, SetLastError = true)]
|
||||
public static extern Int32 SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, Int32 x, Int32 y, Int32 cx,
|
||||
Int32 cy, Int32 wFlagslong);
|
||||
|
||||
[DllImport(LibraryName, ExactSpelling = true)]
|
||||
public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
|
||||
|
||||
public static IntPtr GetWindowLongPtr(IntPtr hWnd, GetWindowLongFields nIndex) =>
|
||||
GetWindowLongPtr(hWnd, (int) nIndex);
|
||||
|
||||
public static IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex)
|
||||
{
|
||||
return IntPtr.Size > 4
|
||||
#pragma warning disable CS0618 // 类型或成员已过时
|
||||
? GetWindowLongPtr_x64(hWnd, nIndex)
|
||||
: new IntPtr(GetWindowLong(hWnd, nIndex));
|
||||
#pragma warning restore CS0618 // 类型或成员已过时
|
||||
}
|
||||
|
||||
[DllImport(LibraryName, CharSet = Properties.BuildCharSet)]
|
||||
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
|
||||
|
||||
[DllImport(LibraryName, CharSet = Properties.BuildCharSet, EntryPoint = "GetWindowLongPtr")]
|
||||
public static extern IntPtr GetWindowLongPtr_x64(IntPtr hWnd, int nIndex);
|
||||
|
||||
public static IntPtr SetWindowLongPtr(IntPtr hWnd, GetWindowLongFields nIndex, IntPtr dwNewLong) =>
|
||||
SetWindowLongPtr(hWnd, (int) nIndex, dwNewLong);
|
||||
|
||||
public static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
|
||||
{
|
||||
return IntPtr.Size > 4
|
||||
#pragma warning disable CS0618 // 类型或成员已过时
|
||||
? SetWindowLongPtr_x64(hWnd, nIndex, dwNewLong)
|
||||
: new IntPtr(SetWindowLong(hWnd, nIndex, dwNewLong.ToInt32()));
|
||||
#pragma warning restore CS0618 // 类型或成员已过时
|
||||
}
|
||||
|
||||
[DllImport(LibraryName, CharSet = Properties.BuildCharSet, EntryPoint = "SetWindowLongPtr")]
|
||||
public static extern IntPtr SetWindowLongPtr_x64(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
|
||||
|
||||
[DllImport(LibraryName, CharSet = Properties.BuildCharSet)]
|
||||
public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct MonitorInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The size of the structure, in bytes.
|
||||
/// </summary>
|
||||
public uint Size;
|
||||
|
||||
/// <summary>
|
||||
/// A RECT structure that specifies the display monitor rectangle, expressed in virtual-screen coordinates. Note that
|
||||
/// if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.
|
||||
/// </summary>
|
||||
public Rectangle MonitorRect;
|
||||
|
||||
/// <summary>
|
||||
/// A RECT structure that specifies the work area rectangle of the display monitor, expressed in virtual-screen
|
||||
/// coordinates. Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may
|
||||
/// be negative values.
|
||||
/// </summary>
|
||||
public Rectangle WorkRect;
|
||||
|
||||
/// <summary>
|
||||
/// A set of flags that represent attributes of the display monitor.
|
||||
/// </summary>
|
||||
public MonitorInfoFlag Flags;
|
||||
}
|
||||
|
||||
enum MonitorInfoFlag
|
||||
{
|
||||
}
|
||||
|
||||
enum MonitorFlag
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a handle to the primary display monitor.
|
||||
/// </summary>
|
||||
MONITOR_DEFAULTTOPRIMARY = 1,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct WindowPosition
|
||||
{
|
||||
public IntPtr Hwnd;
|
||||
public IntPtr HwndZOrderInsertAfter;
|
||||
public int X;
|
||||
public int Y;
|
||||
public int Width;
|
||||
public int Height;
|
||||
public WindowPositionFlags Flags;
|
||||
}
|
||||
|
||||
enum HwndZOrder
|
||||
{
|
||||
/// <summary>
|
||||
/// Places the window at the top of the Z order.
|
||||
/// </summary>
|
||||
HWND_TOP = 0,
|
||||
HWND_TOPMOST = -1,
|
||||
}
|
||||
|
||||
enum DWMWINDOWATTRIBUTE : uint
|
||||
{
|
||||
DWMWA_TRANSITIONS_FORCEDISABLED = 3,
|
||||
}
|
||||
|
||||
enum GetWindowLongFields
|
||||
{
|
||||
/// <summary>
|
||||
/// 设定一个新的窗口风格
|
||||
/// Retrieves the window styles
|
||||
/// </summary>
|
||||
GWL_STYLE = -16,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct WINDOWPLACEMENT // WindowPlacement
|
||||
{
|
||||
public uint Size;
|
||||
public WindowPlacementFlags Flags;
|
||||
public Win32.ShowWindowCommands ShowCmd;
|
||||
public Point MinPosition;
|
||||
public Point MaxPosition;
|
||||
public Rectangle NormalPosition;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum WindowPositionFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// 清除客户区的所有内容。如果未设置该标志,客户区的有效内容被保存并且在窗口尺寸更新和重定位后拷贝回客户区
|
||||
/// </para>
|
||||
/// Discards the entire contents of the client area. If this flag is not specified, the valid contents of the client
|
||||
/// area are saved and copied back into the client area after the window is sized or repositioned.
|
||||
/// </summary>
|
||||
SWP_NOCOPYBITS = 0x0100,
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// 维持当前位置(忽略X和Y参数)
|
||||
/// </para>
|
||||
/// Retains the current position (ignores X and Y parameters).
|
||||
/// </summary>
|
||||
SWP_NOMOVE = 0x0002,
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// 不重画改变的内容。如果设置了这个标志,则不发生任何重画动作。适用于客户区和非客户区(包括标题栏和滚动条)和任何由于窗回移动而露出的父窗口的所有部分。如果设置了这个标志,应用程序必须明确地使窗口无效并区重画窗口的任何部分和父窗口需要重画的部分
|
||||
/// </para>
|
||||
/// Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to the client area,
|
||||
/// the nonclient area (including the title bar and scroll bars), and any part of the parent window uncovered as a
|
||||
/// result of the window being moved. When this flag is set, the application must explicitly invalidate or redraw any
|
||||
/// parts of the window and parent window that need redrawing.
|
||||
/// </summary>
|
||||
SWP_NOREDRAW = 0x0008,
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// 维持当前尺寸(忽略 cx 和 cy 参数)
|
||||
/// </para>
|
||||
/// Retains the current size (ignores the cx and cy parameters).
|
||||
/// </summary>
|
||||
SWP_NOSIZE = 0x0001,
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// 维持当前 Z 序(忽略 hWndlnsertAfter 参数)
|
||||
/// </para>
|
||||
/// Retains the current Z order (ignores the hWndInsertAfter parameter).
|
||||
/// </summary>
|
||||
SWP_NOZORDER = 0x0004,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct Rectangle
|
||||
{
|
||||
public int Left;
|
||||
public int Top;
|
||||
public int Right;
|
||||
public int Bottom;
|
||||
|
||||
/// <summary>
|
||||
/// 矩形的宽度
|
||||
/// </summary>
|
||||
public int Width
|
||||
{
|
||||
get { return unchecked((int) (Right - Left)); }
|
||||
set { Right = unchecked((int) (Left + value)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 矩形的高度
|
||||
/// </summary>
|
||||
public int Height
|
||||
{
|
||||
get { return unchecked((int) (Bottom - Top)); }
|
||||
set { Bottom = unchecked((int) (Top + value)); }
|
||||
}
|
||||
|
||||
public bool Equals(Rectangle other)
|
||||
{
|
||||
return (Left == other.Left) && (Right == other.Right) && (Top == other.Top) && (Bottom == other.Bottom);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Rectangle rectangle && Equals(rectangle);
|
||||
}
|
||||
|
||||
public static bool operator ==(Rectangle left, Rectangle right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hashCode = (int) Left;
|
||||
hashCode = (hashCode * 397) ^ (int) Top;
|
||||
hashCode = (hashCode * 397) ^ (int) Right;
|
||||
hashCode = (hashCode * 397) ^ (int) Bottom;
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator !=(Rectangle left, Rectangle right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct Point
|
||||
{
|
||||
public int X;
|
||||
public int Y;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum WindowPlacementFlags
|
||||
{
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum WindowStyles
|
||||
{
|
||||
/// <summary>
|
||||
/// The window is initially maximized.
|
||||
/// </summary>
|
||||
WS_MAXIMIZE = 0x01000000,
|
||||
|
||||
/// <summary>
|
||||
/// The window has a maximize button. Cannot be combined with the WS_EX_CONTEXTHELP style. The WS_SYSMENU style must
|
||||
/// also be specified.
|
||||
/// </summary>
|
||||
WS_MAXIMIZEBOX = 0x00010000,
|
||||
|
||||
/// <summary>
|
||||
/// The window is initially minimized. Same as the WS_ICONIC style.
|
||||
/// </summary>
|
||||
WS_MINIMIZE = 0x20000000,
|
||||
|
||||
/// <summary>
|
||||
/// The window has a sizing border. Same as the WS_SIZEBOX style.
|
||||
/// </summary>
|
||||
WS_THICKFRAME = 0x00040000,
|
||||
}
|
||||
|
||||
[ComImport]
|
||||
[Guid("602D4995-B13A-429b-A66E-1935E44F4317")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
private interface ITaskbarList2
|
||||
{
|
||||
[PreserveSig]
|
||||
int HrInit();
|
||||
|
||||
[PreserveSig]
|
||||
int AddTab(IntPtr hwnd);
|
||||
|
||||
[PreserveSig]
|
||||
int DeleteTab(IntPtr hwnd);
|
||||
|
||||
[PreserveSig]
|
||||
int ActivateTab(IntPtr hwnd);
|
||||
|
||||
[PreserveSig]
|
||||
int SetActiveAlt(IntPtr hwnd);
|
||||
|
||||
[PreserveSig]
|
||||
int MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
using System;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
|
||||
// 由衷感謝 lindexi 提供的 《WPF 稳定的全屏化窗口方法》
|
||||
// 文章鏈接:https://blog.lindexi.com/post/WPF-%E7%A8%B3%E5%AE%9A%E7%9A%84%E5%85%A8%E5%B1%8F%E5%8C%96%E7%AA%97%E5%8F%A3%E6%96%B9%E6%B3%95.html
|
||||
// lindexi 的部落格:https://blog.lindexi.com/
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// 用来使窗口变得全屏的辅助类
|
||||
/// 采用设置窗口位置和尺寸,确保盖住整个屏幕的方式来实现全屏
|
||||
/// 目前已知需要满足的条件是:窗口盖住整个屏幕、窗口没有WS_THICKFRAME样式、窗口不能有标题栏且最大化
|
||||
/// </summary>
|
||||
public static partial class FullScreenHelper
|
||||
{
|
||||
public static void MarkFullscreenWindowTaskbarList(IntPtr hwnd, bool isFullscreen)
|
||||
{
|
||||
try
|
||||
{
|
||||
var CLSID_TaskbarList = new Guid("56FDF344-FD6D-11D0-958A-006097C9A090");
|
||||
var obj = Activator.CreateInstance(Type.GetTypeFromCLSID(CLSID_TaskbarList));
|
||||
(obj as ITaskbarList2)?.MarkFullscreenWindow(hwnd, isFullscreen);
|
||||
}
|
||||
catch
|
||||
{
|
||||
//应该不会挂
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用于记录窗口全屏前位置的附加属性
|
||||
/// </summary>
|
||||
private static readonly DependencyProperty BeforeFullScreenWindowPlacementProperty =
|
||||
DependencyProperty.RegisterAttached("BeforeFullScreenWindowPlacement", typeof(WINDOWPLACEMENT?),
|
||||
typeof(Window));
|
||||
|
||||
/// <summary>
|
||||
/// 用于记录窗口全屏前样式的附加属性
|
||||
/// </summary>
|
||||
private static readonly DependencyProperty BeforeFullScreenWindowStyleProperty =
|
||||
DependencyProperty.RegisterAttached("BeforeFullScreenWindowStyle", typeof(WindowStyles?), typeof(Window));
|
||||
|
||||
/// <summary>
|
||||
/// 开始进入全屏模式
|
||||
/// 进入全屏模式后,窗口可通过 API 方式(也可以用 Win + Shift + Left/Right)移动,调整大小,但会根据目标矩形寻找显示器重新调整到全屏状态。
|
||||
/// 进入全屏后,不要修改样式等窗口属性,在退出时,会恢复到进入前的状态
|
||||
/// 进入全屏模式后会禁用 DWM 过渡动画
|
||||
/// </summary>
|
||||
/// <param name="window"></param>
|
||||
public static void StartFullScreen(Window window)
|
||||
{
|
||||
if (window == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(window), $"{nameof(window)} 不能为 null");
|
||||
}
|
||||
|
||||
//确保不在全屏模式
|
||||
if (window.GetValue(BeforeFullScreenWindowPlacementProperty) == null &&
|
||||
window.GetValue(BeforeFullScreenWindowStyleProperty) == null)
|
||||
{
|
||||
var hwnd = new WindowInteropHelper(window).EnsureHandle();
|
||||
var hwndSource = HwndSource.FromHwnd(hwnd);
|
||||
|
||||
//获取当前窗口的位置大小状态并保存
|
||||
var placement = new WINDOWPLACEMENT();
|
||||
placement.Size = (uint) Marshal.SizeOf(placement);
|
||||
Win32.User32.GetWindowPlacement(hwnd, ref placement);
|
||||
window.SetValue(BeforeFullScreenWindowPlacementProperty, placement);
|
||||
|
||||
//修改窗口样式
|
||||
var style = (WindowStyles) Win32.User32.GetWindowLongPtr(hwnd, GetWindowLongFields.GWL_STYLE);
|
||||
window.SetValue(BeforeFullScreenWindowStyleProperty, style);
|
||||
//将窗口恢复到还原模式,在有标题栏的情况下最大化模式下无法全屏,
|
||||
//这里采用还原,不修改标题栏的方式
|
||||
//在退出全屏时,窗口原有的状态会恢复
|
||||
//去掉WS_THICKFRAME,在有该样式的情况下不能全屏
|
||||
//去掉WS_MAXIMIZEBOX,禁用最大化,如果最大化会退出全屏
|
||||
//去掉WS_MAXIMIZE,使窗口变成还原状态,不使用ShowWindow(hwnd, ShowWindowCommands.SW_RESTORE),避免看到窗口变成还原状态这一过程(也避免影响窗口的Visible状态)
|
||||
style &= (~(WindowStyles.WS_THICKFRAME | WindowStyles.WS_MAXIMIZEBOX | WindowStyles.WS_MAXIMIZE));
|
||||
Win32.User32.SetWindowLongPtr(hwnd, GetWindowLongFields.GWL_STYLE, (IntPtr) style);
|
||||
|
||||
//禁用 DWM 过渡动画 忽略返回值,若DWM关闭不做处理
|
||||
Win32.Dwmapi.DwmSetWindowAttribute(hwnd, DWMWINDOWATTRIBUTE.DWMWA_TRANSITIONS_FORCEDISABLED, 1,
|
||||
sizeof(int));
|
||||
|
||||
//添加Hook,在窗口尺寸位置等要发生变化时,确保全屏
|
||||
hwndSource.AddHook(KeepFullScreenHook);
|
||||
|
||||
if (Win32.User32.GetWindowRect(hwnd, out var rect))
|
||||
{
|
||||
//不能用 placement 的坐标,placement是工作区坐标,不是屏幕坐标。
|
||||
|
||||
//使用窗口当前的矩形调用下设置窗口位置和尺寸的方法,让Hook来进行调整窗口位置和尺寸到全屏模式
|
||||
Win32.User32.SetWindowPos(hwnd, (IntPtr) HwndZOrder.HWND_TOPMOST, rect.Left, rect.Top, rect.Width,
|
||||
rect.Height, (int) WindowPositionFlags.SWP_NOZORDER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 退出全屏模式
|
||||
/// 窗口会回到进入全屏模式时保存的状态
|
||||
/// 退出全屏模式后会重新启用 DWM 过渡动画
|
||||
/// </summary>
|
||||
/// <param name="window"></param>
|
||||
public static void EndFullScreen(Window window)
|
||||
{
|
||||
if (window == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(window), $"{nameof(window)} 不能为 null");
|
||||
}
|
||||
|
||||
//确保在全屏模式并获取之前保存的状态
|
||||
if (window.GetValue(BeforeFullScreenWindowPlacementProperty) is WINDOWPLACEMENT placement
|
||||
&& window.GetValue(BeforeFullScreenWindowStyleProperty) is WindowStyles style)
|
||||
{
|
||||
var hwnd = new WindowInteropHelper(window).Handle;
|
||||
|
||||
if (hwnd == IntPtr.Zero)
|
||||
{
|
||||
// 句柄为 0 只有两种情况:
|
||||
// 1. 虽然窗口已进入全屏,但窗口已被关闭;
|
||||
// 2. 窗口初始化前,在还没有调用 StartFullScreen 的前提下就调用了此方法。
|
||||
// 所以,直接 return 就好。
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var hwndSource = HwndSource.FromHwnd(hwnd);
|
||||
|
||||
//去除hook
|
||||
hwndSource.RemoveHook(KeepFullScreenHook);
|
||||
|
||||
//恢复保存的状态
|
||||
//不要改变Style里的WS_MAXIMIZE,否则会使窗口变成最大化状态,但是尺寸不对
|
||||
//也不要设置回Style里的WS_MINIMIZE,否则会导致窗口最小化按钮显示成还原按钮
|
||||
Win32.User32.SetWindowLongPtr(hwnd, GetWindowLongFields.GWL_STYLE,
|
||||
(IntPtr) (style & (~(WindowStyles.WS_MAXIMIZE | WindowStyles.WS_MINIMIZE))));
|
||||
|
||||
if ((style & WindowStyles.WS_MINIMIZE) != 0)
|
||||
{
|
||||
//如果窗口进入全屏前是最小化的,这里不让窗口恢复到之前的最小化状态,而是到还原的状态。
|
||||
//大多数情况下,都不期望在退出全屏的时候,恢复到最小化。
|
||||
placement.ShowCmd = Win32.ShowWindowCommands.SW_RESTORE;
|
||||
}
|
||||
|
||||
if ((style & WindowStyles.WS_MAXIMIZE) != 0)
|
||||
{
|
||||
//提前调用 ShowWindow 使窗口恢复最大化,若通过 SetWindowPlacement 最大化会导致闪烁,只靠其恢复 RestoreBounds.
|
||||
Win32.User32.ShowWindow(hwnd, Win32.ShowWindowCommands.SW_MAXIMIZE);
|
||||
}
|
||||
|
||||
Win32.User32.SetWindowPlacement(hwnd, ref placement);
|
||||
|
||||
if ((style & WindowStyles.WS_MAXIMIZE) ==
|
||||
0) //如果窗口是最大化就不要修改WPF属性,否则会破坏RestoreBounds,且WPF窗口自身在最大化时,不会修改 Left Top Width Height 属性
|
||||
{
|
||||
if (Win32.User32.GetWindowRect(hwnd, out var rect))
|
||||
{
|
||||
//不能用 placement 的坐标,placement是工作区坐标,不是屏幕坐标。
|
||||
|
||||
//确保窗口的 WPF 属性与 Win32 位置一致
|
||||
var logicalPos =
|
||||
hwndSource.CompositionTarget.TransformFromDevice.Transform(
|
||||
new System.Windows.Point(rect.Left, rect.Top));
|
||||
var logicalSize =
|
||||
hwndSource.CompositionTarget.TransformFromDevice.Transform(
|
||||
new System.Windows.Point(rect.Width, rect.Height));
|
||||
window.Left = logicalPos.X;
|
||||
window.Top = logicalPos.Y;
|
||||
window.Width = logicalSize.X;
|
||||
window.Height = logicalSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
//重新启用 DWM 过渡动画 忽略返回值,若DWM关闭不做处理
|
||||
Win32.Dwmapi.DwmSetWindowAttribute(hwnd, DWMWINDOWATTRIBUTE.DWMWA_TRANSITIONS_FORCEDISABLED, 0,
|
||||
sizeof(int));
|
||||
|
||||
//删除保存的状态
|
||||
window.ClearValue(BeforeFullScreenWindowPlacementProperty);
|
||||
window.ClearValue(BeforeFullScreenWindowStyleProperty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确保窗口全屏的Hook
|
||||
/// 使用HandleProcessCorruptedStateExceptions,防止访问内存过程中因为一些致命异常导致程序崩溃
|
||||
/// </summary>
|
||||
[HandleProcessCorruptedStateExceptions]
|
||||
private static IntPtr KeepFullScreenHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
||||
{
|
||||
//处理WM_WINDOWPOSCHANGING消息
|
||||
const int WINDOWPOSCHANGING = 0x0046;
|
||||
if (msg == WINDOWPOSCHANGING)
|
||||
{
|
||||
try
|
||||
{
|
||||
//得到WINDOWPOS结构体
|
||||
var pos = (WindowPosition) Marshal.PtrToStructure(lParam, typeof(WindowPosition));
|
||||
|
||||
if ((pos.Flags & WindowPositionFlags.SWP_NOMOVE) != 0 &&
|
||||
(pos.Flags & WindowPositionFlags.SWP_NOSIZE) != 0)
|
||||
{
|
||||
//既然你既不改变位置,也不改变尺寸,我就不管了...
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
if (Win32.User32.IsIconic(hwnd))
|
||||
{
|
||||
// 如果在全屏期间最小化了窗口,那么忽略后续的位置调整。
|
||||
// 否则按后续逻辑,会根据窗口在 -32000 的位置,计算出错误的目标位置,然后就跳到主屏了。
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
//获取窗口现在的矩形,下面用来参考计算目标矩形
|
||||
if (Win32.User32.GetWindowRect(hwnd, out var rect))
|
||||
{
|
||||
var targetRect = rect; //窗口想要变化的目标矩形
|
||||
|
||||
if ((pos.Flags & WindowPositionFlags.SWP_NOMOVE) == 0)
|
||||
{
|
||||
//需要移动
|
||||
targetRect.Left = pos.X;
|
||||
targetRect.Top = pos.Y;
|
||||
}
|
||||
|
||||
if ((pos.Flags & WindowPositionFlags.SWP_NOSIZE) == 0)
|
||||
{
|
||||
//要改变尺寸
|
||||
targetRect.Right = targetRect.Left + pos.Width;
|
||||
targetRect.Bottom = targetRect.Top + pos.Height;
|
||||
}
|
||||
else
|
||||
{
|
||||
//不改变尺寸
|
||||
targetRect.Right = targetRect.Left + rect.Width;
|
||||
targetRect.Bottom = targetRect.Top + rect.Height;
|
||||
}
|
||||
|
||||
//使用目标矩形获取显示器信息
|
||||
var monitor = Win32.User32.MonitorFromRect(targetRect, MonitorFlag.MONITOR_DEFAULTTOPRIMARY);
|
||||
var info = new MonitorInfo();
|
||||
info.Size = (uint) Marshal.SizeOf(info);
|
||||
if (Win32.User32.GetMonitorInfo(monitor, ref info))
|
||||
{
|
||||
//基于显示器信息设置窗口尺寸位置
|
||||
pos.X = info.MonitorRect.Left;
|
||||
pos.Y = info.MonitorRect.Top;
|
||||
pos.Width = info.MonitorRect.Right - info.MonitorRect.Left;
|
||||
pos.Height = info.MonitorRect.Bottom - info.MonitorRect.Top;
|
||||
pos.Flags &= ~(WindowPositionFlags.SWP_NOSIZE | WindowPositionFlags.SWP_NOMOVE |
|
||||
WindowPositionFlags.SWP_NOREDRAW);
|
||||
pos.Flags |= WindowPositionFlags.SWP_NOCOPYBITS;
|
||||
|
||||
if (rect == info.MonitorRect)
|
||||
{
|
||||
var hwndSource = HwndSource.FromHwnd(hwnd);
|
||||
if (hwndSource?.RootVisual is Window window)
|
||||
{
|
||||
//确保窗口的 WPF 属性与 Win32 位置一致,防止有逗比全屏后改 WPF 的属性,发生一些诡异的行为
|
||||
//下面这样做其实不太好,会再次触发 WM_WINDOWPOSCHANGING 来着.....但是又没有其他时机了
|
||||
// WM_WINDOWPOSCHANGED 不能用
|
||||
//(例如:在进入全屏后,修改 Left 属性,会进入 WM_WINDOWPOSCHANGING,然后在这里将消息里的结构体中的 Left 改回,
|
||||
// 使对 Left 的修改无效,那么将不会进入 WM_WINDOWPOSCHANGED,窗口尺寸正常,但窗口的 Left 属性值错误。)
|
||||
var logicalPos =
|
||||
hwndSource.CompositionTarget.TransformFromDevice.Transform(
|
||||
new System.Windows.Point(pos.X, pos.Y));
|
||||
var logicalSize =
|
||||
hwndSource.CompositionTarget.TransformFromDevice.Transform(
|
||||
new System.Windows.Point(pos.Width, pos.Height));
|
||||
window.Left = logicalPos.X;
|
||||
window.Top = logicalPos.Y;
|
||||
window.Width = logicalSize.X;
|
||||
window.Height = logicalSize.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
//这个hwnd是前面从Window来的,如果现在他不是Window...... 你信么
|
||||
}
|
||||
}
|
||||
|
||||
//将修改后的结构体拷贝回去
|
||||
Marshal.StructureToPtr(pos, lParam, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 这里也不需要日志啥的,只是为了防止上面有逗比逻辑,在消息循环里面炸了
|
||||
}
|
||||
}
|
||||
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
static class Hotkey
|
||||
{
|
||||
#region 系统api
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
static extern bool RegisterHotKey(IntPtr hWnd, int id, HotkeyModifiers fsModifiers, uint vk);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
static extern bool UnregisterHotKey(IntPtr hWnd, int id);
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 注册快捷键
|
||||
/// </summary>
|
||||
/// <param name="window">持有快捷键窗口</param>
|
||||
/// <param name="fsModifiers">组合键</param>
|
||||
/// <param name="key">快捷键</param>
|
||||
/// <param name="callBack">回调函数</param>
|
||||
public static bool Regist(Window window, HotkeyModifiers fsModifiers, Key key, HotKeyCallBackHanlder callBack)
|
||||
{
|
||||
var hwnd = new WindowInteropHelper(window).Handle;
|
||||
var _hwndSource = HwndSource.FromHwnd(hwnd);
|
||||
|
||||
if (keyid == 10)
|
||||
{
|
||||
_hwndSource.AddHook(WndProc);
|
||||
}
|
||||
|
||||
int id = keyid++;
|
||||
|
||||
var vk = KeyInterop.VirtualKeyFromKey(key);
|
||||
if (!RegisterHotKey(hwnd, id, fsModifiers, (uint)vk))
|
||||
{
|
||||
//throw new Exception("regist hotkey fail.");
|
||||
return false;
|
||||
}
|
||||
keymap[id] = callBack;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 快捷键消息处理
|
||||
/// </summary>
|
||||
static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
||||
{
|
||||
if (msg == WM_HOTKEY)
|
||||
{
|
||||
int id = wParam.ToInt32();
|
||||
if (keymap.TryGetValue(id, out var callback))
|
||||
{
|
||||
callback();
|
||||
}
|
||||
}
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注销快捷键
|
||||
/// </summary>
|
||||
/// <param name="hWnd">持有快捷键窗口的句柄</param>
|
||||
/// <param name="callBack">回调函数</param>
|
||||
public static void UnRegist(IntPtr hWnd, HotKeyCallBackHanlder callBack)
|
||||
{
|
||||
foreach (KeyValuePair<int, HotKeyCallBackHanlder> var in keymap)
|
||||
{
|
||||
if (var.Value == callBack)
|
||||
UnregisterHotKey(hWnd, var.Key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const int WM_HOTKEY = 0x312;
|
||||
static int keyid = 10;
|
||||
static Dictionary<int, HotKeyCallBackHanlder> keymap = new Dictionary<int, HotKeyCallBackHanlder>();
|
||||
|
||||
public delegate void HotKeyCallBackHanlder();
|
||||
}
|
||||
|
||||
enum HotkeyModifiers
|
||||
{
|
||||
MOD_ALT = 0x1,
|
||||
MOD_CONTROL = 0x2,
|
||||
MOD_SHIFT = 0x4,
|
||||
MOD_WIN = 0x8
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
public class InkRecognizeHelper
|
||||
{
|
||||
//识别形状
|
||||
public static ShapeRecognizeResult RecognizeShape(StrokeCollection strokes)
|
||||
{
|
||||
if (strokes == null || strokes.Count == 0)
|
||||
return default;
|
||||
|
||||
var analyzer = new InkAnalyzer();
|
||||
analyzer.AddStrokes(strokes);
|
||||
analyzer.SetStrokesType(strokes, System.Windows.Ink.StrokeType.Drawing);
|
||||
|
||||
AnalysisAlternate analysisAlternate = null;
|
||||
int strokesCount = strokes.Count;
|
||||
var sfsaf = analyzer.Analyze();
|
||||
if (sfsaf.Successful)
|
||||
{
|
||||
var alternates = analyzer.GetAlternates();
|
||||
if (alternates.Count > 0)
|
||||
{
|
||||
while ((!alternates[0].Strokes.Contains(strokes.Last()) ||
|
||||
!IsContainShapeType(((InkDrawingNode)alternates[0].AlternateNodes[0]).GetShapeName()))
|
||||
&& strokesCount >= 2)
|
||||
{
|
||||
analyzer.RemoveStroke(strokes[strokes.Count - strokesCount]);
|
||||
strokesCount--;
|
||||
sfsaf = analyzer.Analyze();
|
||||
if (sfsaf.Successful)
|
||||
{
|
||||
alternates = analyzer.GetAlternates();
|
||||
}
|
||||
}
|
||||
analysisAlternate = alternates[0];
|
||||
}
|
||||
}
|
||||
|
||||
analyzer.Dispose();
|
||||
|
||||
if (analysisAlternate != null && analysisAlternate.AlternateNodes.Count > 0)
|
||||
{
|
||||
var node = analysisAlternate.AlternateNodes[0] as InkDrawingNode;
|
||||
return new ShapeRecognizeResult(node.Centroid, node.HotPoints, analysisAlternate, node);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public static bool IsContainShapeType(string name)
|
||||
{
|
||||
if (name.Contains("Triangle") || name.Contains("Circle") ||
|
||||
name.Contains("Rectangle") || name.Contains("Diamond") ||
|
||||
name.Contains("Parallelogram") || name.Contains("Square")
|
||||
|| name.Contains("Ellipse"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Recognizer 的实现
|
||||
|
||||
public class ShapeRecognizeResult
|
||||
{
|
||||
public ShapeRecognizeResult(Point centroid, PointCollection hotPoints, AnalysisAlternate analysisAlternate, InkDrawingNode node)
|
||||
{
|
||||
Centroid = centroid;
|
||||
HotPoints = hotPoints;
|
||||
AnalysisAlternate = analysisAlternate;
|
||||
InkDrawingNode = node;
|
||||
}
|
||||
|
||||
public AnalysisAlternate AnalysisAlternate { get; }
|
||||
|
||||
public Point Centroid { get; set; }
|
||||
|
||||
public PointCollection HotPoints { get; }
|
||||
|
||||
public InkDrawingNode InkDrawingNode { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 图形识别类
|
||||
/// </summary>
|
||||
//public class ShapeRecogniser
|
||||
//{
|
||||
// public InkAnalyzer _inkAnalyzer = null;
|
||||
|
||||
// private ShapeRecogniser()
|
||||
// {
|
||||
// this._inkAnalyzer = new InkAnalyzer
|
||||
// {
|
||||
// AnalysisModes = AnalysisModes.AutomaticReconciliationEnabled
|
||||
// };
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 根据笔迹集合返回图形名称字符串
|
||||
// /// </summary>
|
||||
// /// <param name="strokeCollection"></param>
|
||||
// /// <returns></returns>
|
||||
// public InkDrawingNode Recognition(StrokeCollection strokeCollection)
|
||||
// {
|
||||
// if (strokeCollection == null)
|
||||
// {
|
||||
// //MessageBox.Show("dddddd");
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// InkDrawingNode result = null;
|
||||
// try
|
||||
// {
|
||||
// this._inkAnalyzer.AddStrokes(strokeCollection);
|
||||
// if (this._inkAnalyzer.Analyze().Successful)
|
||||
// {
|
||||
// result = _internalAnalyzer(this._inkAnalyzer);
|
||||
// this._inkAnalyzer.RemoveStrokes(strokeCollection);
|
||||
// }
|
||||
// }
|
||||
// catch (System.Exception ex)
|
||||
// {
|
||||
// //result = ex.Message;
|
||||
// System.Diagnostics.Debug.WriteLine(ex.Message);
|
||||
// }
|
||||
|
||||
// return result;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 实现笔迹的分析,返回图形对应的字符串
|
||||
// /// 你在实际的应用中根据返回的字符串来生成对应的Shape
|
||||
// /// </summary>
|
||||
// /// <param name="ink"></param>
|
||||
// /// <returns></returns>
|
||||
// private InkDrawingNode _internalAnalyzer(InkAnalyzer ink)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// ContextNodeCollection nodecollections = ink.FindNodesOfType(ContextNodeType.InkDrawing);
|
||||
// foreach (ContextNode node in nodecollections)
|
||||
// {
|
||||
// InkDrawingNode drawingNode = node as InkDrawingNode;
|
||||
// if (drawingNode != null)
|
||||
// {
|
||||
// return drawingNode;//.GetShapeName();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// catch (System.Exception ex)
|
||||
// {
|
||||
// System.Diagnostics.Debug.WriteLine(ex.Message);
|
||||
// }
|
||||
|
||||
// return null;
|
||||
// }
|
||||
|
||||
|
||||
// private static ShapeRecogniser instance = null;
|
||||
// public static ShapeRecogniser Instance
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
// return instance == null ? (instance = new ShapeRecogniser()) : instance;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
//用于自动控制其他形状相对于圆的位置
|
||||
|
||||
public class Circle
|
||||
{
|
||||
public Circle(Point centroid, double r, Stroke stroke)
|
||||
{
|
||||
Centroid = centroid;
|
||||
R = r;
|
||||
Stroke = stroke;
|
||||
}
|
||||
|
||||
public Point Centroid { get; set; }
|
||||
|
||||
public double R { get; set; }
|
||||
|
||||
public Stroke Stroke { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Runtime.Remoting.Contexts;
|
||||
using System.Windows;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using Pen = System.Windows.Media.Pen;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
|
||||
public class InkStrokesOverlay : FrameworkElement
|
||||
{
|
||||
private VisualCollection _children;
|
||||
private ImprovedDrawingVisual _layer = new ImprovedDrawingVisual();
|
||||
private StrokeCollection cachedStrokeCollection = new StrokeCollection();
|
||||
private DrawingGroup cachedDrawingGroup = new DrawingGroup();
|
||||
private bool isCached = false;
|
||||
private DrawingContext context;
|
||||
|
||||
public class ImprovedDrawingVisual: DrawingVisual {
|
||||
public ImprovedDrawingVisual() {
|
||||
CacheMode = new BitmapCache() {
|
||||
EnableClearType = false,
|
||||
RenderAtScale = 1,
|
||||
SnapsToDevicePixels = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public InkStrokesOverlay()
|
||||
{
|
||||
_children = new VisualCollection(this) {
|
||||
_layer // 初始化DrawingVisual
|
||||
};
|
||||
}
|
||||
|
||||
protected override int VisualChildrenCount => _children.Count;
|
||||
|
||||
protected override Visual GetVisualChild(int index)
|
||||
{
|
||||
if (index < 0 || index >= _children.Count) throw new ArgumentOutOfRangeException();
|
||||
return _children[index];
|
||||
}
|
||||
|
||||
public DrawingContext Open() {
|
||||
context = _layer.RenderOpen();
|
||||
return context;
|
||||
}
|
||||
|
||||
public void Close() {
|
||||
context.Close();
|
||||
}
|
||||
|
||||
public void DrawStrokes(StrokeCollection strokes, Matrix? matrixTransform, bool isOneTimeDrawing = true) {
|
||||
if (isOneTimeDrawing) {
|
||||
context = _layer.RenderOpen();
|
||||
}
|
||||
|
||||
if (matrixTransform != null) context.PushTransform(new MatrixTransform((Matrix)matrixTransform));
|
||||
|
||||
if (strokes.Count != 0) {
|
||||
if (!isCached || (isCached && !strokes.Equals(cachedStrokeCollection))) {
|
||||
cachedStrokeCollection = strokes;
|
||||
cachedDrawingGroup = new DrawingGroup();
|
||||
var gp_context = cachedDrawingGroup.Open();
|
||||
var stks_cloned = strokes.Clone();
|
||||
foreach (var stroke in stks_cloned) {
|
||||
stroke.DrawingAttributes.Width += 2;
|
||||
stroke.DrawingAttributes.Height += 2;
|
||||
}
|
||||
stks_cloned.Draw(gp_context);
|
||||
foreach (var ori_stk in strokes) {
|
||||
var geo = ori_stk.GetGeometry();
|
||||
gp_context.DrawGeometry(new SolidColorBrush(Colors.White),null,geo);
|
||||
|
||||
}
|
||||
gp_context.Close();
|
||||
}
|
||||
}
|
||||
|
||||
context.DrawDrawing(cachedDrawingGroup);
|
||||
|
||||
if (matrixTransform != null) context.Pop();
|
||||
|
||||
if (isOneTimeDrawing) {
|
||||
context.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows;
|
||||
|
||||
namespace Ink_Canvas.Helpers {
|
||||
internal class IsOutsideOfScreenHelper {
|
||||
public static bool IsOutsideOfScreen(FrameworkElement target) {
|
||||
var hwndSource = (HwndSource)PresentationSource.FromVisual(target);
|
||||
if (hwndSource is null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var hWnd = hwndSource.Handle;
|
||||
var targetBounds = GetPixelBoundsToScreen(target);
|
||||
|
||||
var screens = System.Windows.Forms.Screen.AllScreens;
|
||||
return !screens.Any(x => x.Bounds.IntersectsWith(targetBounds));
|
||||
|
||||
System.Drawing.Rectangle GetPixelBoundsToScreen(FrameworkElement visual) {
|
||||
var pixelBoundsToScreen = Rect.Empty;
|
||||
pixelBoundsToScreen.Union(visual.PointToScreen(new Point(0, 0)));
|
||||
pixelBoundsToScreen.Union(visual.PointToScreen(new Point(visual.ActualWidth, 0)));
|
||||
pixelBoundsToScreen.Union(visual.PointToScreen(new Point(0, visual.ActualHeight)));
|
||||
pixelBoundsToScreen.Union(visual.PointToScreen(new Point(visual.ActualWidth, visual.ActualHeight)));
|
||||
return new System.Drawing.Rectangle(
|
||||
(int)pixelBoundsToScreen.X, (int)pixelBoundsToScreen.Y,
|
||||
(int)pixelBoundsToScreen.Width, (int)pixelBoundsToScreen.Height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
class LogHelper
|
||||
{
|
||||
public static string LogFile = "Log.txt";
|
||||
|
||||
public static void NewLog(string str)
|
||||
{
|
||||
WriteLogToFile(str, LogType.Info);
|
||||
}
|
||||
|
||||
public static void NewLog(Exception ex)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static void WriteLogToFile(string str, LogType logType = LogType.Info)
|
||||
{
|
||||
string strLogType = "Info";
|
||||
switch (logType)
|
||||
{
|
||||
case LogType.Event:
|
||||
strLogType = "Event";
|
||||
break;
|
||||
case LogType.Trace:
|
||||
strLogType = "Trace";
|
||||
break;
|
||||
case LogType.Error:
|
||||
strLogType = "Error";
|
||||
break;
|
||||
}
|
||||
try
|
||||
{
|
||||
var file = App.RootPath + LogFile;
|
||||
if (!Directory.Exists(App.RootPath))
|
||||
{
|
||||
Directory.CreateDirectory(App.RootPath);
|
||||
}
|
||||
StreamWriter sw = new StreamWriter(file, true);
|
||||
sw.WriteLine(string.Format("{0} [{1}] {2}", DateTime.Now.ToString("O"), strLogType, str));
|
||||
sw.Close();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public enum LogType
|
||||
{
|
||||
Info,
|
||||
Trace,
|
||||
Error,
|
||||
Event
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
public class VisualCanvas : FrameworkElement
|
||||
{
|
||||
protected override Visual GetVisualChild(int index)
|
||||
{
|
||||
return Visual;
|
||||
}
|
||||
|
||||
protected override int VisualChildrenCount => 1;
|
||||
|
||||
public VisualCanvas(DrawingVisual visual)
|
||||
{
|
||||
Visual = visual;
|
||||
AddVisualChild(visual);
|
||||
}
|
||||
|
||||
public DrawingVisual Visual { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用于显示笔迹的类
|
||||
/// </summary>
|
||||
public class StrokeVisual : DrawingVisual
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建显示笔迹的类
|
||||
/// </summary>
|
||||
public StrokeVisual() : this(new DrawingAttributes()
|
||||
{
|
||||
Color = Colors.Red,
|
||||
//FitToCurve = true,
|
||||
Width = 3,
|
||||
Height = 3
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建显示笔迹的类
|
||||
/// </summary>
|
||||
/// <param name="drawingAttributes"></param>
|
||||
public StrokeVisual(DrawingAttributes drawingAttributes)
|
||||
{
|
||||
_drawingAttributes = drawingAttributes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置或获取显示的笔迹
|
||||
/// </summary>
|
||||
public Stroke Stroke { set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 在笔迹中添加点
|
||||
/// </summary>
|
||||
/// <param name="point"></param>
|
||||
public void Add(StylusPoint point)
|
||||
{
|
||||
if (Stroke == null)
|
||||
{
|
||||
var collection = new StylusPointCollection { point };
|
||||
Stroke = new Stroke(collection) { DrawingAttributes = _drawingAttributes };
|
||||
}
|
||||
else
|
||||
{
|
||||
Stroke.StylusPoints.Add(point);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重新画出笔迹
|
||||
/// </summary>
|
||||
public void Redraw()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var dc = RenderOpen())
|
||||
{
|
||||
Stroke.Draw(dc);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private readonly DrawingAttributes _drawingAttributes;
|
||||
|
||||
public static implicit operator Stroke(StrokeVisual v)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,600 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ink_Canvas.Helpers {
|
||||
public partial class PerformanceTransparentWin {
|
||||
static class Win32 {
|
||||
|
||||
public enum WM
|
||||
{
|
||||
NULL = 0x0000,
|
||||
CREATE = 0x0001,
|
||||
DESTROY = 0x0002,
|
||||
MOVE = 0x0003,
|
||||
SIZE = 0x0005,
|
||||
ACTIVATE = 0x0006,
|
||||
SETFOCUS = 0x0007,
|
||||
KILLFOCUS = 0x0008,
|
||||
ENABLE = 0x000A,
|
||||
SETREDRAW = 0x000B,
|
||||
SETTEXT = 0x000C,
|
||||
GETTEXT = 0x000D,
|
||||
GETTEXTLENGTH = 0x000E,
|
||||
PAINT = 0x000F,
|
||||
CLOSE = 0x0010,
|
||||
QUERYENDSESSION = 0x0011,
|
||||
QUERYOPEN = 0x0013,
|
||||
ENDSESSION = 0x0016,
|
||||
QUIT = 0x0012,
|
||||
ERASEBKGND = 0x0014,
|
||||
SYSCOLORCHANGE = 0x0015,
|
||||
SHOWWINDOW = 0x0018,
|
||||
WININICHANGE = 0x001A,
|
||||
SETTINGCHANGE = WININICHANGE,
|
||||
DEVMODECHANGE = 0x001B,
|
||||
ACTIVATEAPP = 0x001C,
|
||||
FONTCHANGE = 0x001D,
|
||||
TIMECHANGE = 0x001E,
|
||||
CANCELMODE = 0x001F,
|
||||
SETCURSOR = 0x0020,
|
||||
MOUSEACTIVATE = 0x0021,
|
||||
CHILDACTIVATE = 0x0022,
|
||||
QUEUESYNC = 0x0023,
|
||||
GETMINMAXINFO = 0x0024,
|
||||
PAINTICON = 0x0026,
|
||||
ICONERASEBKGND = 0x0027,
|
||||
NEXTDLGCTL = 0x0028,
|
||||
SPOOLERSTATUS = 0x002A,
|
||||
DRAWITEM = 0x002B,
|
||||
MEASUREITEM = 0x002C,
|
||||
DELETEITEM = 0x002D,
|
||||
VKEYTOITEM = 0x002E,
|
||||
CHARTOITEM = 0x002F,
|
||||
SETFONT = 0x0030,
|
||||
GETFONT = 0x0031,
|
||||
SETHOTKEY = 0x0032,
|
||||
GETHOTKEY = 0x0033,
|
||||
QUERYDRAGICON = 0x0037,
|
||||
COMPAREITEM = 0x0039,
|
||||
GETOBJECT = 0x003D,
|
||||
COMPACTING = 0x0041,
|
||||
COMMNOTIFY = 0x0044 /* no longer suported */,
|
||||
WINDOWPOSCHANGING = 0x0046,
|
||||
WINDOWPOSCHANGED = 0x0047,
|
||||
POWER = 0x0048,
|
||||
COPYDATA = 0x004A,
|
||||
CANCELJOURNAL = 0x004B,
|
||||
NOTIFY = 0x004E,
|
||||
INPUTLANGCHANGEREQUEST = 0x0050,
|
||||
INPUTLANGCHANGE = 0x0051,
|
||||
TCARD = 0x0052,
|
||||
HELP = 0x0053,
|
||||
USERCHANGED = 0x0054,
|
||||
NOTIFYFORMAT = 0x0055,
|
||||
CONTEXTMENU = 0x007B,
|
||||
STYLECHANGING = 0x007C,
|
||||
STYLECHANGED = 0x007D,
|
||||
DISPLAYCHANGE = 0x007E,
|
||||
GETICON = 0x007F,
|
||||
SETICON = 0x0080,
|
||||
NCCREATE = 0x0081,
|
||||
NCDESTROY = 0x0082,
|
||||
NCCALCSIZE = 0x0083,
|
||||
NCHITTEST = 0x0084,
|
||||
NCPAINT = 0x0085,
|
||||
NCACTIVATE = 0x0086,
|
||||
GETDLGCODE = 0x0087,
|
||||
SYNCPAINT = 0x0088,
|
||||
NCMOUSEMOVE = 0x00A0,
|
||||
NCLBUTTONDOWN = 0x00A1,
|
||||
NCLBUTTONUP = 0x00A2,
|
||||
NCLBUTTONDBLCLK = 0x00A3,
|
||||
NCRBUTTONDOWN = 0x00A4,
|
||||
NCRBUTTONUP = 0x00A5,
|
||||
NCRBUTTONDBLCLK = 0x00A6,
|
||||
NCMBUTTONDOWN = 0x00A7,
|
||||
NCMBUTTONUP = 0x00A8,
|
||||
NCMBUTTONDBLCLK = 0x00A9,
|
||||
NCXBUTTONDOWN = 0x00AB,
|
||||
NCXBUTTONUP = 0x00AC,
|
||||
NCXBUTTONDBLCLK = 0x00AD,
|
||||
INPUT_DEVICE_CHANGE = 0x00FE,
|
||||
INPUT = 0x00FF,
|
||||
KEYFIRST = 0x0100,
|
||||
KEYDOWN = 0x0100,
|
||||
KEYUP = 0x0101,
|
||||
CHAR = 0x0102,
|
||||
DEADCHAR = 0x0103,
|
||||
SYSKEYDOWN = 0x0104,
|
||||
SYSKEYUP = 0x0105,
|
||||
SYSCHAR = 0x0106,
|
||||
SYSDEADCHAR = 0x0107,
|
||||
UNICHAR = 0x0109,
|
||||
KEYLAST = 0x0109,
|
||||
IME_STARTCOMPOSITION = 0x010D,
|
||||
IME_ENDCOMPOSITION = 0x010E,
|
||||
IME_COMPOSITION = 0x010F,
|
||||
IME_KEYLAST = 0x010F,
|
||||
INITDIALOG = 0x0110,
|
||||
COMMAND = 0x0111,
|
||||
SYSCOMMAND = 0x0112,
|
||||
TIMER = 0x0113,
|
||||
HSCROLL = 0x0114,
|
||||
VSCROLL = 0x0115,
|
||||
INITMENU = 0x0116,
|
||||
INITMENUPOPUP = 0x0117,
|
||||
GESTURE = 0x0119,
|
||||
GESTURENOTIFY = 0x011A,
|
||||
MENUSELECT = 0x011F,
|
||||
MENUCHAR = 0x0120,
|
||||
ENTERIDLE = 0x0121,
|
||||
MENURBUTTONUP = 0x0122,
|
||||
MENUDRAG = 0x0123,
|
||||
MENUGETOBJECT = 0x0124,
|
||||
UNINITMENUPOPUP = 0x0125,
|
||||
MENUCOMMAND = 0x0126,
|
||||
CHANGEUISTATE = 0x0127,
|
||||
UPDATEUISTATE = 0x0128,
|
||||
QUERYUISTATE = 0x0129,
|
||||
CTLCOLORMSGBOX = 0x0132,
|
||||
CTLCOLOREDIT = 0x0133,
|
||||
CTLCOLORLISTBOX = 0x0134,
|
||||
CTLCOLORBTN = 0x0135,
|
||||
CTLCOLORDLG = 0x0136,
|
||||
CTLCOLORSCROLLBAR = 0x0137,
|
||||
CTLCOLORSTATIC = 0x0138,
|
||||
MOUSEFIRST = 0x0200,
|
||||
MOUSEMOVE = 0x0200,
|
||||
LBUTTONDOWN = 0x0201,
|
||||
LBUTTONUP = 0x0202,
|
||||
LBUTTONDBLCLK = 0x0203,
|
||||
RBUTTONDOWN = 0x0204,
|
||||
RBUTTONUP = 0x0205,
|
||||
RBUTTONDBLCLK = 0x0206,
|
||||
MBUTTONDOWN = 0x0207,
|
||||
MBUTTONUP = 0x0208,
|
||||
MBUTTONDBLCLK = 0x0209,
|
||||
MOUSEWHEEL = 0x020A,
|
||||
XBUTTONDOWN = 0x020B,
|
||||
XBUTTONUP = 0x020C,
|
||||
XBUTTONDBLCLK = 0x020D,
|
||||
MOUSEHWHEEL = 0x020E,
|
||||
MOUSELAST = 0x020E,
|
||||
PARENTNOTIFY = 0x0210,
|
||||
ENTERMENULOOP = 0x0211,
|
||||
EXITMENULOOP = 0x0212,
|
||||
NEXTMENU = 0x0213,
|
||||
SIZING = 0x0214,
|
||||
CAPTURECHANGED = 0x0215,
|
||||
MOVING = 0x0216,
|
||||
POWERBROADCAST = 0x0218,
|
||||
DEVICECHANGE = 0x0219,
|
||||
MDICREATE = 0x0220,
|
||||
MDIDESTROY = 0x0221,
|
||||
MDIACTIVATE = 0x0222,
|
||||
MDIRESTORE = 0x0223,
|
||||
MDINEXT = 0x0224,
|
||||
MDIMAXIMIZE = 0x0225,
|
||||
MDITILE = 0x0226,
|
||||
MDICASCADE = 0x0227,
|
||||
MDIICONARRANGE = 0x0228,
|
||||
MDIGETACTIVE = 0x0229,
|
||||
MDISETMENU = 0x0230,
|
||||
ENTERSIZEMOVE = 0x0231,
|
||||
EXITSIZEMOVE = 0x0232,
|
||||
DROPFILES = 0x0233,
|
||||
MDIREFRESHMENU = 0x0234,
|
||||
POINTERDEVICECHANGE = 0x238,
|
||||
POINTERDEVICEINRANGE = 0x239,
|
||||
POINTERDEVICEOUTOFRANGE = 0x23A,
|
||||
TOUCH = 0x0240,
|
||||
NCPOINTERUPDATE = 0x0241,
|
||||
NCPOINTERDOWN = 0x0242,
|
||||
NCPOINTERUP = 0x0243,
|
||||
POINTERUPDATE = 0x0245,
|
||||
POINTERDOWN = 0x0246,
|
||||
POINTERUP = 0x0247,
|
||||
POINTERENTER = 0x0249,
|
||||
POINTERLEAVE = 0x024A,
|
||||
POINTERACTIVATE = 0x024B,
|
||||
POINTERCAPTURECHANGED = 0x024C,
|
||||
TOUCHHITTESTING = 0x024D,
|
||||
POINTERWHEEL = 0x024E,
|
||||
POINTERHWHEEL = 0x024F,
|
||||
IME_SETCONTEXT = 0x0281,
|
||||
IME_NOTIFY = 0x0282,
|
||||
IME_CONTROL = 0x0283,
|
||||
IME_COMPOSITIONFULL = 0x0284,
|
||||
IME_SELECT = 0x0285,
|
||||
IME_CHAR = 0x0286,
|
||||
IME_REQUEST = 0x0288,
|
||||
IME_KEYDOWN = 0x0290,
|
||||
IME_KEYUP = 0x0291,
|
||||
MOUSEHOVER = 0x02A1,
|
||||
MOUSELEAVE = 0x02A3,
|
||||
NCMOUSEHOVER = 0x02A0,
|
||||
NCMOUSELEAVE = 0x02A2,
|
||||
WTSSESSION_CHANGE = 0x02B1,
|
||||
TABLET_FIRST = 0x02c0,
|
||||
TABLET_LAST = 0x02df,
|
||||
DPICHANGED = 0x02E0,
|
||||
CUT = 0x0300,
|
||||
COPY = 0x0301,
|
||||
PASTE = 0x0302,
|
||||
CLEAR = 0x0303,
|
||||
UNDO = 0x0304,
|
||||
RENDERFORMAT = 0x0305,
|
||||
RENDERALLFORMATS = 0x0306,
|
||||
DESTROYCLIPBOARD = 0x0307,
|
||||
DRAWCLIPBOARD = 0x0308,
|
||||
PAINTCLIPBOARD = 0x0309,
|
||||
VSCROLLCLIPBOARD = 0x030A,
|
||||
SIZECLIPBOARD = 0x030B,
|
||||
ASKCBFORMATNAME = 0x030C,
|
||||
CHANGECBCHAIN = 0x030D,
|
||||
HSCROLLCLIPBOARD = 0x030E,
|
||||
QUERYNEWPALETTE = 0x030F,
|
||||
PALETTEISCHANGING = 0x0310,
|
||||
PALETTECHANGED = 0x0311,
|
||||
HOTKEY = 0x0312,
|
||||
PRINT = 0x0317,
|
||||
PRINTCLIENT = 0x0318,
|
||||
APPCOMMAND = 0x0319,
|
||||
THEMECHANGED = 0x031A,
|
||||
CLIPBOARDUPDATE = 0x031D,
|
||||
DWMCOMPOSITIONCHANGED = 0x031E,
|
||||
DWMNCRENDERINGCHANGED = 0x031F,
|
||||
DWMCOLORIZATIONCOLORCHANGED = 0x0320,
|
||||
DWMWINDOWMAXIMIZEDCHANGE = 0x0321,
|
||||
DWMSENDICONICTHUMBNAIL = 0x0323,
|
||||
DWMSENDICONICLIVEPREVIEWBITMAP = 0x0326,
|
||||
GETTITLEBARINFOEX = 0x033F,
|
||||
HANDHELDFIRST = 0x0358,
|
||||
HANDHELDLAST = 0x035F,
|
||||
AFXFIRST = 0x0360,
|
||||
AFXLAST = 0x037F,
|
||||
PENWINFIRST = 0x0380,
|
||||
PENWINLAST = 0x038F,
|
||||
APP = 0x8000,
|
||||
USER = 0x0400
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 扩展的窗口风格
|
||||
/// 这是 long 类型的,如果想要使用 int 类型请使用 <see cref="WindowExStyles"/> 类
|
||||
/// </summary>
|
||||
/// 代码:[Extended Window Styles (Windows)](https://msdn.microsoft.com/en-us/library/windows/desktop/ff700543(v=vs.85).aspx )
|
||||
/// code from [Extended Window Styles (Windows)](https://msdn.microsoft.com/en-us/library/windows/desktop/ff700543(v=vs.85).aspx )
|
||||
[Flags]
|
||||
public enum ExtendedWindowStyles : long {
|
||||
/// <summary>
|
||||
/// The window accepts drag-drop files
|
||||
/// </summary>
|
||||
WS_EX_ACCEPTFILES = 0x00000010L,
|
||||
|
||||
/// <summary>
|
||||
/// Forces a top-level window onto the taskbar when the window is visible
|
||||
/// </summary>
|
||||
WS_EX_APPWINDOW = 0x00040000L,
|
||||
|
||||
/// <summary>
|
||||
/// The window has a border with a sunken edge.
|
||||
/// </summary>
|
||||
WS_EX_CLIENTEDGE = 0x00000200L,
|
||||
|
||||
/// <summary>
|
||||
/// Paints all descendants of a window in bottom-to-top painting order using double-buffering. For more information, see Remarks. This cannot be used if the window has a class style of either CS_OWNDC or CS_CLASSDC.Windows 2000: This style is not supported.
|
||||
/// </summary>
|
||||
WS_EX_COMPOSITED = 0x02000000L,
|
||||
|
||||
/// <summary>
|
||||
/// The title bar of the window includes a question mark. When the user clicks the question mark, the cursor changes to a question mark with a pointer. If the user then clicks a child window, the child receives a WM_HELP message. The child window should pass the message to the parent window procedure, which should call the WinHelp function using the HELP_WM_HELP command. The Help application displays a pop-up window that typically contains help for the child window.WS_EX_CONTEXTHELP cannot be used with the WS_MAXIMIZEBOX or WS_MINIMIZEBOX styles.
|
||||
/// </summary>
|
||||
WS_EX_CONTEXTHELP = 0x00000400L,
|
||||
|
||||
/// <summary>
|
||||
/// The window itself contains child windows that should take part in dialog box navigation. If this style is specified, the dialog manager recurses into children of this window when performing navigation operations such as handling the TAB key, an arrow key, or a keyboard mnemonic.
|
||||
/// </summary>
|
||||
WS_EX_CONTROLPARENT = 0x00010000L,
|
||||
|
||||
/// <summary>
|
||||
/// The window has a double border; the window can, optionally, be created with a title bar by specifying the WS_CAPTION style in the dwStyle parameter.
|
||||
/// </summary>
|
||||
WS_EX_DLGMODALFRAME = 0x00000001L,
|
||||
|
||||
/// <summary>
|
||||
/// The window is a layered window. This style cannot be used if the window has a class style of either CS_OWNDC or CS_CLASSDC.Windows 8: The WS_EX_LAYERED style is supported for top-level windows and child windows. Previous Windows versions support WS_EX_LAYERED only for top-level windows.
|
||||
/// </summary>
|
||||
WS_EX_LAYERED = 0x00080000,
|
||||
|
||||
/// <summary>
|
||||
/// If the shell language is Hebrew, Arabic, or another language that supports reading order alignment, the horizontal origin of the window is on the right edge. Increasing horizontal values advance to the left.
|
||||
/// </summary>
|
||||
WS_EX_LAYOUTRTL = 0x00400000L,
|
||||
|
||||
/// <summary>
|
||||
/// The window has generic left-aligned properties. This is the default.
|
||||
/// </summary>
|
||||
WS_EX_LEFT = 0x00000000L,
|
||||
|
||||
/// <summary>
|
||||
/// If the shell language is Hebrew, Arabic, or another language that supports reading order alignment, the vertical scroll bar (if present) is to the left of the client area. For other languages, the style is ignored.
|
||||
/// </summary>
|
||||
WS_EX_LEFTSCROLLBAR = 0x00004000L,
|
||||
|
||||
/// <summary>
|
||||
/// The window text is displayed using left-to-right reading-order properties. This is the default.
|
||||
/// </summary>
|
||||
WS_EX_LTRREADING = 0x00000000L,
|
||||
|
||||
/// <summary>
|
||||
/// The window is a MDI child window.
|
||||
/// </summary>
|
||||
WS_EX_MDICHILD = 0x00000040L,
|
||||
|
||||
/// <summary>
|
||||
/// A top-level window created with this style does not become the foreground window when the user clicks it. The system does not bring this window to the foreground when the user minimizes or closes the foreground window.To activate the window, use the SetActiveWindow or SetForegroundWindow function.The window does not appear on the taskbar by default. To force the window to appear on the taskbar, use the WS_EX_APPWINDOW style.
|
||||
/// </summary>
|
||||
WS_EX_NOACTIVATE = 0x08000000L,
|
||||
|
||||
/// <summary>
|
||||
/// The window does not pass its window layout to its child windows.
|
||||
/// </summary>
|
||||
WS_EX_NOINHERITLAYOUT = 0x00100000L,
|
||||
|
||||
/// <summary>
|
||||
/// The child window created with this style does not send the WM_PARENTNOTIFY message to its parent window when it is created or destroyed.
|
||||
/// </summary>
|
||||
WS_EX_NOPARENTNOTIFY = 0x00000004L,
|
||||
|
||||
/// <summary>
|
||||
/// The window does not render to a redirection surface. This is for windows that do not have visible content or that use mechanisms other than surfaces to provide their visual.
|
||||
/// </summary>
|
||||
WS_EX_NOREDIRECTIONBITMAP = 0x00200000L,
|
||||
|
||||
/// <summary>
|
||||
/// The window is an overlapped window.
|
||||
/// </summary>
|
||||
WS_EX_OVERLAPPEDWINDOW = (WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE),
|
||||
|
||||
/// <summary>
|
||||
/// The window is palette window, which is a modeless dialog box that presents an array of commands.
|
||||
/// </summary>
|
||||
WS_EX_PALETTEWINDOW = (WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST),
|
||||
|
||||
/// <summary>
|
||||
/// The window has generic "right-aligned" properties. This depends on the window class. This style has an effect only if the shell language is Hebrew, Arabic, or another language that supports reading-order alignment; otherwise, the style is ignored.Using the WS_EX_RIGHT style for static or edit controls has the same effect as using the SS_RIGHT or ES_RIGHT style, respectively. Using this style with button controls has the same effect as using BS_RIGHT and BS_RIGHTBUTTON styles.
|
||||
/// </summary>
|
||||
WS_EX_RIGHT = 0x00001000L,
|
||||
|
||||
/// <summary>
|
||||
/// The vertical scroll bar (if present) is to the right of the client area. This is the default.
|
||||
/// </summary>
|
||||
WS_EX_RIGHTSCROLLBAR = 0x00000000L,
|
||||
|
||||
/// <summary>
|
||||
/// If the shell language is Hebrew, Arabic, or another language that supports reading-order alignment, the window text is displayed using right-to-left reading-order properties. For other languages, the style is ignored.
|
||||
/// </summary>
|
||||
WS_EX_RTLREADING = 0x00002000L,
|
||||
|
||||
/// <summary>
|
||||
/// The window has a three-dimensional border style intended to be used for items that do not accept user input.
|
||||
/// </summary>
|
||||
WS_EX_STATICEDGE = 0x00020000L,
|
||||
|
||||
/// <summary>
|
||||
/// The window is intended to be used as a floating toolbar. A tool window has a title bar that is shorter than a normal title bar, and the window title is drawn using a smaller font. A tool window does not appear in the taskbar or in the dialog that appears when the user presses ALT+TAB. If a tool window has a system menu, its icon is not displayed on the title bar. However, you can display the system menu by right-clicking or by typing ALT+SPACE.
|
||||
/// </summary>
|
||||
WS_EX_TOOLWINDOW = 0x00000080L,
|
||||
|
||||
/// <summary>
|
||||
/// The window should be placed above all non-topmost windows and should stay above them, even when the window is deactivated. To add or remove this style, use the SetWindowPos function.
|
||||
/// </summary>
|
||||
WS_EX_TOPMOST = 0x00000008L,
|
||||
|
||||
/// <summary>
|
||||
/// The window should not be painted until siblings beneath the window (that were created by the same thread) have been painted. The window appears transparent because the bits of underlying sibling windows have already been painted.To achieve transparency without these restrictions, use the SetWindowRgn function.
|
||||
/// </summary>
|
||||
WS_EX_TRANSPARENT = 0x00000020L,
|
||||
|
||||
/// <summary>
|
||||
/// The window has a border with a raised edge
|
||||
/// </summary>
|
||||
WS_EX_WINDOWEDGE = 0x00000100L
|
||||
}
|
||||
|
||||
public static partial class User32 {
|
||||
/// <summary>
|
||||
/// 获得指定窗口的信息
|
||||
/// </summary>
|
||||
/// <param name="hWnd">指定窗口的句柄</param>
|
||||
/// <param name="nIndex">需要获得的信息的类型 请使用<see cref="GetWindowLongFields"/></param>
|
||||
/// <returns></returns>
|
||||
// This static method is required because Win32 does not support
|
||||
// GetWindowLongPtr directly
|
||||
public static IntPtr GetWindowLongPtr(IntPtr hWnd, GetWindowLongFields nIndex) =>
|
||||
GetWindowLongPtr(hWnd, (int)nIndex);
|
||||
|
||||
/// <summary>
|
||||
/// 获得指定窗口的信息
|
||||
/// </summary>
|
||||
/// <param name="hWnd">指定窗口的句柄</param>
|
||||
/// <param name="nIndex">需要获得的信息的类型 请使用<see cref="GetWindowLongFields"/></param>
|
||||
/// <returns></returns>
|
||||
// This static method is required because Win32 does not support
|
||||
// GetWindowLongPtr directly
|
||||
public static IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex) {
|
||||
return IntPtr.Size > 4
|
||||
#pragma warning disable CS0618 // 类型或成员已过时
|
||||
? GetWindowLongPtr_x64(hWnd, nIndex)
|
||||
: new IntPtr(GetWindowLong(hWnd, nIndex));
|
||||
#pragma warning restore CS0618 // 类型或成员已过时
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获得指定窗口的信息
|
||||
/// </summary>
|
||||
/// <param name="hWnd">指定窗口的句柄</param>
|
||||
/// <param name="nIndex">需要获得的信息的类型 请使用<see cref="GetWindowLongFields"/></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("请使用 GetWindowLongPtr 解决 x86 和 x64 需要使用不同方法")]
|
||||
[DllImport(LibraryName, CharSet = Properties.BuildCharSet)]
|
||||
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
|
||||
|
||||
/// <summary>
|
||||
/// 获得指定窗口的信息
|
||||
/// </summary>
|
||||
/// <param name="hWnd">指定窗口的句柄</param>
|
||||
/// <param name="nIndex">需要获得的信息的类型 请使用<see cref="GetWindowLongFields"/></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("请使用 GetWindowLongPtr 解决 x86 和 x64 需要使用不同方法")]
|
||||
[DllImport(LibraryName, CharSet = Properties.BuildCharSet, EntryPoint = "GetWindowLongPtr")]
|
||||
public static extern IntPtr GetWindowLongPtr_x64(IntPtr hWnd, int nIndex);
|
||||
|
||||
/// <summary>
|
||||
/// 改变指定窗口的属性
|
||||
/// </summary>
|
||||
/// <param name="hWnd">窗口句柄</param>
|
||||
/// <param name="nIndex">
|
||||
/// 指定将设定的大于等于0的偏移值。有效值的范围从0到额外类的存储空间的字节数减4:例如若指定了12或多于12个字节的额外窗口存储空间,则应设索引位8来访问第三个4字节,同样设置0访问第一个4字节,4访问第二个4字节。要设置其他任何值,可以指定下面值之一
|
||||
/// 从 GetWindowLongFields 可以找到所有的值
|
||||
/// </param>
|
||||
/// <param name="dwNewLong">指定的替换值</param>
|
||||
/// <returns></returns>
|
||||
public static IntPtr SetWindowLongPtr(IntPtr hWnd, GetWindowLongFields nIndex, IntPtr dwNewLong) =>
|
||||
SetWindowLongPtr(hWnd, (int)nIndex, dwNewLong);
|
||||
|
||||
/// <summary>
|
||||
/// 改变指定窗口的属性
|
||||
/// </summary>
|
||||
/// <param name="hWnd">窗口句柄</param>
|
||||
/// <param name="nIndex">指定将设定的大于等于0的偏移值。有效值的范围从0到额外类的存储空间的字节数减4:例如若指定了12或多于12个字节的额外窗口存储空间,则应设索引位8来访问第三个4字节,同样设置0访问第一个4字节,4访问第二个4字节。要设置其他任何值,可以指定下面值之一
|
||||
/// 从 GetWindowLongFields 可以找到所有的值
|
||||
/// <para>
|
||||
/// GetWindowLongFields.GWL_EXSTYLE -20 设定一个新的扩展风格。 </para>
|
||||
/// <para>GWL_HINSTANCE -6 设置一个新的应用程序实例句柄。</para>
|
||||
/// <para>GWL_ID -12 设置一个新的窗口标识符。</para>
|
||||
/// <para>GWL_STYLE -16 设定一个新的窗口风格。</para>
|
||||
/// <para>GWL_USERDATA -21 设置与窗口有关的32位值。每个窗口均有一个由创建该窗口的应用程序使用的32位值。</para>
|
||||
/// <para>GWL_WNDPROC -4 为窗口设定一个新的处理函数。</para>
|
||||
/// <para>GWL_HWNDPARENT -8 改变子窗口的父窗口,应使用SetParent函数</para>
|
||||
/// </param>
|
||||
/// <param name="dwNewLong">指定的替换值</param>
|
||||
/// <returns></returns>
|
||||
// This static method is required because Win32 does not support
|
||||
// GetWindowLongPtr directly
|
||||
public static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong) {
|
||||
return IntPtr.Size > 4
|
||||
#pragma warning disable CS0618 // 类型或成员已过时
|
||||
? SetWindowLongPtr_x64(hWnd, nIndex, dwNewLong)
|
||||
: new IntPtr(SetWindowLong(hWnd, nIndex, dwNewLong.ToInt32()));
|
||||
#pragma warning restore CS0618 // 类型或成员已过时
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 改变指定窗口的属性
|
||||
/// </summary>
|
||||
/// <param name="hWnd">窗口句柄</param>
|
||||
/// <param name="nIndex">指定将设定的大于等于0的偏移值。有效值的范围从0到额外类的存储空间的字节数减4:例如若指定了12或多于12个字节的额外窗口存储空间,则应设索引位8来访问第三个4字节,同样设置0访问第一个4字节,4访问第二个4字节。要设置其他任何值,可以指定下面值之一
|
||||
/// 从 GetWindowLongFields 可以找到所有的值
|
||||
/// <para>
|
||||
/// GetWindowLongFields.GWL_EXSTYLE -20 设定一个新的扩展风格。 </para>
|
||||
/// <para>GWL_HINSTANCE -6 设置一个新的应用程序实例句柄。</para>
|
||||
/// <para>GWL_ID -12 设置一个新的窗口标识符。</para>
|
||||
/// <para>GWL_STYLE -16 设定一个新的窗口风格。</para>
|
||||
/// <para>GWL_USERDATA -21 设置与窗口有关的32位值。每个窗口均有一个由创建该窗口的应用程序使用的32位值。</para>
|
||||
/// <para>GWL_WNDPROC -4 为窗口设定一个新的处理函数。</para>
|
||||
/// <para>GWL_HWNDPARENT -8 改变子窗口的父窗口,应使用SetParent函数</para>
|
||||
/// </param>
|
||||
/// <param name="dwNewLong">指定的替换值</param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("请使用 SetWindowLongPtr 解决 x86 和 x64 需要使用不同方法")]
|
||||
[DllImport(LibraryName, CharSet = Properties.BuildCharSet)]
|
||||
public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
|
||||
|
||||
/// <summary>
|
||||
/// 改变指定窗口的属性
|
||||
/// </summary>
|
||||
/// <param name="hWnd">窗口句柄</param>
|
||||
/// <param name="nIndex">指定将设定的大于等于0的偏移值。有效值的范围从0到额外类的存储空间的字节数减4:例如若指定了12或多于12个字节的额外窗口存储空间,则应设索引位8来访问第三个4字节,同样设置0访问第一个4字节,4访问第二个4字节。要设置其他任何值,可以指定下面值之一
|
||||
/// 从 GetWindowLongFields 可以找到所有的值
|
||||
/// <para>
|
||||
/// GetWindowLongFields.GWL_EXSTYLE -20 设定一个新的扩展风格。 </para>
|
||||
/// <para>GWL_HINSTANCE -6 设置一个新的应用程序实例句柄。</para>
|
||||
/// <para>GWL_ID -12 设置一个新的窗口标识符。</para>
|
||||
/// <para>GWL_STYLE -16 设定一个新的窗口风格。</para>
|
||||
/// <para>GWL_USERDATA -21 设置与窗口有关的32位值。每个窗口均有一个由创建该窗口的应用程序使用的32位值。</para>
|
||||
/// <para>GWL_WNDPROC -4 为窗口设定一个新的处理函数。</para>
|
||||
/// <para>GWL_HWNDPARENT -8 改变子窗口的父窗口,应使用SetParent函数</para>
|
||||
/// </param>
|
||||
/// <param name="dwNewLong">指定的替换值</param>
|
||||
/// <returns></returns>
|
||||
[DllImport(LibraryName, CharSet = Properties.BuildCharSet, EntryPoint = "SetWindowLongPtr")]
|
||||
[Obsolete("请使用 SetWindowLongPtr 解决 x86 和 x64 需要使用不同方法")]
|
||||
public static extern IntPtr SetWindowLongPtr_x64(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
|
||||
|
||||
public const string LibraryName = "user32";
|
||||
}
|
||||
|
||||
internal static class Properties {
|
||||
#if !ANSI
|
||||
public const CharSet BuildCharSet = CharSet.Unicode;
|
||||
#else
|
||||
public const CharSet BuildCharSet = CharSet.Ansi;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用于在 <see cref="Win32.GetWindowLong"/> 的 int index 传入
|
||||
/// </summary>
|
||||
/// 代码:[GetWindowLong function (Windows)](https://msdn.microsoft.com/en-us/library/windows/desktop/ms633584(v=vs.85).aspx )
|
||||
public enum GetWindowLongFields {
|
||||
/// <summary>
|
||||
/// 设定一个新的扩展风格
|
||||
/// Retrieves the extended window styles
|
||||
/// </summary>
|
||||
GWL_EXSTYLE = -20,
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个新的应用程序实例句柄
|
||||
/// Retrieves a handle to the application instance
|
||||
/// </summary>
|
||||
GWL_HINSTANCE = -6,
|
||||
|
||||
/// <summary>
|
||||
/// 改变子窗口的父窗口
|
||||
/// Retrieves a handle to the parent window, if any
|
||||
/// </summary>
|
||||
GWL_HWNDPARENT = -8,
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个新的窗口标识符
|
||||
/// Retrieves the identifier of the window
|
||||
/// </summary>
|
||||
GWL_ID = -12,
|
||||
|
||||
/// <summary>
|
||||
/// 设定一个新的窗口风格
|
||||
/// Retrieves the window styles
|
||||
/// </summary>
|
||||
GWL_STYLE = -16,
|
||||
|
||||
/// <summary>
|
||||
/// 设置与窗口有关的32位值。每个窗口均有一个由创建该窗口的应用程序使用的32位值
|
||||
/// Retrieves the user data associated with the window. This data is intended for use by the application that created the window. Its value is initially zero
|
||||
/// </summary>
|
||||
GWL_USERDATA = -21,
|
||||
|
||||
/// <summary>
|
||||
/// 为窗口设定一个新的处理函数
|
||||
/// Retrieves the address of the window procedure, or a handle representing the address of the window procedure. You must use the CallWindowProc function to call the window procedure
|
||||
/// </summary>
|
||||
GWL_WNDPROC = -4,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static System.Windows.Forms.VisualStyles.VisualStyleElement.TrayNotify;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Shell;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// 高性能透明桌面窗口
|
||||
/// </summary>
|
||||
public partial class PerformanceTransparentWin : Window
|
||||
{
|
||||
|
||||
static class BrushCreator
|
||||
{
|
||||
/// <summary>
|
||||
/// 尝试从缓存获取或创建颜色笔刷
|
||||
/// </summary>
|
||||
/// <param name="color">对应的字符串颜色</param>
|
||||
/// <returns>已经被 Freeze 的颜色笔刷</returns>
|
||||
public static SolidColorBrush GetOrCreate(string color)
|
||||
{
|
||||
if (!color.StartsWith("#"))
|
||||
{
|
||||
throw new ArgumentException($"输入的{nameof(color)}不是有效颜色,需要使用 # 开始");
|
||||
// 如果不使用 # 开始将会在 ConvertFromString 出现异常
|
||||
}
|
||||
|
||||
if (TryGetBrush(color, out var brushValue))
|
||||
{
|
||||
return (SolidColorBrush)brushValue;
|
||||
}
|
||||
|
||||
object convertColor;
|
||||
|
||||
try
|
||||
{
|
||||
convertColor = ColorConverter.ConvertFromString(color);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
// 因为在 ConvertFromString 会抛出的是 令牌无效 难以知道是为什么传入的不对
|
||||
throw new ArgumentException($"输入的{nameof(color)}不是有效颜色");
|
||||
}
|
||||
|
||||
if (convertColor == null)
|
||||
{
|
||||
throw new ArgumentException($"输入的{nameof(color)}不是有效颜色");
|
||||
}
|
||||
|
||||
var brush = new SolidColorBrush((Color)convertColor);
|
||||
if (TryFreeze(brush))
|
||||
{
|
||||
BrushCacheList.Add(color, new WeakReference<Brush>(brush));
|
||||
}
|
||||
|
||||
return brush;
|
||||
}
|
||||
|
||||
private static Dictionary<string, WeakReference<Brush>> BrushCacheList { get; } =
|
||||
new Dictionary<string, WeakReference<Brush>>();
|
||||
|
||||
private static bool TryGetBrush(string key, out Brush brush)
|
||||
{
|
||||
if (BrushCacheList.TryGetValue(key, out var brushValue))
|
||||
{
|
||||
if (brushValue.TryGetTarget(out brush))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 被回收的资源
|
||||
BrushCacheList.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
brush = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryFreeze(Freezable freezable)
|
||||
{
|
||||
if (freezable.CanFreeze)
|
||||
{
|
||||
freezable.Freeze();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建高性能透明桌面窗口
|
||||
/// </summary>
|
||||
public PerformanceTransparentWin()
|
||||
{
|
||||
WindowStyle = WindowStyle.None;
|
||||
ResizeMode = ResizeMode.NoResize;
|
||||
|
||||
Stylus.SetIsFlicksEnabled(this, false);
|
||||
Stylus.SetIsPressAndHoldEnabled(this, false);
|
||||
Stylus.SetIsTapFeedbackEnabled(this, false);
|
||||
Stylus.SetIsTouchFeedbackEnabled(this, false);
|
||||
|
||||
WindowChrome.SetWindowChrome(this,
|
||||
new WindowChrome { GlassFrameThickness = WindowChrome.GlassFrameCompleteThickness, CaptionHeight = 0, CornerRadius = new CornerRadius(0), ResizeBorderThickness = new Thickness(0)});
|
||||
|
||||
var visualTree = new FrameworkElementFactory(typeof(Border));
|
||||
visualTree.SetValue(Border.BackgroundProperty, new TemplateBindingExtension(Window.BackgroundProperty));
|
||||
var childVisualTree = new FrameworkElementFactory(typeof(ContentPresenter));
|
||||
childVisualTree.SetValue(UIElement.ClipToBoundsProperty, true);
|
||||
visualTree.AppendChild(childVisualTree);
|
||||
|
||||
Template = new ControlTemplate
|
||||
{
|
||||
TargetType = typeof(Window),
|
||||
VisualTree = visualTree,
|
||||
};
|
||||
|
||||
_dwmEnabled = DwmCompositionHelper.DwmIsCompositionEnabled();
|
||||
if (_dwmEnabled)
|
||||
{
|
||||
_hwnd = new WindowInteropHelper(this).EnsureHandle();
|
||||
Loaded += PerformanceDesktopTransparentWindow_Loaded;
|
||||
Background = Brushes.Transparent;
|
||||
}
|
||||
else
|
||||
{
|
||||
AllowsTransparency = true;
|
||||
Background = BrushCreator.GetOrCreate("#0100000");
|
||||
_hwnd = new WindowInteropHelper(this).EnsureHandle();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置点击穿透到后面透明的窗口
|
||||
/// </summary>
|
||||
public void SetTransparentHitThrough()
|
||||
{
|
||||
if (_dwmEnabled)
|
||||
{
|
||||
Win32.User32.SetWindowLongPtr(_hwnd, Win32.GetWindowLongFields.GWL_EXSTYLE,
|
||||
(IntPtr)(int)((long)Win32.User32.GetWindowLongPtr(_hwnd, Win32.GetWindowLongFields.GWL_EXSTYLE) | (long)Win32.ExtendedWindowStyles.WS_EX_TRANSPARENT));
|
||||
}
|
||||
else
|
||||
{
|
||||
Background = Brushes.Transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置点击命中,不会穿透到后面的窗口
|
||||
/// </summary>
|
||||
public void SetTransparentNotHitThrough()
|
||||
{
|
||||
if (_dwmEnabled)
|
||||
{
|
||||
Win32.User32.SetWindowLongPtr(_hwnd, Win32.GetWindowLongFields.GWL_EXSTYLE,
|
||||
(IntPtr)(int)((long)Win32.User32.GetWindowLongPtr(_hwnd, Win32.GetWindowLongFields.GWL_EXSTYLE) & ~(long)Win32.ExtendedWindowStyles.WS_EX_TRANSPARENT));
|
||||
}
|
||||
else
|
||||
{
|
||||
Background = BrushCreator.GetOrCreate("#0100000");
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct STYLESTRUCT
|
||||
{
|
||||
public int styleOld;
|
||||
public int styleNew;
|
||||
}
|
||||
|
||||
private void PerformanceDesktopTransparentWindow_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
((HwndSource)PresentationSource.FromVisual(this)).AddHook((IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) =>
|
||||
{
|
||||
//想要让窗口透明穿透鼠标和触摸等,需要同时设置 WS_EX_LAYERED 和 WS_EX_TRANSPARENT 样式,
|
||||
//确保窗口始终有 WS_EX_LAYERED 这个样式,并在开启穿透时设置 WS_EX_TRANSPARENT 样式
|
||||
//但是WPF窗口在未设置 AllowsTransparency = true 时,会自动去掉 WS_EX_LAYERED 样式(在 HwndTarget 类中),
|
||||
//如果设置了 AllowsTransparency = true 将使用WPF内置的低性能的透明实现,
|
||||
//所以这里通过 Hook 的方式,在不使用WPF内置的透明实现的情况下,强行保证这个样式存在。
|
||||
if (msg == (int)Win32.WM.STYLECHANGING && (long)wParam == (long)Win32.GetWindowLongFields.GWL_EXSTYLE)
|
||||
{
|
||||
var styleStruct = (STYLESTRUCT)Marshal.PtrToStructure(lParam, typeof(STYLESTRUCT));
|
||||
styleStruct.styleNew |= (int)Win32.ExtendedWindowStyles.WS_EX_LAYERED;
|
||||
Marshal.StructureToPtr(styleStruct, lParam, false);
|
||||
handled = true;
|
||||
}
|
||||
return IntPtr.Zero;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否开启 DWM 了,如果开启了,那么才可以使用高性能的桌面透明窗口
|
||||
/// </summary>
|
||||
private readonly bool _dwmEnabled;
|
||||
private readonly IntPtr _hwnd;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
|
||||
public class RectangleSelectionViewer : FrameworkElement
|
||||
{
|
||||
private VisualCollection _children;
|
||||
private DrawingVisual _layer = new DrawingVisual();
|
||||
private Pen defaultPen = new Pen();
|
||||
private Pen lassoPen = new Pen();
|
||||
|
||||
public RectangleSelectionViewer()
|
||||
{
|
||||
_children = new VisualCollection(this) {
|
||||
_layer // 初始化DrawingVisual
|
||||
};
|
||||
defaultPen.Thickness = 2;
|
||||
defaultPen.Brush = new SolidColorBrush(Color.FromRgb(37, 99, 235));
|
||||
defaultPen.DashStyle = DashStyles.Dash;
|
||||
|
||||
lassoPen.Thickness = 6;
|
||||
lassoPen.Brush = new SolidColorBrush(Color.FromRgb(37, 99, 235));
|
||||
lassoPen.DashStyle = new DashStyle(new double[]{0,2},0);
|
||||
lassoPen.DashCap = PenLineCap.Round;
|
||||
lassoPen.StartLineCap = PenLineCap.Round;
|
||||
lassoPen.EndLineCap = PenLineCap.Round;
|
||||
}
|
||||
|
||||
protected override int VisualChildrenCount => _children.Count;
|
||||
|
||||
protected override Visual GetVisualChild(int index)
|
||||
{
|
||||
if (index < 0 || index >= _children.Count) throw new ArgumentOutOfRangeException();
|
||||
return _children[index];
|
||||
}
|
||||
|
||||
public void DrawSelectionBox(Rect rect) {
|
||||
DrawingContext context = _layer.RenderOpen();
|
||||
context.DrawRoundedRectangle(new SolidColorBrush(Color.FromArgb(78, 96, 165, 250)), defaultPen, rect, 4, 4);
|
||||
context.Close();
|
||||
}
|
||||
|
||||
public void DrawLassoLine(PointCollection pts) {
|
||||
DrawingContext context = _layer.RenderOpen();
|
||||
if (pts.Count > 2) {
|
||||
StreamGeometry geometry = new StreamGeometry();
|
||||
var _pts = pts.Clone();
|
||||
_pts.RemoveAt(0);
|
||||
using (StreamGeometryContext ctx = geometry.Open()) {
|
||||
ctx.BeginFigure(pts[0], true , false);
|
||||
ctx.PolyLineTo(_pts,true, true);
|
||||
}
|
||||
context.DrawGeometry(new SolidColorBrush(Colors.Transparent), lassoPen, geometry);
|
||||
} else if (pts.Count == 2) {
|
||||
context.DrawLine(defaultPen, pts[0], pts[1]);
|
||||
} else if (pts.Count == 1) {
|
||||
context.DrawLine(defaultPen, pts[0], pts[0]);
|
||||
}
|
||||
context.Close();
|
||||
}
|
||||
|
||||
public void ClearDrawing() {
|
||||
DrawingContext context = _layer.RenderOpen();
|
||||
context.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
internal class SoftwareLauncher
|
||||
{
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||
|
||||
public static void LaunchEasiCamera(string softwareName)
|
||||
{
|
||||
string executablePath = FindEasiCameraExecutablePath(softwareName);
|
||||
|
||||
if (!string.IsNullOrEmpty(executablePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(executablePath);
|
||||
//Console.WriteLine(softwareName + " 启动成功!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("启动失败: " + ex.Message);
|
||||
//MessageBox.Show("启动失败: " + ex.Message);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Console.WriteLine(softwareName + " 未找到可执行文件路径。");
|
||||
}
|
||||
}
|
||||
|
||||
private static string FindEasiCameraExecutablePath(string softwareName)
|
||||
{
|
||||
string executablePath = null;
|
||||
|
||||
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall"))
|
||||
{
|
||||
foreach (string subkeyName in key.GetSubKeyNames())
|
||||
{
|
||||
using (RegistryKey subkey = key.OpenSubKey(subkeyName))
|
||||
{
|
||||
string displayName = subkey.GetValue("DisplayName") as string;
|
||||
string installLocation = subkey.GetValue("InstallLocation") as string;
|
||||
string uninstallString = subkey.GetValue("UninstallString") as string;
|
||||
|
||||
if (!string.IsNullOrEmpty(displayName) && displayName.Contains(softwareName))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(installLocation))
|
||||
{
|
||||
executablePath = System.IO.Path.Combine(installLocation, "sweclauncher.exe");
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(uninstallString))
|
||||
{
|
||||
int lastSlashIndex = uninstallString.LastIndexOf("\\");
|
||||
if (lastSlashIndex >= 0)
|
||||
{
|
||||
string folderPath = uninstallString.Substring(0, lastSlashIndex);
|
||||
executablePath = System.IO.Path.Combine(folderPath, "sweclauncher", "sweclauncher.exe");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return executablePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
public class TimeMachine
|
||||
{
|
||||
private readonly List<TimeMachineHistory> _currentStrokeHistory = new List<TimeMachineHistory>();
|
||||
|
||||
private int _currentIndex = -1;
|
||||
|
||||
public delegate void OnUndoStateChange(bool status);
|
||||
|
||||
public event OnUndoStateChange OnUndoStateChanged;
|
||||
|
||||
public delegate void OnRedoStateChange(bool status);
|
||||
|
||||
public event OnRedoStateChange OnRedoStateChanged;
|
||||
|
||||
public void CommitStrokeUserInputHistory(StrokeCollection stroke)
|
||||
{
|
||||
if (_currentIndex + 1 < _currentStrokeHistory.Count)
|
||||
{
|
||||
_currentStrokeHistory.RemoveRange(_currentIndex + 1, (_currentStrokeHistory.Count - 1) - _currentIndex);
|
||||
}
|
||||
_currentStrokeHistory.Add(new TimeMachineHistory(stroke, TimeMachineHistoryType.UserInput, false));
|
||||
_currentIndex = _currentStrokeHistory.Count - 1;
|
||||
NotifyUndoRedoState();
|
||||
}
|
||||
|
||||
public void CommitStrokeShapeHistory(StrokeCollection strokeToBeReplaced, StrokeCollection generatedStroke)
|
||||
{
|
||||
if (_currentIndex + 1 < _currentStrokeHistory.Count)
|
||||
{
|
||||
_currentStrokeHistory.RemoveRange(_currentIndex + 1, (_currentStrokeHistory.Count - 1) - _currentIndex);
|
||||
}
|
||||
_currentStrokeHistory.Add(new TimeMachineHistory(generatedStroke,
|
||||
TimeMachineHistoryType.ShapeRecognition,
|
||||
false,
|
||||
strokeToBeReplaced));
|
||||
_currentIndex = _currentStrokeHistory.Count - 1;
|
||||
NotifyUndoRedoState();
|
||||
}
|
||||
|
||||
public void CommitStrokeManipulationHistory(Dictionary<Stroke, Tuple<StylusPointCollection, StylusPointCollection>> stylusPointDictionary)
|
||||
{
|
||||
if (_currentIndex + 1 < _currentStrokeHistory.Count)
|
||||
{
|
||||
_currentStrokeHistory.RemoveRange(_currentIndex + 1, (_currentStrokeHistory.Count - 1) - _currentIndex);
|
||||
}
|
||||
_currentStrokeHistory.Add(
|
||||
new TimeMachineHistory(stylusPointDictionary,
|
||||
TimeMachineHistoryType.Manipulation));
|
||||
_currentIndex = _currentStrokeHistory.Count - 1;
|
||||
NotifyUndoRedoState();
|
||||
}
|
||||
public void CommitStrokeDrawingAttributesHistory(Dictionary<Stroke, Tuple<DrawingAttributes, DrawingAttributes>> drawingAttributes)
|
||||
{
|
||||
if (_currentIndex + 1 < _currentStrokeHistory.Count)
|
||||
{
|
||||
_currentStrokeHistory.RemoveRange(_currentIndex + 1, (_currentStrokeHistory.Count - 1) - _currentIndex);
|
||||
}
|
||||
_currentStrokeHistory.Add(
|
||||
new TimeMachineHistory(drawingAttributes,
|
||||
TimeMachineHistoryType.DrawingAttributes));
|
||||
_currentIndex = _currentStrokeHistory.Count - 1;
|
||||
NotifyUndoRedoState();
|
||||
}
|
||||
|
||||
public void CommitStrokeEraseHistory(StrokeCollection stroke, StrokeCollection sourceStroke = null)
|
||||
{
|
||||
if (_currentIndex + 1 < _currentStrokeHistory.Count)
|
||||
{
|
||||
_currentStrokeHistory.RemoveRange(_currentIndex + 1, (_currentStrokeHistory.Count - 1) - _currentIndex);
|
||||
}
|
||||
_currentStrokeHistory.Add(new TimeMachineHistory(stroke, TimeMachineHistoryType.Clear, true, sourceStroke));
|
||||
_currentIndex = _currentStrokeHistory.Count - 1;
|
||||
NotifyUndoRedoState();
|
||||
}
|
||||
|
||||
public void ClearStrokeHistory()
|
||||
{
|
||||
_currentStrokeHistory.Clear();
|
||||
_currentIndex = -1;
|
||||
NotifyUndoRedoState();
|
||||
}
|
||||
|
||||
public TimeMachineHistory Undo()
|
||||
{
|
||||
var item = _currentStrokeHistory[_currentIndex];
|
||||
item.StrokeHasBeenCleared = !item.StrokeHasBeenCleared;
|
||||
_currentIndex--;
|
||||
OnUndoStateChanged?.Invoke(_currentIndex > -1);
|
||||
OnRedoStateChanged?.Invoke(_currentStrokeHistory.Count - _currentIndex - 1 > 0);
|
||||
return item;
|
||||
}
|
||||
|
||||
public TimeMachineHistory Redo()
|
||||
{
|
||||
var item = _currentStrokeHistory[++_currentIndex];
|
||||
item.StrokeHasBeenCleared = !item.StrokeHasBeenCleared;
|
||||
NotifyUndoRedoState();
|
||||
return item;
|
||||
}
|
||||
|
||||
public TimeMachineHistory[] ExportTimeMachineHistory()
|
||||
{
|
||||
if (_currentIndex + 1 < _currentStrokeHistory.Count)
|
||||
{
|
||||
_currentStrokeHistory.RemoveRange(_currentIndex + 1, (_currentStrokeHistory.Count - 1) - _currentIndex);
|
||||
}
|
||||
return _currentStrokeHistory.ToArray();
|
||||
}
|
||||
|
||||
public bool ImportTimeMachineHistory(TimeMachineHistory[] sourceHistory)
|
||||
{
|
||||
_currentStrokeHistory.Clear();
|
||||
_currentStrokeHistory.AddRange(sourceHistory);
|
||||
_currentIndex = _currentStrokeHistory.Count - 1;
|
||||
NotifyUndoRedoState();
|
||||
return true;
|
||||
}
|
||||
private void NotifyUndoRedoState()
|
||||
{
|
||||
OnUndoStateChanged?.Invoke(_currentIndex > -1);
|
||||
OnRedoStateChanged?.Invoke(_currentStrokeHistory.Count - _currentIndex - 1 > 0);
|
||||
}
|
||||
}
|
||||
|
||||
public class TimeMachineHistory
|
||||
{
|
||||
public TimeMachineHistoryType CommitType;
|
||||
public bool StrokeHasBeenCleared = false;
|
||||
public StrokeCollection CurrentStroke;
|
||||
public StrokeCollection ReplacedStroke;
|
||||
//这里说一下 Tuple的 Value1 是初始值 ; Value 2 是改变值
|
||||
public Dictionary<Stroke, Tuple<StylusPointCollection, StylusPointCollection>> StylusPointDictionary;
|
||||
public Dictionary<Stroke, Tuple<DrawingAttributes, DrawingAttributes>> DrawingAttributes;
|
||||
public TimeMachineHistory(StrokeCollection currentStroke, TimeMachineHistoryType commitType, bool strokeHasBeenCleared)
|
||||
{
|
||||
CommitType = commitType;
|
||||
CurrentStroke = currentStroke;
|
||||
StrokeHasBeenCleared = strokeHasBeenCleared;
|
||||
ReplacedStroke = null;
|
||||
}
|
||||
public TimeMachineHistory(Dictionary<Stroke, Tuple<StylusPointCollection, StylusPointCollection>> stylusPointDictionary, TimeMachineHistoryType commitType)
|
||||
{
|
||||
CommitType = commitType;
|
||||
StylusPointDictionary = stylusPointDictionary;
|
||||
}
|
||||
public TimeMachineHistory(Dictionary<Stroke, Tuple<DrawingAttributes, DrawingAttributes>> drawingAttributes, TimeMachineHistoryType commitType)
|
||||
{
|
||||
CommitType = commitType;
|
||||
DrawingAttributes = drawingAttributes;
|
||||
}
|
||||
public TimeMachineHistory(StrokeCollection currentStroke, TimeMachineHistoryType commitType, bool strokeHasBeenCleared, StrokeCollection replacedStroke)
|
||||
{
|
||||
CommitType = commitType;
|
||||
CurrentStroke = currentStroke;
|
||||
StrokeHasBeenCleared = strokeHasBeenCleared;
|
||||
ReplacedStroke = replacedStroke;
|
||||
}
|
||||
}
|
||||
|
||||
public enum TimeMachineHistoryType
|
||||
{
|
||||
UserInput,
|
||||
ShapeRecognition,
|
||||
Clear,
|
||||
Manipulation,
|
||||
DrawingAttributes
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using System.Windows.Automation;
|
||||
|
||||
namespace Ink_Canvas.Helpers {
|
||||
internal class WinTabWindowsChecker {
|
||||
/*
|
||||
public static bool IsWindowMinimized(string windowName, bool matchFullName = true) {
|
||||
// 获取Win+Tab预览中的窗口
|
||||
AutomationElementCollection windows = AutomationElement.RootElement.FindAll(
|
||||
TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window));
|
||||
|
||||
foreach (AutomationElement window in windows) {
|
||||
//LogHelper.WriteLogToFile("" + window.Current.Name);
|
||||
|
||||
string windowTitle = window.Current.Name;
|
||||
|
||||
// 如果窗口标题包含 windowName,则进行检查
|
||||
if (!string.IsNullOrEmpty(windowTitle) && windowTitle.Contains(windowName)) {
|
||||
if (matchFullName) {
|
||||
if (windowTitle.Length == windowName.Length) {
|
||||
// 检查窗口是否最小化
|
||||
WindowPattern windowPattern = window.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
|
||||
if (windowPattern != null) {
|
||||
bool isMinimized = windowPattern.Current.WindowVisualState == WindowVisualState.Minimized;
|
||||
//LogHelper.WriteLogToFile("" + windowTitle + isMinimized);
|
||||
return isMinimized;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 检查窗口是否最小化
|
||||
WindowPattern windowPattern = window.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
|
||||
if (windowPattern != null) {
|
||||
bool isMinimized = windowPattern.Current.WindowVisualState == WindowVisualState.Minimized;
|
||||
return isMinimized;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 未找到软件白板窗口
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
public static bool IsWindowExisted(string windowName, bool matchFullName = true) {
|
||||
// 获取Win+Tab预览中的窗口
|
||||
AutomationElementCollection windows = AutomationElement.RootElement.FindAll(
|
||||
TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window));
|
||||
|
||||
foreach (AutomationElement window in windows) {
|
||||
//LogHelper.WriteLogToFile("" + window.Current.Name);
|
||||
|
||||
string windowTitle = window.Current.Name;
|
||||
|
||||
// 如果窗口标题包含 windowName,则进行检查
|
||||
if (!string.IsNullOrEmpty(windowTitle) && windowTitle.Contains(windowName)) {
|
||||
if (matchFullName) {
|
||||
if (windowTitle.Length == windowName.Length) {
|
||||
WindowPattern windowPattern = window.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
|
||||
if (windowPattern != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
WindowPattern windowPattern = window.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
|
||||
if (windowPattern != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user