当视图被渲染/实例化时通知ViewModel

6

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

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

在启动 MainApp 时,按照以下顺序执行:
MainApp 视图 MainApp 视图模型 ChartControl 视图模型 ChartControl 视图
我在我的 MainApp 视图模型的构造函数中实例化了 ChartControl 视图模型。问题是,在实例化 ChartControl 视图模型后,我还需要从 MainApp 中调用 ChartControl 的一个方法。
我遇到的问题是,我需要先呈现 ChartControl 视图(执行其 InitializeComponent),然后再调用其视图模型中的方法。
我认为一种解决方案可能是当视图完全实例化并设置好后,从视图通知视图模型。这是可行的解决方案吗?如果是,我该如何做?
总之,我需要在调用相应视图模型的方法之前完全设置好视图。问题是,在这种情况下,先实例化视图模型,然后才呈现视图。
有什么想法吗?
谢谢

那个方法是什么,为什么需要在InitializeComponent执行后调用它?你实际上想做什么?对我来说,这听起来像一个xy问题 - Sriram Sakthivel
它将数据系列呈现为图表,并且我遇到了一个问题,即图表未显示。我怀疑这是因为视图模型在视图初始化之前构建了图表并可以呈现它。 - Matt
@SriramSakthivel,正如你正确地暗示的那样,这并没有为我的问题提供解决方案。我的问题是,在用户控件中的自定义图表库不会呈现图表系列,当我从托管wpf应用程序的视图模型构造函数中呈现系列时。请参见http://stackoverflow.com/questions/29805060/wpf-contentcontrol-not-correctly-rendered-when-not-in-view-not-active了解更多详情。 - Matt
4个回答

8

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

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

<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>

当然,您的虚拟机中会有命令:

public ICommand OnLoadedCommand { get; private set; }

public MyUserControl()
{
    OnLoadedCommand = new DelegateCommand(OnLoaded);
}

public void OnLoaded()
{
}

2
如果您的UserControl的DataContext设置为ViewModel(通常情况下),则应使用Command = "{Path = OnLoadedCommand}"。 myControl(this)不包含OnLoadedCommand(ViewmModel包含)。 在视图中(this = myControl = behind code),您可以直接使用事件(通过事件处理程序)。 - honzakuzel1989
真希望这个能够与Xamarin.Forms一起使用,而不仅仅是WPF。 - Damian

5

另一种连接Loaded事件的方法,基本上与nit的答案产生相同的结果,只需在视图的构造函数中引用您的视图模型并添加一个事件处理程序,该处理程序再调用您需要调用的任何方法,如下所示:

public MyControl()
{
   InitializeComponent();

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

如果您觉得语法很难理解,您可能需要了解匿名方法和使用匿名方法订阅事件处理程序。请参考匿名方法使用匿名方法订阅事件处理程序

在我连接到UserControls的Visibility属性的各种方式中,到目前为止我最喜欢这种方式。 - Dave

3

我正在使用类似于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)。没有更多了。


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 ...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接