22 KiB
重要说明
InkCanvas
InkCanvasEditingMode
枚举
EditingMode
属性有以下几种值,每个值对应不同的功能和行为:
-
Ink
- 这是默认值,允许用户绘制笔迹(手写)到画布上。
- 用户可以使用手写笔或触摸屏在
InkCanvas
上绘制。 - 适用于需要自由绘画的情况。
- 举例:用户在画布上用手写笔画出图形或写字。
-
EraseByPoint
- 允许用户通过触摸或手写笔直接擦除指定位置的笔迹。
- 在擦除模式下,用户可以通过点击或在某个区域上划过来擦除画布上的笔迹。
- 举例:点击画布上的笔迹来删除它。
-
EraseByStroke
- 允许用户通过选择并删除特定的笔迹(线条)来擦除。
- 在此模式下,用户通过拖动手写笔(或鼠标)来选中一条笔迹,然后删除它。
- 举例:用户可以通过点击或拖动鼠标擦除整条笔迹。
-
Select
-
允许用户选择画布上的一条或多条笔迹。
-
用户可以通过绘制一个框或点击已有的笔迹来选择它们。这些选中的笔迹可以进行后续的操作,比如移动、修改等。
-
举例:用户选择一条已绘制的线条进行编辑或移动。
None
-
不允许任何编辑操作。即画布处于只读模式,用户无法绘制、擦除或选择任何内容。
-
举例:画布只是一个静态展示,不能进行任何手写或操作。各种模式的实际应用场景:
-
-
Ink
模式- 用于自由绘图应用,例如绘画软件,用户可以随意画出线条、形状,甚至手写文字。
-
EraseByPoint
和EraseByStroke
模式- 用于擦除功能,在用户绘制之后需要修改或删除内容时,提供擦除笔迹的功能。
-
Select
模式- 在用户完成绘制后,可以用来选择、移动或修改笔迹。它适合在用户绘制完后需要对某些元素进行调整的情况,比如在草图应用中编辑单个图形。
-
None
模式- 用于展示或演示,画布上不允许任何编辑操作。常用于只读视图,例如用于展示静态图片或其他内容,用户不能进行任何绘画或修改。
WPF中的事件
在 WPF 中,事件是用户交互的一个重要部分,WPF 的事件机制包括常见的用户界面交互事件(如按钮点击、鼠标点击、键盘输入等),以及比普通事件更复杂的“隧道”和“冒泡”事件。下面我会详细说明如何在 XAML 中使用事件(如 Click
和 MouseUp
事件),并介绍如何通过委托创建事件。同时,我还会讲解事件的“隧道”和“冒泡”机制,并通过代码示例进行说明。
一、WPF 中的事件:Click 和 MouseUp 事件
1. Click 事件
Click
事件通常与按钮控件等交互元素关联。当用户点击按钮时,Click
事件被触发。
XAML 示例:
<Button Content="Click Me" Click="Button_Click" />
C# 代码示例:
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Button was clicked!");
}
在这个例子中,Button_Click
方法会在按钮被点击时被触发。
2. MouseUp 事件
MouseUp
事件会在用户释放鼠标按钮时触发。你可以使用此事件来响应鼠标按钮的释放操作。
XAML 示例:
<Button Content="Mouse Up Test" MouseUp="Button_MouseUp" />
C# 代码示例:
private void Button_MouseUp(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("Mouse button released!");
}
MouseUp
事件可以用于检测鼠标何时被释放,通常用于拖放操作、绘图操作等。
二、使用委托创建事件
WPF 中的事件通常是基于委托的。委托在 C# 中是一种类型安全的函数指针,可以用来引用方法。
1. 委托的创建和事件的声明
首先定义一个委托类型,然后使用该委托创建事件。
C# 示例:
// 1. 定义委托类型
public delegate void MyCustomEventHandler(object sender, EventArgs e);
// 2. 创建事件
public event MyCustomEventHandler MyCustomEvent;
private void RaiseCustomEvent()
{
// 3. 触发事件
MyCustomEvent?.Invoke(this, EventArgs.Empty);
}
2. 订阅事件
创建事件之后,可以使用 +=
操作符订阅事件,在事件触发时执行相应的方法。
C# 示例:
public MainWindow()
{
InitializeComponent();
// 订阅事件
MyCustomEvent += OnCustomEventTriggered;
}
private void OnCustomEventTriggered(object sender, EventArgs e)
{
MessageBox.Show("Custom event triggered!");
}
private void TriggerButton_Click(object sender, RoutedEventArgs e)
{
// 触发自定义事件
RaiseCustomEvent();
}
在这个例子中,当点击一个按钮时,会触发 RaiseCustomEvent
方法,并调用 OnCustomEventTriggered
方法。
三、隧道时间和冒泡事件
WPF 支持 事件路由,即事件传播的方式。事件有两种传播模式:冒泡(Bubble) 和 隧道(Tunnel)。
1. 冒泡事件(Bubble Events)
冒泡事件是指事件从事件源开始,逐级向上传播到父元素。比如,点击一个按钮时,Click
事件会先在按钮上触发,然后向上传播到父级容器、窗口等。
代码示例:
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Event Bubbling" Height="350" Width="525">
<Grid Background="LightGray" MouseDown="Grid_MouseDown">
<Button Content="Click Me" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Window>
C# 示例:
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("Mouse Down event bubbled up to Grid");
}
在上面的例子中,Grid
控件订阅了 MouseDown
事件,而当按钮被点击时,事件会从按钮开始冒泡,最终触发 Grid_MouseDown
方法。
2. 隧道事件(Tunnel Events)
隧道事件是事件传播的反向过程,事件从根元素(如窗口)开始,逐级向下传播到事件源。隧道事件通常以 Preview
开头(如 PreviewMouseDown
)。
代码示例:
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Event Tunneling" Height="350" Width="525">
<Grid Background="LightGray" PreviewMouseDown="Grid_PreviewMouseDown">
<Button Content="Click Me" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Window>
C# 示例:
private void Grid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("PreviewMouseDown event tunneled from root element");
}
在这个例子中,PreviewMouseDown
是一个隧道事件,它会从 Window
开始向下传播。如果点击按钮,PreviewMouseDown
事件会首先触发,然后传播到 Grid
,接着传播到按钮。
四、冒泡与隧道的区别:
- 冒泡事件:从事件源开始,向上传播到父元素,直到最顶层的容器(比如窗口)。
- 常见的冒泡事件:
Click
,MouseUp
,MouseDown
。 - 事件传播的顺序:事件源 → 子元素 → 父元素。
- 常见的冒泡事件:
委托和事件:你可以通过自定义委托来创建自定义事件,并通过
+=
订阅事件。
- 隧道事件:从最顶层的容器(如窗口)开始,向下传播到事件源。
- 常见的隧道事件:
PreviewMouseDown
,PreviewKeyDown
。 - 事件传播的顺序:父元素 → 子元素 → 事件源。
- 常见的隧道事件:
冒泡事件和隧道事件:WPF 中的事件支持路由机制,事件有两种传播方式:冒泡(从源元素向父元素)和隧道(从父元素向源元素)。
5、阻止事件
在 WPF 中,冒泡事件和隧道事件都可以通过两种主要方式进行拦截或阻止:Handled
属性和**e.Handled = true
**。这两种方法可以阻止事件继续向上传播或向下传播,分别适用于冒泡事件和隧道事件。
一、阻止冒泡事件
冒泡事件是从事件源(如按钮)开始,向父元素或祖先元素传播。你可以通过设置事件的 Handled
属性为 true
来阻止冒泡事件的进一步传播。
阻止冒泡事件的传播
通过设置 e.Handled = true
,可以停止冒泡事件继续传播到父级元素。
假设有一个 Button
被嵌套在一个 Grid
中,你希望在点击按钮时,事件不再继续向上传播到 Grid
。
XAML 示例:
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Event Bubbling" Height="350" Width="525">
<Grid Background="LightGray" MouseDown="Grid_MouseDown">
<Button Content="Click Me" HorizontalAlignment="Center" VerticalAlignment="Center" MouseDown="Button_MouseDown"/>
</Grid>
</Window>
C# 示例:
// Grid 上的 MouseDown 事件
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("MouseDown event bubbled up to Grid");
}
// Button 上的 MouseDown 事件
private void Button_MouseDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("MouseDown event triggered on Button");
// 阻止事件继续冒泡到 Grid
e.Handled = true;
}
在这个例子中,当按钮被点击时,Button_MouseDown
事件被触发,然后设置 e.Handled = true
,阻止了事件继续冒泡到 Grid
。因此,Grid_MouseDown
事件不会被触发。
为什么使用 e.Handled = true
?
Handled
属性标记事件是否已被处理。如果你设置 e.Handled = true
,就意味着事件已经被处理,事件将不会继续传递到父元素,从而防止了冒泡事件继续传播。
二、阻止隧道事件
隧道事件是从根元素开始,向下传播到事件源。要阻止隧道事件的传播,同样需要设置 Handled
属性为 true
。
阻止隧道事件的传播
通过设置 e.Handled = true
来阻止隧道事件继续传播到子元素。
假设有一个 Grid
和 Button
,你希望阻止 PreviewMouseDown
(隧道事件)继续向下传播。
XAML 示例:
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Event Tunneling" Height="350" Width="525">
<Grid Background="LightGray" PreviewMouseDown="Grid_PreviewMouseDown">
<Button Content="Click Me" HorizontalAlignment="Center" VerticalAlignment="Center" PreviewMouseDown="Button_PreviewMouseDown"/>
</Grid>
</Window>
C# 示例:
// Grid 上的 PreviewMouseDown 事件(隧道事件)
private void Grid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("PreviewMouseDown event tunneled down to Grid");
}
// Button 上的 PreviewMouseDown 事件(隧道事件)
private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("PreviewMouseDown event tunneled to Button");
// 阻止事件继续传播到 Grid
e.Handled = true;
}
在这个例子中,Button_PreviewMouseDown
事件会首先触发。设置 e.Handled = true
后,事件就不会继续传递给 Grid_PreviewMouseDown
,即阻止了隧道事件的传播。
- 冒泡事件的阻止:通过设置
e.Handled = true
来阻止事件向上传播(即冒泡到父元素)。- 隧道事件的阻止:通过设置
e.Handled = true
来阻止事件向下传播(即阻止传递给子元素)。为什么要阻止事件传播?
阻止事件传播可以帮助你控制事件的流动,避免多个元素同时响应同一事件。例如,点击按钮时,可能既希望按钮处理该事件,也不希望父容器(如
Grid
)对该事件做出响应。在这种情况下,你可以通过设置Handled = true
来确保事件不再被父级元素处理。其他相关方法
Preview
事件和普通事件:Preview
事件是隧道事件,在事件传播到目标控件之前触发;普通事件是冒泡事件,在目标控件后开始传播。e.Handled
的局限性:e.Handled = true
只在事件的传播链中起作用,通常是在局部处理事件时使用。如果你需要阻止某个控件的交互行为(比如禁止按钮点击),你可能还需要结合其他逻辑来实现。
所有元素生命周期
WPF 中的生命周期通常指的是从应用程序启动到关闭的整个过程中,各个控件和窗口的创建、初始化、显示、交互、销毁等阶段。WPF 的生命周期可以分为 应用程序生命周期 和 窗口生命周期,并且每个控件、窗口和元素都有相应的生命周期事件。
WPF 应用程序的生命周期是从启动应用程序到关闭应用程序的全过程。这个生命周期涉及到应用程序的启动、运行和退出,通常是由 App.xaml
文件中的代码来管理。
1. 应用程序启动
应用程序的启动是 WPF 生命周期的第一步。App.xaml
中定义了应用程序的启动入口。应用程序通常通过 App.xaml.cs
文件中的 OnStartup
方法启动。
App.xaml
:定义了应用程序的资源、样式、启动窗口等。App.xaml.cs
:定义了应用程序的启动逻辑,通常重写OnStartup
方法来处理启动相关的逻辑。
示例:
<Application x:Class="WpfApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
</Application>
App.xaml.cs
:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e); // 调用基类的 OnStartup 方法
// 启动时的其他逻辑
MessageBox.Show("Application Started");
}
}
2. 应用程序初始化
WPF 应用程序启动后,首先会初始化资源字典、应用程序的 UI 树、数据绑定、主题等。在这个阶段,应用程序会加载 App.xaml
中定义的资源(如样式和主题),并为界面控件分配空间。
3. 应用程序运行
应用程序运行时,MainWindow
等窗口会被创建和显示,用户可以与应用程序交互。此时事件循环开始运行,应用程序会响应各种事件(如鼠标点击、键盘输入等)。
4. 应用程序退出
当应用程序关闭时,退出事件会被触发,窗口销毁,资源释放,所有的托管资源和非托管资源将被清理。
OnExit
方法:当应用程序关闭时,OnExit
方法会被调用,通常用来处理应用退出时的清理工作。
App.xaml.cs
:
protected override void OnExit(ExitEventArgs e)
{
base.OnExit(e); // 调用基类的 OnExit 方法
// 退出时的其他清理逻辑
MessageBox.Show("Application Exited");
}
Windows类的生命周期
在 WPF 中,Window
类代表一个应用程序的窗口,它是应用程序的主要交互界面。Window
类的生命周期包括从窗口创建、显示、交互到关闭的一系列事件和过程。理解 Window
类的生命周期对于开发健壮和高效的 WPF 应用程序非常重要。以下是 Window
类生命周期的详细解释,涵盖了创建、初始化、加载、显示、关闭等各个阶段。
一、Window
类生命周期概述
Window
类的生命周期大致可以分为以下几个阶段:
- 创建阶段:窗口被实例化。
- 初始化阶段:窗口的资源和控件进行初始化。
- 加载阶段:窗口及其内容被布局并显示。
- 激活阶段:窗口获得焦点并成为用户交互的窗口。
- 关闭阶段:窗口即将关闭,清理资源。
- 销毁阶段:窗口被完全销毁,释放资源。
二、Window
类生命周期的详细过程
1. 创建阶段
在 WPF 中,Window
类通常通过 new
操作符创建。当创建窗口时,Window
的构造函数被调用,并且它会开始初始化窗口的基本结构。
- 构造函数调用:
Window
类的构造函数首先被调用,在这个阶段,InitializeComponent()
方法会被调用,这个方法用于加载 XAML 文件并初始化控件。
示例:
public MainWindow()
{
InitializeComponent(); // 初始化 XAML 控件
}
2. 初始化阶段
窗口初始化时,会进行控件初始化以及一些基础设置。此时,窗口的控件已经创建好,但尚未显示到屏幕上。
OnInitialized
方法:当窗口的控件和资源完全初始化时,OnInitialized
方法会被调用。此时,控件的属性已经设置好,但尚未进行布局和渲染。
示例:
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
// 初始化完成后执行的逻辑
MessageBox.Show("Window Initialized");
}
3. 加载阶段
加载阶段是窗口开始准备显示的过程。WPF 会在这个阶段计算控件的布局,应用控件的样式,并最终将控件的可视化内容绘制到屏幕上。控件的大小、位置和可见性等属性都在此阶段确定。
OnLoaded
方法:当窗口的控件和布局完全准备好并呈现时,OnLoaded
方法会被调用。此时窗口已经显示在屏幕上,控件已经布局完成。
示例:
protected override void OnLoaded(RoutedEventArgs e)
{
base.OnLoaded(e);
// 控件加载完成后的逻辑
MessageBox.Show("Window Loaded");
}
此时,你可以访问窗口中已经加载的控件,并可以做一些初始化的后续工作,如绑定数据、动态添加控件等。
4. 激活阶段
当窗口获得用户输入焦点时,它会被激活并成为前景窗口。此时,窗口将接收用户的鼠标、键盘等输入。激活窗口通常是用户与窗口交互的开始。
Activated
事件:窗口获得焦点并被激活时,Activated
事件会被触发。
示例:
private void Window_Activated(object sender, EventArgs e)
{
MessageBox.Show("Window Activated");
}
此事件通常用于更新窗口的内容或状态,或者启动与用户交互的操作。
5. 关闭阶段
窗口进入关闭阶段时,应用程序会开始清理窗口资源,释放窗口占用的内存和其他资源。此时,窗口关闭前会进行一些处理,比如保存数据、确认是否退出等。
OnClosing
方法:当窗口即将关闭时,OnClosing
方法会被调用。如果你希望在窗口关闭前进行一些操作(比如询问用户是否保存更改),可以在此方法中实现。如果你希望取消窗口的关闭操作,可以设置e.Cancel = true
来阻止关闭。
示例:
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
// 在窗口关闭前执行一些操作
MessageBox.Show("Window is Closing");
// 可以通过设置 e.Cancel = true 来取消窗口关闭
// e.Cancel = true;
}
Closing
事件:OnClosing
触发前会触发Closing
事件。此时,窗口正在关闭,资源释放操作还未完成。
示例:
private void Window_Closing(object sender, CancelEventArgs e)
{
MessageBox.Show("Window Closing");
}
6. 销毁阶段
窗口销毁时,所有的资源都会被释放,窗口将完全从内存中清除。窗口的销毁通常发生在窗口关闭后,资源被释放并且窗口对象将被垃圾回收。
OnClosed
方法:当窗口完全关闭时,OnClosed
方法会被调用。此时,可以执行一些清理工作,比如释放资源、注销事件等。
示例:
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
// 窗口关闭后的清理工作
MessageBox.Show("Window Closed");
}
Closed
事件:当窗口完全关闭后,Closed
事件会被触发。此时窗口已经销毁,资源已经释放。
示例:
private void Window_Closed(object sender, EventArgs e)
{
MessageBox.Show("Window Closed");
}
阶段 方法或事件 作用 创建 Window()
构造函数窗口的实例化,初始化控件和资源 初始化 OnInitialized
窗口的控件已创建,但未进行布局和显示 加载 OnLoaded
控件和窗口显示并完成布局 激活 Activated
事件窗口获得输入焦点并成为前景窗口 关闭 OnClosing
方法,Closing
事件窗口即将关闭,可以进行数据保存或确认操作 销毁 OnClosed
方法,Closed
事件清理资源,窗口销毁,释放内存
OnLoaded
:窗口完全加载并显示后调用。Activated
:窗口获得焦点并被激活时触发。OnClosing
:窗口即将关闭时调用。OnClosed
:窗口完全关闭后调用。