如何为ChildViewModel提供MainViewModel中的数据

时间:2014-05-08 09:17:15

标签: c# wpf xaml mvvm

通常在我设计MVVM应用程序时,会出现以下情况。一个窗口,有多个孩子,共享相同的数据源。

我正在尝试确定为所有孩子实施单个数据源的最佳方法。我能想到的有三种选择,都有各自的优缺点。

实施例

Window有两个子UserControl,每个都在自己的标签中。

Main Window Example

用户界面是这样链接的。

XAML Design

为了保持模块化并为其提供数据,ViewModels中反映了相同的设计。

ViewModel Design

MainViewModel设置如下。

public class MainViewModel : ViewModelBase
{
    private readonly ChildViewModelA _childViewModelA = new ChildViewModelA();
    private readonly ChildViewModelB _childViewModelB = new ChildViewModelB();

    public ChildViewModelA ChildViewModelA { get { return this._childViewModelA; } }
    public ChildViewModelB ChildViewModelB { get { return this._childViewModelB; } }
}

MainWindow实例化MainViewModel并设置子节点的DataContext。子控件绑定到数据属性。

public partial class MainWindow : Window
{
    private readonly MainViewModel _viewModel = new MainViewModel();

    public MainWindow()
    {
        InitializeComponent();
    }

    public MainViewModel ViewModel { get { return this._viewModel; } }
}

<Window x:Class="WpfApplication3.View.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:WpfApplication3.View.Controls"
    Title="MainWindow" Height="350" Width="525"
    DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
    <TabControl>
        <TabItem Header="Tab 1">
            <controls:ChildViewA DataContext="{Binding ViewModel.ChildViewModelA}"/>
        </TabItem>
        <TabItem Header="Tab 2">
            <controls:ChildViewB DataContext="{Binding ViewModel.ChildViewModelB}"/>
        </TabItem>
    </TabControl>
</Grid>
</Window>

为了防止每个ViewModel从数据库中检索相同的数据,我想在MainViewModel中加载数据并提供子项。但是,有多种方法可以做到这一点。

示例1:对孩子使用setter方法

public class MainViewModel : ViewModelBase
{
    ...
    private readonly FakeDataManager _fakeDataManager = new FakeDataManager();
    public MainViewModel()
    {
        this.CurrentPerson = _fakeDataManager.GetNextPerson();
    }

    private Person _currentPerson;

    public Person CurrentPerson
    {
        get { return this._currentPerson; }
        set
        {
            this._currentPerson = value;
            this.RaisePropertyChanged("CurrentPerson");

            this.ChildViewModelA.SetPerson(this.CurrentPerson);
            this.ChildViewModelB.SetPerson(this.CurrentPerson);
        }
    }


public class ChildViewModelA : ViewModelBase
{
    private Person _currentPerson;

    public Person CurrentPerson
    {
        get { return this._currentPerson; }
        set
        {
            this._currentPerson = value;
            this.RaisePropertyChanged("CurrentPerson");
        }
    }
}

易于实施,但很快就难以保留。没有很多代码重用。没有松耦合。不应该用这个。

示例2:将数据放入容器

public class MainViewDataContainer : INotifyPropertyChanged
{
    private Person _currentPerson;

    public Person CurrentPerson
    {
        get { return this._currentPerson; }
        set
        {
            this._currentPerson = value;
            this.RaisePropertyChanged("CurrentPerson");
        }
    }
}

public class MainViewModel : ViewModelBase
{
    ...
    private readonly FakeDataManager _fakeDataManager = new FakeDataManager();
    private readonly MainViewDataContainer _dataContainer = new MainViewDataContainer();

    public MainViewModel()
    {
        this._childViewModelA = new ChildViewModelA(_dataContainer);
        this._childViewModelB = new ChildViewModelB(_dataContainer);

        this._dataContainer.CurrentPerson = _fakeDataManager.GetNextPerson();
    }

public class ChildViewModelA : ViewModelBase
{
    private readonly MainViewDataContainer _dataContainer;

    public ChildViewModelA(MainViewDataContainer dataContainer)
    {
        this._dataContainer = dataContainer;
    }

    public MainViewDataContainer DataContainer { get { return this._dataContainer; } }
}

易于维护,更多代码重用。松耦合。

示例3:存储在MainViewModel属性中并通过接口提供给子项

public interface IMainDataProvider
{
    Person CurrentPerson { get; }
}

public class MainViewModel : ViewModelBase, IMainDataProvider
{
    private readonly ChildViewModelA _childViewModelA;
    private readonly ChildViewModelB _childViewModelB;

    private readonly FakeDataManager _fakeDataManager = new FakeDataManager();

    public MainViewModel()
    {
        this._childViewModelA = new ChildViewModelA(this);
        this._childViewModelB = new ChildViewModelB(this);

        this.CurrentPerson = _fakeDataManager.GetNextPerson();
    }

    private Person _currentPerson;

    public Person CurrentPerson
    {
        get { return this._currentPerson; }
        set
        {
            this._currentPerson = value;
            this.RaisePropertyChanged("CurrentPerson");
        }
    }
}

public class ChildViewModelA : ViewModelBase
{
    private readonly IMainDataProvider _dataProvider;

    public ChildViewModelA(IMainDataProvider dataProvider)
    {
        this._dataProvider = dataProvider;
    }

    public IMainDataProvider DataProvider { get { return this._dataProvider; } }
}

再次更容易维护,更多代码重用。松耦合。

示例3似乎是最好的解决方案,但是这是真的吗?你觉得这个怎么样?有没有更好的方法来解决这个问题?

1 个答案:

答案 0 :(得分:0)

如果ChildViewModels和CurrentPerson都使用MainView,则可以更改CurrentPerson。我将如何解决这个问题:

  1. 使用ChildViewModelBase : ViewModelBase属性定义一个基本VM CurrentPerson(使用INotifyPropertyChanged和all)。我的所有ChildViewModels都会扩展此虚拟机。

  2. 使用事件聚合器http://msdn.microsoft.com/en-us/library/ff921122.aspx。使用EventAggregator,您可以在CurrentPersonChanged订阅ChildViewModelBase个活动。

  3. MainViewModel一旦从db加载CurrentPerson,请以新人作为事件争论提升CurrentPersonChanged事件。

  4. ChildViewModelBase的事件处理程序中,设置您获得的CurrentPerson作为事件参数。

  5. 通过这种方式,从{DB}加载ChildViewModels的{​​{1}}对您来说无关紧要。今天它是Person,tommorow它也可以是其他视图。您只需要将person对象作为参数引发上述事件,并且子视图将获得它。