Xamarin中Timer Elapsed事件未触发 警告视图无法自动隐藏问题
问题根因
这个问题和INotifyPropertyChanged接口完全无关,该接口仅用于数据绑定场景下的属性变更通知,你是直接在代码后台操作控件自身属性,本身不需要实现该接口。
真正的问题出在你使用的System.Timers.Timer运行机制上:
System.Timers.Timer的Elapsed事件默认在线程池的工作线程触发,而所有跨平台UI框架(包括MAUI/Xamarin.Forms)都有强制要求:UI元素的属性修改、渲染操作必须在UI主线程执行,非主线程直接操作UI属性会被静默拦截,不会抛显性异常,也不会生效。你这里的Elapsed事件实际上已经正常触发了,只是回调里修改IsVisible = false的操作因为跨线程被拦截,才会出现警告框一直不消失的现象。- 你当前的Timer逻辑还有个隐藏缺陷:如果用户在警告显示的5秒内多次触发
ShowWarning,现有逻辑不会重置计时器,会出现警告显示时长不足5秒就提前隐藏,或者多次触发回调的异常表现。
修复方案
有两种可行的修复方式,优先推荐第二种,更适配UI场景:
方案1:保留现有Timer,回调中切换到UI线程执行UI操作
只需要修改Elapsed事件的回调逻辑,把UI操作切到主线程执行,同时补充计时器重置逻辑即可:
private void SetEvents() { timer.Elapsed += (object sender, ElapsedEventArgs e) => { // 切换到UI主线程修改控件属性 MainThread.BeginInvokeOnMainThread(() => { this.IsVisible = false; }); timer.Stop(); }; ShowWarning += () => { this.IsVisible = true; // 每次触发警告先重置计时器,保证每次都显示满5秒 timer.Stop(); timer.Start(); }; }
方案2:替换为UI线程专属的DispatcherTimer(推荐)
直接使用UI框架自带的IDispatcherTimer,这个计时器的Tick事件天然就在UI主线程触发,完全不需要手动处理跨线程问题,也不会出现你现在遇到的bug,是UI场景下做延时操作的最优选择。
替换后的完整类代码如下:
public partial class ValidExpressionWarning : ContentView { public static ValidExpressionWarning Instance; public Action ShowWarning; private readonly IDispatcherTimer _warningTimer; public ValidExpressionWarning() { InitializeComponent(); Instance = this; // 初始化UI线程计时器 _warningTimer = Dispatcher.CreateTimer(); _warningTimer.Interval = TimeSpan.FromSeconds(5); BindEvents(); } private void BindEvents() { _warningTimer.Tick += (_, _) => { IsVisible = false; _warningTimer.Stop(); }; ShowWarning += () => { IsVisible = true; // 重置计时,保证每次触发都显示满5秒 _warningTimer.Stop(); _warningTimer.Start(); }; } }
补充说明:如果你用的是Xamarin.Forms而非.NET MAUI,
IDispatcherTimer对应的API是Device.StartTimer,逻辑是一致的,都是在UI线程执行计时回调。
内容的提问来源于stack exchange,提问作者youngcoder126
相关产品推荐
相关产品推荐

