WinUI 3中为CustomControl添加自定义事件及崩溃问题解决
WinUI 3 自定义控件添加状态变更事件及崩溃问题解决
一、需求与初始代码
需要在WinUI 3的自定义控件MyCustomTextBlock中添加状态变更事件,当CurrentStatus属性变化时触发,支持像Button的Click事件一样在XAML中绑定使用。
初始自定义控件代码
public sealed class MyCustomTextBlock : Control { public enum Status { Waiting, Busy, } private Status currentStatus; public Status CurrentStatus { get { return currentStatus; } } public MyCustomTextBlock() { this.DefaultStyleKey = typeof(MyCustomTextBlock); currentStatus = Status.Waiting; } // currentStatus变量会在控件内部的自定义方法中被设置 }
期望的XAML使用方式
<local:MyCustomTextBlock x:Name="MessageBlock" Message="{TemplateBinding Message}" DisplayMode="{TemplateBinding DisplayMode}" StatusChanged="MessageBlock_StatusChanged"> </local:MyCustomTextBlock>
期望的事件处理代码
private void MessageBlock_StatusChanged(object sender, RoutedEventArgs e) { // 检查CurrentStatus属性 if (((MyCustomTextBlock)sender).CurrentStatus == MyCustomTextBlock.Status.Waiting) // 执行操作... }
疑问:如何在控件代码中声明Event和EventHandler?WinUI 3的Microsoft.UI.Xaml库中找不到WPF里的EventManager.RegisterRoutedEvent()方法。
二、自定义事件的正确实现
WinUI 3中没有WPF的路由事件注册机制,推荐使用CLR自定义事件实现需求,步骤如下:
- 定义自定义事件参数类(可选,但能传递状态变更前后的值,提升实用性):
public class StatusChangedEventArgs : EventArgs { public MyCustomTextBlock.Status OldValue { get; } public MyCustomTextBlock.Status NewValue { get; } public StatusChangedEventArgs(MyCustomTextBlock.Status oldValue, MyCustomTextBlock.Status newValue) { OldValue = oldValue; NewValue = newValue; } }
- 修改自定义控件代码,添加事件声明并在状态变更时触发事件:
public sealed class MyCustomTextBlock : Control { public enum Status { Waiting, Busy, } private Status currentStatus; public Status CurrentStatus { get { return currentStatus; } private set { if (currentStatus != value) { Status oldValue = currentStatus; currentStatus = value; // 触发状态变更事件 OnStatusChanged(new StatusChangedEventArgs(oldValue, currentStatus)); } } } // 声明自定义状态变更事件 public event EventHandler<StatusChangedEventArgs> StatusChanged; public MyCustomTextBlock() { this.DefaultStyleKey = typeof(MyCustomTextBlock); currentStatus = Status.Waiting; } // 触发事件的内部方法 private void OnStatusChanged(StatusChangedEventArgs e) { StatusChanged?.Invoke(this, e); } // 示例:控件内部修改状态的方法,调用此方法会自动触发事件 public void UpdateStatus(Status newStatus) { CurrentStatus = newStatus; } }
三、崩溃问题及解决办法
问题现象
按上述代码修改后,XAML中绑定StatusChanged事件后,运行时出现Win32非托管异常,应用崩溃,无具体异常信息,崩溃发生在MyCustomTextBlock构造函数执行完成后。
解决办法
移除XAML中的事件绑定声明:
<local:MyCustomTextBlock x:Name="MessageBlock" Message="{TemplateBinding Message}" DisplayMode="{TemplateBinding DisplayMode}"> </local:MyCustomTextBlock>
改为在控件Loaded事件中通过代码绑定事件:
public sealed partial class MainWindow : Window { public MainWindow() { this.InitializeComponent(); MessageBlock.Loaded += MessageBlock_Loaded; } private void MessageBlock_Loaded(object sender, RoutedEventArgs e) { MessageBlock.StatusChanged += MessageBlock_StatusChanged; } private void MessageBlock_StatusChanged(object sender, StatusChangedEventArgs e) { MyCustomTextBlock control = (MyCustomTextBlock)sender; if (e.NewValue == MyCustomTextBlock.Status.Waiting) { // 执行操作... } else if (e.NewValue == MyCustomTextBlock.Status.Busy) { // 执行其他操作... } } }
该问题属于Microsoft.UI.Xaml的已知问题,通过代码后置绑定事件可规避崩溃。
内容的提问来源于stack exchange,提问作者Cheshire Cat
相关产品推荐
相关产品推荐

