c# 快速了解事件的使用
C# 中的事件
在 C# 中,事件是一种成员,用于将特定的事件通知发送给订阅者。事件通常用于实现观察者模式,它允许一个对象将状态的变化通知其他对象,而不需要知道这些对象的细节。
事件的基本概念
事件基本上是一个用户操作,如按键、点击、鼠标移动等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。。。。
简单粗暴的理解就是 我在一个A地触发了这个事件,需要在B地做出对应的回应
事件的几大要素
一、事件的声明要素
1、委托类型(Delegate Type):定义事件处理方法的签名;必须与事件处理方法和参数和返回类型匹配;可以使用预定义委托(EventHandler<T>)或自定义委托,本次使用的是预定义委托
//自定义委托
public delegate void MainWindowEvntSelectedHandler(object sender,MainWindowEvntArgs e)
//预定义委托
public static event EventHandler<MainWindowEvntArgs> MainWindowEvntSelected;
2、事件关键字(event):声明成员为事件,只允许+=,-=操作
3、事件发布者:声明和触发事件的类;控制事件何时被触发;包含事件触发方法(On开头)
public static class MainWindowEventBus
{
//定义事件
public static event EventHandler<MainWindowEvntArgs> MainWindowEvntSelected;
//触发事件的方法
public static void OnMainWindowEvntSelected(string message1, string message2)
{
MainWindowEvntSelected?.Invoke(null, new MainWindowEvntArgs
{
labelMessage = message1,
buttonMessage = message2
});
}
}
二、事件的订阅和取消
1、事件订阅(Subscription)
- 使用
+=操作符添加事件处理方法 - 处理方法必须与事件委托类型兼容
- 可以是实例方法、静态方法、匿名方法或 Lambda 表达式
public WindowB()
{
InitializeComponent();
MainWindowEventBus.MainWindowEvntSelected += OnMainWindowEvntSelected;//订阅事件
this.Unloaded += WindowB_Unloaded;
}
2、事件取消订阅(Unsubscription)
- 使用
-=操作符移除事件处理方法 - 必须使用与订阅时相同的方法引用
- 防止内存泄漏和意外调用
private void WindowB_Unloaded(object sender, RoutedEventArgs e)
{
MainWindowEventBus.MainWindowEvntSelected -= OnMainWindowEvntSelected;//取消订阅事件
}
三、事件要素的关系图
发布者(Publisher)
├── 声明事件(event)
│ └── 基于委托类型(Delegate)
├── 触发事件(OnEvent())
│ └── 传递 sender 和 EventArgs
└── 维护订阅者列表
订阅者(Subscriber)
├── 订阅事件(+=)
│ └── 提供兼容的处理方法
└── 取消订阅(-=)
事件参数(EventArgs)
├── 继承自 System.EventArgs
└── 封装事件相关数据
四、实际例子
比如我在界面A触发事件,希望B页面发生变化,这在生产环境中是比较常用的,比如,两个页面都有配方表格,生产人员会希望在A页面加载完配方B页面的配方会随着变化;或者在A页面按下操作按钮,隐藏B页面的部分内容等等;
1、在主页面编写事件参数和事件方法
xaml代码
<Window x:Class="Demo_Event.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Demo_Event"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Label Content="这是A页面" Width="150" Height="50" FontSize="20" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Label x:Name="ceshi_label" Width="150" Height="50" FontSize="20"/>
<Button x:Name="btn1" Height="50" VerticalAlignment="Bottom" Content="A事件" FontSize="15" Margin="333,0,329,71" Click="Button_Click"/>
</Grid>
</Window>
mainWindow.cs代码
namespace Demo_Event
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private WindowB _windowB;
private void Button_Click(object sender, RoutedEventArgs e)
{
ceshi_label.Content = "在A触发事件";
if (_windowB == null)
{
_windowB = new WindowB();
_windowB.Closed += (s, ev) => _windowB = null;
_windowB.WindowStartupLocation= WindowStartupLocation.CenterScreen;
_windowB.Show();
}
MainWindowEventBus.OnMainWindowEvntSelected(ceshi_label.Content.ToString(), btn1.Content.ToString());
}
/// <summary>
/// 定义事件参数,传递信息
/// </summary>
public class MainWindowEvntArgs:EventArgs
{
public string labelMessage { get; set; }//传递要给出的信息
public string buttonMessage { get; set; }
}
public static class MainWindowEventBus
{
//定义事件
public static event EventHandler<MainWindowEvntArgs> MainWindowEvntSelected;
//触发事件的方法
public static void OnMainWindowEvntSelected(string message1, string message2)
{
MainWindowEvntSelected?.Invoke(null, new MainWindowEvntArgs
{
labelMessage = message1,
buttonMessage = message2
});
}
}
}
}
2、在其他页面订阅该事件
B页面的Xaml代码
<Window x:Class="Demo_Event.WindowB"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Demo_Event"
mc:Ignorable="d"
Title="WindowB" Height="450" Width="800">
<Grid>
<Label Content="这是B页面" Width="150" Height="50" FontSize="20" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Label Content="文本信息" Width="100" Height="50" FontSize="20" Margin="112,119,588,266"/>
<Label Content="按钮信息" Width="100" Height="50" FontSize="20" Margin="112,182,588,203"/>
<TextBox x:Name="txt_ceshi1" FontSize="15" Width="200" Height="50" Margin="220,119,380,266" />
<TextBox x:Name="txt_ceshi2" FontSize="15" Width="200" Height="50" Margin="220,182,380,203" />
</Grid>
</Window>
B页面的CS代码
namespace Demo_Event
{
/// <summary>
/// WindowB.xaml 的交互逻辑
/// </summary>
public partial class WindowB : Window
{
public WindowB()
{
InitializeComponent();
MainWindowEventBus.MainWindowEvntSelected += OnMainWindowEvntSelected;//订阅事件
this.Unloaded += WindowB_Unloaded;
}
private void WindowB_Unloaded(object sender, RoutedEventArgs e)
{
MainWindowEventBus.MainWindowEvntSelected -= OnMainWindowEvntSelected;//取消订阅事件
}
//事件处理方法
private void OnMainWindowEvntSelected(object sender, MainWindowEvntArgs e)
{
txt_ceshi1.Text=e.labelMessage;
txt_ceshi2.Text=e.buttonMessage;
}
}
}
五、运行效果


