WPF 必知技巧

光标

自定义光标

1
2
StreamResourceInfo sri = Application.GetResourceStream(new Uri(@”cur\erase.cur”, UriKind.Relative));
m_canvas.Cursor = new Cursor(sri.Stream);

其中cur\erase.cur位于项目根目录下

使用系统光标

1m_canvas.Cursor = Cursors.Arrow;

Canvas

在做黑板的时候我们需要显示一个橡皮擦,它位于Canvas的最顶层

1Canvas.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属性即可

窗口置顶

1this.Topmost = true;

窗体拖拽

无边框情况下默认是无法拖拽的,如果需要拖拽则为Window的MouseLeftButtonDown绑定事件,并调用默认DragMove方法即可。

XAML:

1MouseLeftButtonDown=”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;
}
}
技术交流Q群: 1012481075 群内有各种流行书籍资料
文章后续会在公众号更新,微信搜索 OneByOneDotNet 即可关注。
你的一分鼓励,我的十分动力,点赞免费,感恩回馈。喜欢就点赞评论吧,双击6666~
本网站(网站地址)刊载的所有内容,包括文字、图片、音频、视频、软件、程序、以及网页版式设计等均在网上搜集。
访问者可将本网站提供的内容或服务用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯本网站及相关权利人的合法权利。
除此以外,将本网站任何内容或服务用于其他用途时,须征得本网站及相关权利人的书面许可,并支付报酬.
本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站,予以删除.
转载请注明出处
作者昵称:OneByOneDotNet
作者链接:https://onebyone.icu/user/1
网站标题:OneByOne DotNet
网站地址:https://onebyone.icu
文章标题:WPF 必知技巧
文章链接:https://onebyone.icu/archives/2149
原文出处:https://www.psvmc.cn/article/2020-01-03-wpf-start-05-tips.html
原文链接:https://www.psvmc.cn/article/2020-01-03-wpf-start-05-tips.html
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

发表评论

邮箱地址不会被公开。 必填项已用*标注

WeChat
WeChat
QQ
QQ
返回顶部