C# MVVM+中介模式:中介注册太晚

C# MVVM+中介模式:中介注册太晚,c#,wpf,mvvm,mediator,C#,Wpf,Mvvm,Mediator,我已经尝试在WPF/MVVM应用程序中实现中介模式,以使ViewModels之间的通信成为可能 为了应用中介模式,我从下载了一个示例项目。然后我从样本中学习,然后我申请了我的样本项目 我在使用这种模式时遇到了一些问题,这反过来会产生荒谬的输出 让我从我的代码开始: 以下是我的项目结构: 代码: 我将发布ViewModels和Models的代码,以缩短问题的长度 MenuItem.cs MainWindowViewModel.cs ParentMenuViewModel.cs 样本: 您可以下载我

我已经尝试在WPF/MVVM应用程序中实现中介模式,以使ViewModels之间的通信成为可能

为了应用中介模式,我从下载了一个示例项目。然后我从样本中学习,然后我申请了我的样本项目

我在使用这种模式时遇到了一些问题,这反过来会产生荒谬的输出

让我从我的代码开始:

以下是我的项目结构:

代码:

我将发布ViewModels和Models的代码,以缩短问题的长度

MenuItem.cs

MainWindowViewModel.cs

ParentMenuViewModel.cs

样本:

您可以下载我创建的示例项目

问题:

请点击上面提到的链接下载示例项目,以清楚地了解我的问题

运行应用程序。 正如您可能期望ChildMenuView显示某些项目一样,它最初不会显示任何内容。我认为出现这个问题是因为ParentMenuView在ChildMenuView自身注册之前通知selectedParentMenuItem已更改。 当您选择任何其他ParentMenuItem时,ChildMenuView将获得一些数据并正确显示。 单击任何子菜单项,您可能会看到加载的页面和框架上的一些文本。但这并没有显示任何东西。这里我也想到了我在第二步提到的同样的问题。 单击任何其他子菜单项。这个时间段应该显示一些数据,然后应用程序按预期工作。
所以,我的问题是,如何在另一个属性调用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
    }
}