如何响应ContentPage子ContentView的ControlTemplate属性变更?
问题描述
页面里有个ContentView,通过DataTriggers绑定ViewModel的布尔属性BoolValueFromViewModel,根据状态加载不同的静态ControlTemplate,代码如下:
<ContentPage.Resources> <ControlTemplate x:Key="template1"> <local:AnotherContentView /> <!-- 需要调用AnotherContentView的某个方法 --> </ControlTemplate> <ControlTemplate x:Key="template2"> </ControlTemplate> </ContentPage.Resources> <ContentView x:Name="templatedView" > <ContentView.Triggers> <DataTrigger TargetType="ContentView" Binding="{Binding BoolValueFromViewModel}" Value="True"> <Setter Property="ControlTemplate" Value="{StaticResource template1}"/> </DataTrigger> <DataTrigger TargetType="ContentView" Binding="{Binding BoolValueFromViewModel}" Value="False"> <Setter Property="ControlTemplate" Value="{StaticResource template2}"/> </DataTrigger> </ContentView.Triggers> </ContentView>
AnotherContentView是自定义控件,里面有个LoopGaugeDrawing方法,负责每隔若干毫秒重绘GraphicsView,实现随时间递减的仪表盘。当这个控件显示时必须调用该方法,但试了OnApplyTemplate和OnAppearing都没生效。
LoopGaugeDrawing方法代码:
public async Task LoopGaugeDrawing() { do { _progressGauge.TargetTime = Duration; _progressGauge.ElapsedTime = Duration.Subtract(RemainingTime); ProgressView.Invalidate(); await Task.Delay(TimeSpan.FromMilliseconds(Constants.TimerTickMilliseconds)); } while (!ConditionFromViewModel); }
解决方案
方法1:重写AnotherContentView的OnParentSet方法
控件被加载到模板中时,父元素会被设置,利用这个时机启动任务,同时处理控件被移除时的任务终止:
private Task _drawingTask; protected override void OnParentSet() { base.OnParentSet(); // 父元素存在(控件显示)且任务未启动时,开始绘制循环 if (Parent != null && _drawingTask == null) { _drawingTask = LoopGaugeDrawing(); } // 父元素为null(控件被移除)时,终止循环 else if (Parent == null && _drawingTask != null) { ConditionFromViewModel = true; _drawingTask = null; } }
方法2:监听ContentView的TemplateApplied事件
在页面中监听模板切换完成事件,找到模板内的AnotherContentView并调用方法:
- 先给template1里的控件加个名字:
<ControlTemplate x:Key="template1"> <local:AnotherContentView x:Name="anotherView" /> </ControlTemplate>
- 给ContentView绑定
TemplateApplied事件:
<ContentView x:Name="templatedView" TemplateApplied="TemplatedView_TemplateApplied"> <!-- 原有触发器代码不变 --> </ContentView>
- 后台实现事件处理:
private Task _currentDrawingTask; private AnotherContentView _previousAnotherView; private void TemplatedView_TemplateApplied(object sender, EventArgs e) { var contentView = sender as ContentView; var anotherView = contentView.FindByName<AnotherContentView>("anotherView"); // 切换到template1时启动任务 if (anotherView != null && _currentDrawingTask == null) { _previousAnotherView = anotherView; _currentDrawingTask = anotherView.LoopGaugeDrawing(); } // 切换到template2时终止任务 else if (anotherView == null && _previousAnotherView != null) { _previousAnotherView.ConditionFromViewModel = true; _currentDrawingTask = null; _previousAnotherView = null; } }
方法3:通过ViewModel发送消息通知
利用MessagingCenter在ViewModel属性变化时,通知AnotherContentView启动或停止任务:
- 在
AnotherContentView中订阅消息:
private Task _drawingTask; public AnotherContentView() { InitializeComponent(); MessagingCenter.Subscribe<YourViewModel, bool>(this, "ToggleGaugeDrawing", (vm, isActive) => { if (isActive && _drawingTask == null) { _drawingTask = LoopGaugeDrawing(); } else if (!isActive && _drawingTask != null) { ConditionFromViewModel = true; _drawingTask = null; } }); } protected override void Dispose(bool disposing) { if (disposing) { MessagingCenter.Unsubscribe<YourViewModel, bool>(this, "ToggleGaugeDrawing"); } base.Dispose(disposing); }
- 在ViewModel的
BoolValueFromViewModel属性变更时发送消息:
private bool _boolValueFromViewModel; public bool BoolValueFromViewModel { get => _boolValueFromViewModel; set { if (_boolValueFromViewModel != value) { _boolValueFromViewModel = value; OnPropertyChanged(); MessagingCenter.Send(this, "ToggleGaugeDrawing", value); } } }
内容的提问来源于stack exchange,提问作者Toto
相关产品推荐
相关产品推荐

