更改语言版本
运行时报错,我们需要修改C#语言版本
“Using 声明”在 C# 7.3 中不可用。请使用 8.0 或更高的语言版本。
正常来说我们可以按照下述流程操作
属性
=>生成
=>高级
=>语言版本
但是有时我们无法选择其他版本,这时候就要手动修改
手动修改
打开项目下的*.csproj
文件
搜索<LangVersion>
修改对应的值即可。
宽高
设备无关单位尺寸
首先我们先了解一下,什么是设备无关单位尺寸,什么是像素。
DPI转换比例常量,DpiPercent = 96;
为何DpiPercent为96 ?有一个概念设备无关单位尺寸
,其大小为1/96英寸。比如:
【物理单位尺寸】=1/96英寸 * 96dpi = 1像素;
【物理单位尺寸】=1/96英寸 * 120dpi = 1.25像素;
我们组件在赋值宽高的时候就是设备无关单位尺寸
。
而如果系统调成125%
的缩放时,对应的的就是120dpi。
前提
在分辨率为1920x1080,缩放为125%的情况下
主屏的宽高
获取宽高
1 | // 方式1 |
结果
PrimaryScreen.Bounds.Width:1920
PrimaryScreen.Bounds.Height:1080
PrimaryScreenWidth:1536
PrimaryScreenHeight:864
WorkArea.Size.Width:1536
WorkArea.Size.Height:824
FullPrimaryScreenWidth:1536
FullPrimaryScreenHeight:800.8
注意
方式2和方式3的高度差就是任务栏的高度(设备无关单位尺寸)
方式4官方说的是
获取主监视器上全屏窗口工作区的高度(以像素为单位)
,但是跟实际不符,不知道到底有什么用。
主屏的缩放倍数
我们在开发截屏的功能时如果设置了缩放与布局
为200%,显示分辨率
为2560x1600,
我们通过代码SystemParameters.PrimaryScreenWidth
获取的屏幕宽度就是1280,
如果截图截取1280的话,截出的图片就宽高都只有一半,
所以我们就必须获取系统缩放的倍数
1 | //100%的时候,DPI是96;这条语句的作用时获取缩放倍数 |
组件所在屏幕的宽高
这里组件所在窗口依旧是主屏
1 | Window window = Window.GetWindow(myimage);//获取当前窗口 |
结果
screen.Bounds.Width:1920
screen.Bounds.Height:1080
screen.WorkingArea.Width:1920
screen.WorkingArea.Height:1030
我们会发现
这种方式获取的是实际的像素,结果正好是上面方式2和方式3的1.25倍。
组件所在屏幕的缩放倍数
转化为设备无关单位尺寸
1 | using (Graphics currentGraphics = Graphics.FromHwnd(intPtr)) |
光标
自定义光标
1 | StreamResourceInfo sri = Application.GetResourceStream(new Uri(@"cur\erase.cur", UriKind.Relative)); |
其中cur\erase.cur
位于项目根目录下
使用系统光标
1 | m_canvas.Cursor = Cursors.Arrow; |
Canvas设置层级
在做黑板的时候我们需要显示一个橡皮擦,它位于Canvas的最顶层
1 | Canvas.SetZIndex(m_erase_img, int.MaxValue); |
Bitmap/BitmapImage/BitmapSource
BitmapSource是Imagesource的子类
WPF的Image控件中设置ImageSource
1 | image1.Source = new BitmapImage(new Uri(@"image file path", Urikind.RelativeOrAbsolute)); |
还可以使用:
1 | System.IO.FileStream fs = new System.IO.FileStream(filepath, System.IO.FileMode.Open, System.IO.FileAccess.Read); |
还可以使用:
1 | BitmapImage bitmapImage = new BitmapImage(); |
Bitmap => BitmapImage
先将Bitmap储存成memorystream,然后指定给BitmapImage
1 | private BitmapImage BitmapToBitmapImage(System.Drawing.Bitmap bitmap) |
Bitmap => BitmapSource
1 | BitmapSource bs = Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); |
BitmapSource => Bitmap
1 | BitmapSource m = (BitmapSource)image1.Source; |
线程切换
1 | this.Dispatcher.Invoke(() => |
官方说,WPF一般来说启动后会有两个线程,一个是责呈现,一个负责UI界面管理。
负责UI界面管理的线程,我们就简称为UI线程。UI线程内有个Dispatcher对象。
Dispatcher对象内则包含这个UI线程的众多工作内容(官方叫work item)的队列。UI线程就是靠Dispatcher负责控件相关的这些事件的处理。
只有创建了UI控件的UI Thread才有权限控制控件的访问和更新!!!
其他线程(非直接创建你要访问和控制UI控件的线程)要访问和更新某个控件,必须通过创建这个控件的线程(一般就是UI线程)所关联的Dispatcher来访问和更新这个控件。这也是为什么经常会有this.Dispatcher.Invoke()的原因
同一个类下的方法根据你调用的方式不同,并不一定都运行于同一个线程下。即使调用其他类的函数,也可能存在两种情况,要么运行在一个线程里,要么运行在不同的线程里。实际上是否是一个线程里完全跟如何调度相关,跟是否属于哪个类没有任何关系。
循环生成组件传值
1 | <Button Click="BookItem_Click" Tag="{Binding}"> |
C#
1 | private void BookItem_Click(object sender, RoutedEventArgs e) |
获取组件
1 | using System.Collections.Generic; |
程序关闭
推荐使用
1 | Application.Current.Shutdown(); |
下面的方式系统监听不到退出事件
1 | System.Environment.Exit(0); |
打开文件
1 | ProcessStartInfo psi = new ProcessStartInfo(filepath); |