You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何响应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并调用方法:

  1. 先给template1里的控件加个名字:
<ControlTemplate x:Key="template1">
    <local:AnotherContentView x:Name="anotherView" />
</ControlTemplate>
  1. 给ContentView绑定TemplateApplied事件:
<ContentView x:Name="templatedView" TemplateApplied="TemplatedView_TemplateApplied">
    <!-- 原有触发器代码不变 -->
</ContentView>
  1. 后台实现事件处理:
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启动或停止任务:

  1. 在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);
}
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 02:47:06