如何在创建后检索控件的viewmodel?

时间:2013-01-09 15:08:24

标签: wpf mvvm

我有一个Window和一个UserControl。 UserControl创建自己的viewmodel,如下所示:

<UserControl x:Class="UiInteraction.UserControl3"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:UiInteraction"
             mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">

    <UserControl.DataContext>
        <local:UserControl3Vm/>
    </UserControl.DataContext>

    <StackPanel>
        <TextBlock Text="{Binding String1}"/>
    </StackPanel>

</UserControl>

当Window实例化UserControl时,我希望Window的viewmodel能够检索UserControl的viewmodel。

<Window x:Class="UiInteraction.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:UiInteraction"
        Title="MainWindow" Height="350" Width="525">

    <Window.DataContext>
        <local:MainWindowVm/>
    </Window.DataContext>

    <StackPanel>
        <local:UserControl3 DataContext="{Binding UserControl3Vm, Mode=OneWayToSource}"/>
    </StackPanel>

</Window>

Window的viewmodel具有object类型的公共可设置属性。使用DataContext绑定,我期望一旦创建UserControl3,其DataContext的值(它是对其viewmodel的引用)将被分配给Window的viewmodel上的UserControl3Vm属性。

实际发生的是使用null值调用Window.UserControl3Vm属性setter。

为什么会发生这种情况,实现我的想法的最佳途径是什么?

我知道将UserControl的viewmodel实例化为Window的viewmodel上的属性并让UserControl简单地绑定到它上(并且这也会最小化视图与其viewmodel的耦合)会更容易。但是在我工作的地方他们有点疯狂并且首先选择查看第一个MVVM而不是viewmodel,所以我正在寻找最分离的方式来使viewmodel在视图模型由视图创建时有效地进行协作。

2 个答案:

答案 0 :(得分:2)

我不认为在没有代码隐藏的情况下使用OneWayToSource绑定会起作用。

最初,您的UserControl.DataContext设置为UserControl3vm的实例,但是您要用绑定替换UserControl3vm,因此您的原始UserControl3vm不再在任何地方引用。

要使OneWayToSource绑定起作用,您必须先将DataContext设置为OneWayToSource绑定,然后将绑定的Source设置为{{1}的新实例来自UserControl内部。

如果我没记错,您可以使用BindingOperations.GetBindingExpression获取绑定,并更新它的DataItem属性。您不能简单地设置UserControl3vm,因为它会覆盖您的UserControl.DataContext绑定。

就个人而言,我只会在OneWayToSource事件背后的代码中执行此操作

如果他们坚持使用View-First MVVM,那么View正在控制应用程序流程,我认为你没有任何理由将应用程序逻辑保留在View的代码隐藏之外。

所以只需在Loaded事件中将Loaded属性设置为Window.DataContext.UserControl3Vm:)

UserControl3.DataContext

答案 1 :(得分:0)

这可以在XAML中使用一些解决方法(破解DataContext主机元素的访问权限)。提到了这种方法here。它使用Freezables

XAML

<Window x:Class="VM2VMBindingInXaml.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vw="clr-namespace:VM2VMBindingInXaml.View"
        xmlns:vm="clr-namespace:VM2VMBindingInXaml.ViewModel"
        Title="MainWindow" Height="350" Width="525" >
    <Window.Resources>
             <vm:UserControl1ViewModel x:Key="childVM"></vm:UserControl1ViewModel>
        <vm:DataResource x:Key="childVmBinding" BindingTarget="{Binding ElementName=child, Path=DataContext}"/>
    </Window.Resources>
    <Window.DataContext>
        <vm:MainWindowViewModel x:Name="mainViewModel" >
            <vm:MainWindowViewModel.ChildViewModel>
                <vm:DataResourceBinding DataResource="{StaticResource childVmBinding}">

                </vm:DataResourceBinding>
            </vm:MainWindowViewModel.ChildViewModel>
        </vm:MainWindowViewModel>
    </Window.DataContext>
    <Grid>
        <vw:UserControl1 x:Name="child" DataContext="{Binding Source={StaticResource ResourceKey=childVM}}">
        </vw:UserControl1>
    </Grid>
</Window>