WPF 必知技巧
光标
自定义光标
1 2 | StreamResourceInfo sri = Application.GetResourceStream(new Uri(@”cur\erase.cur”, UriKind.Relative)); m_canvas.Cursor = new Cursor(sri.Stream); |
其中cur\erase.cur
位于项目根目录下
使用系统光标
1 | m_canvas.Cursor = Cursors.Arrow; |
Canvas
在做黑板的时候我们需要显示一个橡皮擦,它位于Canvas的最顶层
1 | Canvas.SetZIndex(m_erase_img, int.MaxValue); |
获取显示器的缩放倍数
我们在开发截屏的功能时如果设置了缩放与布局
为200%,显示分辨率
为2560×1600,
我们通过代码SystemParameters.PrimaryScreenWidth
获取的屏幕宽度就是1280,
如果截图截取1280的话,截出的图片就宽高都只有一半,
所以我们就必须获取系统缩放的倍数
1 2 | //100%的时候,DPI是96;这条语句的作用时获取缩放倍数 float factor = Graphics.FromHwnd(IntPtr.Zero).DpiX / 96; |
Bitmap/BitmapImage/BitmapSource
BitmapSource是Imagesource的子类
WPF的Image控件中设置ImageSource
image1.Source = new BitmapImage(new Uri(@"image file path", Urikind.RelativeOrAbsolute));
还可以使用:
System.IO.FileStream fs = new System.IO.FileStream(filepath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
byte[] buffer = new byte[fs.Length]; fs.Read(buffer, 0, buffer.Length);
fs.Close(); fs.Dispose();
System.IO.MemoryStream ms = new System.IO.MemoryStream(buffer);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = ms;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad; bitmapImage.EndInit();
ms.Dispose();
image1.Source = bitmapImage;
还可以使用:
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.UriSource = new Uri(szPath);//szPath为图片的全路径
bitmapImage.EndInit();
bitmapImage.Freeze();
image1.Source = bitmapImage;
Bitmap => BitmapImage
先将Bitmap储存成memorystream,然后指定给BitmapImage
private BitmapImage BitmapToBitmapImage(System.Drawing.Bitmap bitmap)
{
BitmapImage bitmapImage = new BitmapImage();
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
bitmap.Save(ms, bitmap.RawFormat);
bitmapImage.BeginInit();
bitmapImage.StreamSource = ms;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
}
return bitmapImage;
}
image1.Source = BitmapToBitmapImage(bitmap);
Bitmap => BitmapSource
BitmapSource bs = Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
BitmapSource => Bitmap
BitmapSource m = (BitmapSource)image1.Source;
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(m.PixelWidth, m.PixelHeight, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
System.Drawing.Imaging.BitmapData data = bmp.LockBits(
new System.Drawing.Rectangle(System.Drawing.Point.Empty, bmp.Size), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
m.CopyPixels(Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride); bmp.UnlockBits(data);
无边框与可拖动
去除边框
WPF的默认样式是有边框的,为了去除窗体的边框,可以设置Window的以下属性
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
窗口初始化位置
一旦没了边框之后 默认情况下是无法进行拖拽的 因此初始化位置就比较重要了
WPF的窗体初始化位置属性WindowStartupLocation
分为
- Manual(默认值)
- CenterScreen
- CenterOwner 三种,
默认是Manual 因此要想设置到屏幕中央 使用CenterScreen即可
如果要自定义设置位置 使用Manual后再设置Left和Top属性即可
窗口置顶
1 | this.Topmost = true; |
窗体拖拽
无边框情况下默认是无法拖拽的,如果需要拖拽则为Window的MouseLeftButtonDown绑定事件,并调用默认DragMove方法即可。
XAML:
1 | MouseLeftButtonDown=”Window_MouseLeftButtonDown_1″ |
C#:
1 2 3 4 | private void Window_MouseLeftButtonDown_1(object sender, MouseButtonEventArgs e) { this.DragMove(); } |
窗口全屏
this.WindowState = System.Windows.WindowState.Normal;
this.WindowStyle = System.Windows.WindowStyle.None;
this.ResizeMode = System.Windows.ResizeMode.NoResize;
this.Topmost = true;
this.Left = 0.0;
this.Top = 0.0;
this.Width = System.Windows.SystemParameters.PrimaryScreenWidth;
this.Height = System.Windows.SystemParameters.PrimaryScreenHeight;
线程切换
this.Dispatcher.Invoke(() =>
{
});
官方说,WPF一般来说启动后会有两个线程,一个是责呈现,一个负责UI界面管理。
负责UI界面管理的线程,我们就简称为UI线程。UI线程内有个Dispatcher对象。
Dispatcher对象内则包含这个UI线程的众多工作内容(官方叫work item)的队列。UI线程就是靠Dispatcher负责控件相关的这些事件的处理。
只有创建了UI控件的UI Thread才有权限控制控件的访问和更新!!!
其他线程(非直接创建你要访问和控制UI控件的线程)要访问和更新某个控件,必须通过创建这个控件的线程(一般就是UI线程)所关联的Dispatcher来访问和更新这个控件。这也是为什么经常会有this.Dispatcher.Invoke()的原因
同一个类下的方法根据你调用的方式不同,并不一定都运行于同一个线程下。即使调用其他类的函数,也可能存在两种情况,要么运行在一个线程里,要么运行在不同的线程里。实际上是否是一个线程里完全跟如何调度相关,跟是否属于哪个类没有任何关系。
循环生成组件传值
<Button Click="BookItem_Click" Tag="{Binding}">
</Button>
C#
private void BookItem_Click(object sender, RoutedEventArgs e)
{
var itemData = ((FrameworkElement)sender).Tag;
if (itemData is BookItem)
{
var itemData2 = (BookItem)itemData;
}
}
获取组件
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
namespace ZJClassTool.Utils
{
internal class ZJVTHelper
{
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
if (parent == null) return null;
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// 如果子控件不是需查找的控件类型
T childType = child as T;
if (childType == null)
{
// 在下一级控件中递归查找
foundChild = FindChild<T>(child, childName);
// 找到控件就可以中断递归操作
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// 如果控件名称符合参数条件
if (frameworkElement != null && frameworkElement.Name == childName)
{
foundChild = (T)child;
break;
}
}
else
{
// 查找到了控件
foundChild = (T)child;
break;
}
}
return foundChild;
}
public static List<T> FindChilds<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
var list = new List<T>();
if (parent == null) return list;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// 如果子控件不是需查找的控件类型
T childType = child as T;
if (childType == null)
{
// 在下一级控件中递归查找
var findChildList = FindChilds<T>(child, childName);
for (int j = 0; j < findChildList.Count; j++)
{
}
list.AddRange(FindChilds<T>(child, childName));
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// 如果控件名称符合参数条件
if (frameworkElement != null && frameworkElement.Name == childName)
{
list.Add((T)child);
}
}
else
{
// 查找到了控件
list.Add((T)child);
}
}
return list;
}
/// <summary>
/// 查找父元素
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <param name="name"></param>
/// <returns></returns>
public static T FindParent<T>(DependencyObject i_dp) where T : DependencyObject
{
DependencyObject dobj = VisualTreeHelper.GetParent(i_dp);
if (dobj != null)
{
if (dobj is T)
{
return (T)dobj;
}
else
{
dobj = FindParent<T>(dobj);
if (dobj != null && dobj is T)
{
return (T)dobj;
}
}
}
return null;
}
}
}
程序关闭
推荐使用
Application.Current.Shutdown();
下面的方式系统监听不到退出事件
System.Environment.Exit(0);
打开文件
ProcessStartInfo psi = new ProcessStartInfo(filepath);
Process pro = new Process();
pro.StartInfo = psi;
pro.Start();
程序异常捕获 不崩溃退出
internal class MyApp : Application
{
[STAThread]
private static void Main()
{
// 定义Application对象作为整个应用程序入口
Application app = new MyApp();
LoginWindow win = new LoginWindow();
app.Run(win);
}
public MyApp()
{
DispatcherUnhandledException += MyApp_DispatcherUnhandledException;
}
private void MyApp_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
MessageBox.Show("程序异常." + Environment.NewLine + e.Exception.Message);
e.Handled = true;
}
}
原文链接:https://www.psvmc.cn/article/2020-01-03-wpf-start-05-tips.html