C# MVVM+中介模式:中介注册太晚
我已经尝试在WPF/MVVM应用程序中实现中介模式,以使ViewModels之间的通信成为可能 为了应用中介模式,我从下载了一个示例项目。然后我从样本中学习,然后我申请了我的样本项目 我在使用这种模式时遇到了一些问题,这反过来会产生荒谬的输出 让我从我的代码开始: 以下是我的项目结构: 代码: 我将发布ViewModels和Models的代码,以缩短问题的长度 MenuItem.cs MainWindowViewModel.cs ParentMenuViewModel.cs 样本: 您可以下载我创建的示例项目 问题: 请点击上面提到的链接下载示例项目,以清楚地了解我的问题 运行应用程序。 正如您可能期望ChildMenuView显示某些项目一样,它最初不会显示任何内容。我认为出现这个问题是因为ParentMenuView在ChildMenuView自身注册之前通知selectedParentMenuItem已更改。 当您选择任何其他ParentMenuItem时,ChildMenuView将获得一些数据并正确显示。 单击任何子菜单项,您可能会看到加载的页面和框架上的一些文本。但这并没有显示任何东西。这里我也想到了我在第二步提到的同样的问题。 单击任何其他子菜单项。这个时间段应该显示一些数据,然后应用程序按预期工作。C# MVVM+中介模式:中介注册太晚,c#,wpf,mvvm,mediator,C#,Wpf,Mvvm,Mediator,我已经尝试在WPF/MVVM应用程序中实现中介模式,以使ViewModels之间的通信成为可能 为了应用中介模式,我从下载了一个示例项目。然后我从样本中学习,然后我申请了我的样本项目 我在使用这种模式时遇到了一些问题,这反过来会产生荒谬的输出 让我从我的代码开始: 以下是我的项目结构: 代码: 我将发布ViewModels和Models的代码,以缩短问题的长度 MenuItem.cs MainWindowViewModel.cs ParentMenuViewModel.cs 样本: 您可以下载我
所以,我的问题是,如何在另一个属性调用NotifyCollaborates后通知注册自身的属性?查找我的应用程序更新版本 对我来说,中介模式只是一种不必正确构造代码的方式,我从未在实际代码场景中使用过它。您的演示应用程序是在ViewModel上创建子模型集合(例如ParentMenuViewModel上的ObservableCollection)的主要示例。相比之下,从尚未存在的子ViewModel监视父ViewModel上的属性似乎是自食其果。它不是一个很好的层次结构,而是每个人广播的杂音 如果您真的想保持这种模式,那么您需要确保您的对象已注册到中介,正如您在问题中已经注意到的那样,然后才应该捕获中介通知 对于父/子菜单,这很简单,只需重新排列MainWindow.xaml:
<Grid Grid.Row="1">
<!-- ColumnDefinitions omitted -->
<views:ChildMenuView Grid.Column="0" />
<Frame Grid.Column="1" NavigationUIVisibility="Hidden" Content="{Binding SourcePage}"/>
</Grid>
<views:ParentMenuView Grid.Row="0" />
你的问题太长了,很可能得不到当前形式的答案。尽量总结并只包括相关信息。如果你的问题需要1到2分钟的时间,没有人会读你的问题……看起来项目有点太复杂了,我想知道,当你有绑定时,为什么你需要一个自定义的中介体?@pushpraj你能进一步解释一下吗?wpf的绑定以松散耦合的方式为对象之间的通信提供了一种机制。调解人的主要目的也是一样的。因此,我关心的是了解在wpf中引入调解人的目的。因为它引入了代码复制和额外的同步。如果有必要,你可以选择代替调解人。@pushpraj,但我还是不明白你的意思。如何使用绑定在对象之间进行通信?如果你有时间,那么请参考我的样本项目,这将使我有一些意义。优秀的东西!它现在可以正常工作了。我也在我的真实项目上进行了测试。到目前为止,它运行良好。我已经接受了答案,并将根据stackoverflow.com的规则在24小时后奖励奖金。如果你要创建类似的项目,那么你会选择什么?我猜不是调解人。你能提供一个你将如何创建它的例子吗?因为你问得很好。唯一有点棘手的是将DataContext传递到框架内容中。请注意,唯一剩下的真正代码是从xml加载菜单一次,而不是每次选择更改。水平/垂直菜单控件替换子菜单视图/父菜单视图我尽量不在一个控件上有多个视图,而是在一个视图上有多个控件。这有帮助吗,你有反对意见吗?
public class MenuItem
{
public int Id { get; set; }
public string Name { get; set; }
}
public class MainWindowViewModel : ViewModelBase
{
public MainWindowViewModel()
{
Mediator.Register(this, new[] { Messages.SelectedParentMenuItem, Messages.SelectedChildMenuItem });
}
private string _sourcePage;
public string SourcePage
{
get
{
return _sourcePage;
}
set
{
_sourcePage = value;
NotifyPropertyChanged("SourcePage");
}
}
private MenuItem _currentParentMenuItem;
public MenuItem CurrentParentMenuItem
{
get
{
return _currentParentMenuItem;
}
set
{
_currentParentMenuItem = value;
NotifyPropertyChanged("CurrentParentMenuItem");
}
}
private MenuItem _currentChildMenuItem;
public MenuItem CurrentChildMenuItem
{
get
{
return _currentChildMenuItem;
}
set
{
_currentChildMenuItem = value;
NotifyPropertyChanged("CurrentChildMenuItem");
if (CurrentChildMenuItem != null)
{
SourcePage = (from menuItem in XDocument.Load(Messages.DataDirectory + "MenuItems.xml")
.Element("MenuItems").Elements("MenuItem").Elements("MenuItem")
where (int)menuItem.Parent.Attribute("Id") == CurrentParentMenuItem.Id &&
(int)menuItem.Attribute("Id") == CurrentChildMenuItem.Id
select menuItem.Element("SourcePage").Value).FirstOrDefault();
}
}
}
public override void MessageNotification(string message, object args)
{
switch (message)
{
case Messages.SelectedParentMenuItem:
CurrentParentMenuItem = (MenuItem)args;
break;
case Messages.SelectedChildMenuItem:
CurrentChildMenuItem = (MenuItem)args;
break;
}
}
}
public class ParentMenuViewModel : ViewModelBase
{
public ParentMenuViewModel()
{
ParentMenuItems = new ObservableCollection<MenuItem>(
from menuItem in XDocument.Load(Messages.DataDirectory + "MenuItems.xml")
.Element("MenuItems").Elements("MenuItem")
select new MenuItem
{
Id = Convert.ToInt32(menuItem.Attribute("Id").Value),
Name = menuItem.Element("Name").Value
}
);
}
private ObservableCollection<MenuItem> _parentMenuItems;
public ObservableCollection<MenuItem> ParentMenuItems
{
get
{
return _parentMenuItems;
}
set
{
_parentMenuItems = value;
NotifyPropertyChanged("ParentMenuItems");
}
}
private MenuItem _selectedParentMenuItem;
public MenuItem SelectedParentMenuItem
{
get
{
return _selectedParentMenuItem;
}
set
{
_selectedParentMenuItem = value;
NotifyPropertyChanged("SelectedParentMenuItem");
Mediator.NotifyColleagues(Messages.SelectedParentMenuItem, SelectedParentMenuItem);
}
}
public override void MessageNotification(string message, object args)
{
throw new NotImplementedException();
}
}
public class ChildMenuViewModel : ViewModelBase
{
public ChildMenuViewModel()
{
Mediator.Register(this, new[] { Messages.SelectedParentMenuItem });
}
private MenuItem _currentParentMenuItem;
public MenuItem CurrentParentMenuItem
{
get
{
return _currentParentMenuItem;
}
set
{
_currentParentMenuItem = value;
NotifyPropertyChanged("CurrentParentMenuItem");
ChildMenuItemsOfSelectedParent
= new ObservableCollection<MenuItem>(
from menuItem in XDocument.Load(Messages.DataDirectory + "MenuItems.xml")
.Element("MenuItems").Elements("MenuItem").Elements("MenuItem")
where (int)menuItem.Parent.Attribute("Id") == CurrentParentMenuItem.Id
select new MenuItem
{
Id = Convert.ToInt32(menuItem.Attribute("Id").Value),
Name = menuItem.Element("Name").Value,
}
);
}
}
private ObservableCollection<MenuItem> _childMenuItemsOfSelectedParent;
public ObservableCollection<MenuItem> ChildMenuItemsOfSelectedParent
{
get
{
return _childMenuItemsOfSelectedParent;
}
set
{
_childMenuItemsOfSelectedParent = value;
NotifyPropertyChanged("ChildMenuItemsOfSelectedParent");
}
}
private MenuItem _selectedChildMenuItem;
public MenuItem SelectedChildMenuItem
{
get
{
return _selectedChildMenuItem;
}
set
{
_selectedChildMenuItem = value;
NotifyPropertyChanged("SelectedChildMenuItem");
Mediator.NotifyColleagues(Messages.SelectedChildMenuItem, SelectedChildMenuItem);
}
}
public override void MessageNotification(string message, object args)
{
switch (message)
{
case Messages.SelectedParentMenuItem:
CurrentParentMenuItem = (MenuItem)args;
break;
}
}
}
public class SamplePageViewModel : ViewModelBase
{
public SamplePageViewModel()
{
Mediator.Register(this, new[] { Messages.SelectedChildMenuItem });
}
private MenuItem _currentChildMenuItem;
public MenuItem CurrentChildMenuItem
{
get
{
return _currentChildMenuItem;
}
set
{
_currentChildMenuItem = value;
NotifyPropertyChanged("CurrentChildMenuItem");
}
}
public override void MessageNotification(string message, object args)
{
switch (message)
{
case Messages.SelectedChildMenuItem:
CurrentChildMenuItem = (MenuItem)args;
break;
}
}
<Grid Grid.Row="1">
<!-- ColumnDefinitions omitted -->
<views:ChildMenuView Grid.Column="0" />
<Frame Grid.Column="1" NavigationUIVisibility="Hidden" Content="{Binding SourcePage}"/>
</Grid>
<views:ParentMenuView Grid.Row="0" />
private MenuItem _selectedChildMenuItem;
public MenuItem SelectedChildMenuItem
{
get { return _selectedChildMenuItem; }
set
{
_selectedChildMenuItem = value;
NotifyPropertyChanged("SelectedChildMenuItem");
LoadSourcePage(); // first instantiate the page (register it to mediator)
Mediator.NotifyColleagues(Messages.SelectedChildMenuItem, SelectedChildMenuItem); // only now notify
}
}
/// <summary>
/// Get the SourcePage and pass it to MainWindowViewModel
/// </summary>
private void LoadSourcePage()
{
if (SelectedChildMenuItem != null)
{
var sourceUri = (from menuItem in XDocument.Load(Messages.DataDirectory + "MenuItems.xml")
.Element("MenuItems").Elements("MenuItem").Elements("MenuItem")
where (int)menuItem.Parent.Attribute("Id") == CurrentParentMenuItem.Id &&
(int)menuItem.Attribute("Id") == SelectedChildMenuItem.Id
select menuItem.Element("SourcePage").Value).FirstOrDefault();
var relativePart = sourceUri.Substring(sourceUri.IndexOf(",,,") + 3);
var sourcePage = System.Windows.Application.LoadComponent(new Uri(relativePart, UriKind.Relative)); // instantiation with URI
Mediator.NotifyColleagues(Messages.SourcePage, sourcePage); // pass on
}
}