【问题标题】:Notify ViewModel when View is rendered/instantiated当 View 被渲染/实例化时通知 ViewModel
【发布时间】:2025-12-04 07:45:01
【问题描述】:

我有一个自定义用户控件 (ChartControl),我在我的 WPF 应用程序 (MainApp) 中使用它并按如下方式呈现:

<ContentControl Grid.Row="1" Content="{Binding ChartControl, Mode=OneWay}" />

在启动MainApp 时,将按给定顺序执行以下操作:

主应用视图 MainApp 视图模型 ChartControl 视图模型 图表控件视图

我在 MainApp ViewModel 的构造函数中实例化了 ChartControl ViewModel。问题是在实例化ChartControl ViewModel 之后,我还需要从MainApp 中调用ChartControl 的方法。

我遇到的问题是,在我调用该方法作为其视图模型的一部分之前,我需要渲染 ChartControl 视图(执行其 InitializeComponent)。

我认为一种解决方案是在视图模型完全实例化和设置时通知视图模型。这是一个可行的解决方案吗?如果是,我该怎么做?

总之,在调用匹配视图模型的方法之前,我需要完全设置视图。我遇到的问题是,在这种情况下,首先实例化视图模型,然后才渲染视图。

有什么想法吗?

谢谢

【问题讨论】:

  • 那个方法是什么,为什么要在InitializeComponent执行后调用?你到底想做什么?对我来说,这听起来像是 xy problem
  • 它将数据系列呈现为图表表面上的图表,我遇到了图表未显示的问题。我怀疑这是因为视图模型在视图初始化之前构造了图表并可以渲染它。
  • @SriramSakthivel,正如您正确暗示的那样,这并没有让我找到解决问题的方法。我的问题是,当我从托管 wpf 应用程序的视图模型构造函数呈现系列时,用户控件中的自定义图表库不会呈现图表系列。详情请见*.com/questions/29805060/…

标签: c# wpf mvvm view viewmodel


【解决方案1】:

在 MVVM 世界中,我发现在创建可视项并将其放入视图(在本例中添加到列表)时,该项在加载事件触发之前不会出现在可视树中。

我的视图模型包含 XAML 视图将显示的可观察集合中的项目列表。

ObservableCollection<MyControl> Items;

我会向列表中添加一个项目,但是当我执行要求它位于可视树中并执行可视树递归的操作时,这不会立即发生。相反,我不得不编写如下代码:

var newItem = new MyControl();

newItem.Loaded += NewItemLoaded;

Items.Add(new MyControl());

然后,事件处理程序将取消挂钩并执行操作 - 此时它根据需要位于可视树中:

private void NewItemLoaded(object sender, RoutedEventArgs e)
{
    var item = sender as MyControl;
    item.Loaded -= NewItemLoaded;

    // now this item is in the visual tree, go ahead and do stuff ...
}

【讨论】:

    【解决方案2】:

    我正在使用类似 Hogler 的解决方案,仅使用反射(惰性耦合解决方案)。我不想引用特定类型的 ViewModel(因为通用性、可互换性等)。

    public MyControl()
    {
       InitializeComponent();
       Loaded += MyControl_Loaded;
    }
    
    private void MyControl_Loaded(object sender, RoutedEventArgs e)
    {   
      (DataContext.GetType().GetProperty("LoadedCommand")?.
        GetValue(DataContext) as ICommand)?.
        Execute(null);
    }
    

    ViewModel 可以(不必)包含所需的命令,如属性(在本例中为 LoadedCommand)。仅此而已。

    【讨论】:

      【解决方案3】:

      另一种连接 Loaded 事件的方法,基本上呈现与 nit 的答案相同的结果,只是在视图的构造函数中引用您的视图模型并添加一个事件处理程序,该事件处理程序反过来调用您需要调用的任何方法,就像这样:

      public MyControl()
      {
         InitializeComponent();
      
         this.Loaded += (s, e) => { ((MyViewModel)DataContext).MyInitializer(); };
      }
      

      如果您发现语法令人困惑,您可能需要阅读 Anonymous methodsSubscribing to event handlers (using anonymous methods)

      【讨论】:

      • 在我连接到 UserControls 的 Visibility 属性的各种方式中,我最喜欢这个。
      【解决方案4】:

      您可以使用交互触发器在任何 UI 事件上在您的 VM 上触发命令

      您可以像下面这样监听 UserControl 的 Loaded 事件并将其绑定到 VM 上的 Command:

      <UserControl x:Class="Test.TestView.MyUserControl"
               xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
               xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
              x:Name="myControl" >
      
      <i:Interaction.Triggers>
          <i:EventTrigger EventName="Loaded">
              <i:InvokeCommandAction Command="{Binding ElementName=myControl, Path=OnLoadedCommand}"/>
          </i:EventTrigger>
      </i:Interaction.Triggers>
      

      并确保您的 VM 中有 Command

      public ICommand OnLoadedCommand { get; private set; }
      
      public MyUserControl()
      {
          OnLoadedCommand = new DelegateCommand(OnLoaded);
      }
      
      public void OnLoaded()
      {
      }
      

      【讨论】:

      • 如果您的 UserControl 的 DataContext 设置为您的 ViewModel(通常),您应该使用 Command="{Path=OnLoadedCommand}"。 myControl (this) 不包含 OnLoadedCommand (ViewmModel 包含)。在视图中(this = myControl = 后面的代码)您可以直接使用事件(通过事件处理程序)。
      • 真的希望这适用于 Xamarin.Forms 而不仅仅是 WPF