通常在我设计MVVM应用程序时,会出现以下情况。一个窗口,有多个孩子,共享相同的数据源。
我正在尝试确定为所有孩子实施单个数据源的最佳方法。我能想到的有三种选择,都有各自的优缺点。
Window有两个子UserControl,每个都在自己的标签中。
用户界面是这样链接的。
为了保持模块化并为其提供数据,ViewModels中反映了相同的设计。
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中加载数据并提供子项。但是,有多种方法可以做到这一点。
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");
}
}
}
易于实施,但很快就难以保留。没有很多代码重用。没有松耦合。不应该用这个。
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; } }
}
易于维护,更多代码重用。松耦合。
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似乎是最好的解决方案,但是这是真的吗?你觉得这个怎么样?有没有更好的方法来解决这个问题?
答案 0 :(得分:0)
如果ChildViewModels和CurrentPerson
都使用MainView
,则可以更改CurrentPerson。我将如何解决这个问题:
使用ChildViewModelBase : ViewModelBase
属性定义一个基本VM CurrentPerson
(使用INotifyPropertyChanged和all)。我的所有ChildViewModels
都会扩展此虚拟机。
使用事件聚合器http://msdn.microsoft.com/en-us/library/ff921122.aspx。使用EventAggregator
,您可以在CurrentPersonChanged
订阅ChildViewModelBase
个活动。
从MainViewModel
一旦从db加载CurrentPerson
,请以新人作为事件争论提升CurrentPersonChanged
事件。
在ChildViewModelBase
的事件处理程序中,设置您获得的CurrentPerson
作为事件参数。
通过这种方式,从{DB}加载ChildViewModels
的{{1}}对您来说无关紧要。今天它是Person
,tommorow它也可以是其他视图。您只需要将person对象作为参数引发上述事件,并且子视图将获得它。