答案是他会接着触发事件回到步骤2
六、特殊情况
需要注意的是如果是在单例模式(Instance)下,上述的写法只会在加载的时候,第一次触发事件有效,后续就无效了
问题的根源是,单例模式下对象一旦被创建就无法被摧毁,只有程序退出才会被摧毁
第一次加载:订阅事件 ✅
第一次卸载:移除订阅 ✅
第二次加载:尝试再次订阅,但单例实例已存在,Loaded事件不会触发订阅逻辑 ❌
后续事件触发:由于订阅已被移除且未重新订阅,事件处理方法不会执行 ❌
导致不会继续订阅
单例模式的核心特性
- 唯一性:整个应用程序中只有一个实例
- 全局访问:通过静态方法Instance()全局访问
- 延迟初始化:实例在第一次使用时创建
解决方案:添加一个_isEventSubscribed 状态来判断他是否在订阅 ;增加了一个确保事件被订阅的方法EnsureEventSubscribed,这样就能始终记录事件的订阅状态,从而一直触发
public partial class WindowB : Window
{
private static WindowB _instance;
private static readonly object _lock = new object();
private bool _isEventSubscribed = false;
public static WindowB Instance()
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
_instance = new WindowB();
}
}
}
_instance.EnsureEventSubscribed();
return _instance;
}
public WindowB()
{
InitializeComponent();
MainWindowEventBus.MainWindowEvntSelected += OnMainWindowEvntSelected;//订阅事件
this.Unloaded += WindowB_Unloaded;
}
private void EnsureEventSubscribed()
{
if (!_isEventSubscribed)
{
MainWindowEventBus.MainWindowEvntSelected += OnMainWindowEvntSelected;
_isEventSubscribed = true;
}
}
private void WindowB_Unloaded(object sender, RoutedEventArgs e)
{
if (_isEventSubscribed)
{
MainWindowEventBus.MainWindowEvntSelected -= OnMainWindowEvntSelected;//取消订阅事件
_isEventSubscribed= false;
}
}
//事件处理方法
private void OnMainWindowEvntSelected(object sender, MainWindowEvntArgs e)
{
txt_ceshi1.Text = e.labelMessage;
txt_ceshi2.Text = e.buttonMessage;
}
}
更多推荐


所有评论(0)