使用ViewModel(MVVM)在当前View的子UserControl中设置属性

时间:2013-12-11 15:02:51

标签: c# wpf xaml mvvm

我有一个名为MainContentView.xaml的主视图,我将DataContext设置为名为MainContentViewModel.cs的ViewModel。在这里,我可以更新UI中出现的值,就像普通的MVVM一样 它看起来像这样:

<UserControl x:Class="namespace:MyProject.View.Home.MainContentView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:UserTools="clr-namespace:MyProject.View.Tools"
             xmlns:ViewModel="clr-namespace:MyProject.ViewModel.Home"
             mc:Ignorable="d" >
    <UserControl.DataContext>
        <ViewModel:MainContentViewModel />
    </UserControl.DataContext>

    <Grid x:Name="mainGrid">
        <!-- Normal objects -->
        <StackPanel>
            <Label Text="Im a label" />
            <TextBox />
        </StackPanel>
        <!-- My Custom Object -->
        <UserTools:MyCustomUserControl x:Name="myUserControl" />
    </Grid>
</UserControl>

我的MainContentViewModel.cs看起来像这样:

public class MainContentViewModel : INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;

    private string myLabel = "";
    public string MyLabel {
        get { return this.myLabel; }
        set {
            this.myLabel = value;
            OnPropertyChangedEvent("MyLabel");
        }
    }

    public MainContentViewModel() {
        MyLabel = string.Format("my label text");
    }

    protected virtual void OnPropertyChangedEvent(string _propertyName) {
        var _handler = this.PropertyChanged;
        if(_handler != null) { _handler(this, new PropertyChangedEventArgs(_propertyName)); }
    }
}

现在我想通过它自己的MyCustomUserControl.xamlMyCustomUserControlViewModel.cs中设置一些属性 我的MyCustomUserControl.xaml看起来像这样:

<UserControl x:Class="MyProject.View.Tools.MyCustomUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:ViewModel="clr-namespace:MyProject.ViewModel.Tools"
             mc:Ignorable="d" >
    <UserControl.DataContext>
        <ViewModel:MyCustomUserControlViewModel />
    </UserControl.DataContext>
    <Grid x:Name="mainGrid">
        <Label Content="{Binding myCustomLabel}" />
    </Grid>
</UserControl>

我的MyCustomUserControlViewModel.cs看起来像这样:

public class MyCustomUserControlViewModel : INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;

    private string myCustomLabel = "";
    public string MyCustomLabel {
        get { return this.myCustomLabel; }
        set {
            this.myCustomLabel = value;
            OnPropertyChangedEvent("MyCustomLabel");
        }
    }

    public MyCustomUserControlViewModel() {
        MyCustomLabel = string.Format("my custom label text");
    }

    protected virtual void OnPropertyChangedEvent(string _propertyName) {
        var _handler = this.PropertyChanged;
        if(_handler != null) { _handler(this, new PropertyChangedEventArgs(_propertyName)); }
    }
}

现在我想从MainContentViewModel.cs更新MyCustomLabel上的MyUserCustomUserControl.xaml属性,我想我必须通过MyCustomUserControlViewModel.cs进行更新,所以我可以保持MVVM模式。

我尝试过这样的事情: 在MainContentViewModel.cs

private MyCustomUserControlViewModel _viewModel = new MyCustomUserControlViewModel ();
(...)
public MainContentViewModel() {
    (...)
    _viewModel.SetMyCustomLabel("my new custom label text");
}

并在MyCustomUserControlViewModel.cs

public void SetMyCustomLabel(string _text) {
    MyCustomLabel = _text;
}

但这不起作用。我猜这是因为我正在实例化另一个MyCustomUserControlViewModel.cs对象。

那么,我该怎么办呢?

更新和工作(谢谢Sheridan)
我选择了Sheridan的一个解决方案,最终得到了这个有效的解决方案。在我的MainContentView.xaml

(...)
<UserTools:MyCustomUserControl DataContext="{Binding ChildViewModel, Mode=OneWay}" />

在我的MainContentViewModel.cs

private MyCustomUserControlViewModel childViewModel = new MyCustomUserControlViewModel();
public MyCustomUserControlViewModel ChildViewModel {
    get { return childViewModel; }
    private set { childViewModel = value; OnPropertyChangedEvent("ChildViewModel"); }
}

然后我可以这样做:

public MainContentViewModel() {
    (...)
    ChildViewModel.SetMyCustomLabel("my new custom label text");
}

1 个答案:

答案 0 :(得分:2)

如果您的父视图模型可以访问所使用的实际子视图模型实例(由于您实例化子视图模型的方式,它可能不是这样),那么您可以简单地完成此操作:

public MainContentViewModel() {
    (...)
    _viewModel.CustomLabel  = "my new custom label text";
}

<强> 1

一种解决方案是将子视图模型从子视图中移除到父视图模型,然后使用ContentControlDataTemplate显示子视图:

<DataTemplate DataType="{x:Type ViewModels:MyCustomUserControlViewModel}">
    <Views:MyCustomUserControlView />
</DataTemplate>

...

在MainContentViewModel中:

public MyCustomUserControlViewModel ChildViewModel
{
    get { return childViewModel; }
    private set { childViewModel = value; NotifyPropertyChanged("ChildViewModel"); }
}

...

<Grid x:Name="mainGrid">
    <!-- Normal objects -->
    <StackPanel>
        <Label Text="Im a label" />
        <TextBox />
    </StackPanel>
    <!-- My Custom Object -->
    <ContentControl x:Name="myUserControl" Content="{Binding ChildViewModel}" />
</Grid>

这样做可以让你简单地调用属性,如我的第一个例子所示。

<强> 2

一种替代方法是将属性移动到父视图模型并直接从子UserControl绑定到它:

<Grid x:Name="mainGrid">
    <Label Content="{Binding DataContext.myCustomLabel, RelativeSource={RelativeSource 
        AncestorType={x:Type Views:MainContentView}}}" />
</Grid>

更新&gt;&gt;&gt;

第3

好的,我刚才有了另一个想法......你可以将另一个属性添加到父视图模型中,并将数据绑定到子视图模型,如下所示:

<UserTools:MyCustomUserControl DataContext="{Binding ChildViewModel, 
Mode=OneWayToSource}" x:Name="myUserControl" />

这只是猜测,但可能能够像这样挂钩到子视图模型......然后你可以在父视图模型中执行此操作:

if (ChildViewModel != null) ChildViewModel.CustomLabel = "my new custom label text";

<强> 4

实际上......我刚才有了另一个想法。您可以从父视图模型中的属性绑定到UserControl,如果您向其添加DependencyProperty

<UserTools:MyCustomUserControl CustomLabel="{Binding CustomLabelInParentViewModel}" 
x:Name="myUserControl" />

在这种情况下,您可以简单地从子视图模型中删除该属性。