无法在 MVVM 中完成模式中介

Can't complete pattern mediator in MVVM

我在 mvvm 中遇到中介模式问题

为了更好地理解我的问题,我描述了几乎所有 class。

  1. 我有 MainWindow 和 ViewModel,它非常简单,实际上除了持有我的一个 UserControl 什么都不做,ViewModel 中有一个 UserControl 属性 绑定到ContentControl.Content 在主窗口中。

  2. 用户控件是相同的,每个控件只有一个按钮, 还有两个 ViewModels 带有处理 clikcs 的命令。

  3. Class Mediator 是单调的,我试图将它用于我的 ViewModel

  4. 之间的迭代

所以我想做的是在 UserControl 之间切换,而不是在 MainWindowViewModel 中创建它们和它们的 ViewModel。单击按钮后必须进行切换。例如,如果我单击 FirstUserControl 上的按钮,则 MainWindow 的 ContentControl 应切换到 SecondUserControl。

问题出现在 UserControlsViewModels 中,我应该将 UserControls 对象作为参数传递给 Mediator NotifyCollegue() 函数,但我无法访问它们 (当然,这是MVVM的原则之一),这是用户类型的问题,因为标准类型应该不是问题(例如传递int或string ...)。

我在这里找到了这个解决方案 http://www.codeproject.com/Articles/35277/MVVM-Mediator-Pattern

以及为什么我不能在 MainWindowViewModel 中切换 UserControls,因为我希望 MainWindow 清除除绑定到 ContentControl 的当前 UserControl 之外的所有内容。

这个问题可能的解决方案是什么,我应该制作另一个单例 class 并在那里收集所有 userControls 引用并在 UserControlsViewModels 中使用它们,还是其他什么?

我希望我已经清楚地描述了我的问题,并且有某种解决方案。

我很乐意回答任何问题,非常感谢您的帮助!!!

哦,那不是真正的应用程序,我只是想了解 ViewModel 之间的消息传递系统的想法(概念),而不是混合 ViewModel,而不是在其他 ViewModel 中创建 View 和它们的 ViewModel...

再次感谢!

主视图

<Window x:Class="TESTPROJECT.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:TESTPROJECT"
    mc:Ignorable="d"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    Title="MainWindow" Height="500" Width="750">



<Grid>
    <ContentControl Grid.Row="1" Content="{Binding PagesControl}"/>
</Grid>

主视图视图模型

namespace TESTPROJECT
{
    class MainWindowViewModel : ViewModelBase
    {
        private UserControl _pagesControl;
        public UserControl PagesControl
        {
            //Property that switches UserControls
            set
            {
                _pagesControl = value;
                OnPropertyChanged();
            }
            get
            {
                return _pagesControl;
            }
        }

        public MainWindowViewModel()
        {
            //Method that will be listening all the changes from UserControls ViewModels
            Mediator.Instance.Register(
                (object obj) =>
                {
                    PagesControl = obj as UserControl;
                }, ViewModelMessages.UserWroteSomething);
        }
    }
}

第一个用户控件

<UserControl x:Class="TESTPROJECT.FirstUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:TESTPROJECT"
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
    <Button Command="{Binding GetCommand}">
        hello, i'm first user control!
    </Button>
</Grid>

FirstUserControl ViewModel

namespace TESTPROJECT
{
class FirstUserControlViewModel : ViewModelBase
{
    //command that is binded to button
    private DelegateCommand getCommand;
    public ICommand GetCommand
    {
        get
        {
            if (getCommand == null)
                getCommand = new DelegateCommand(param => this.func(param), null);
            return getCommand;
        }
    }
    //method that will handle button click, and in it i'm sending a message
    //to MainWindowViewModel throug Mediator class 
    //and that is allso a problem place because in theory i should
    //pass the opposite UserControl object , but from here i have no 
    //acces to it
    private void func(object obj)
    {
        Mediator.Instance.NotifyColleagues(
            ViewModelMessages.UserWroteSomething,
            "PROBLEM PLACE");
    }
}

}

第二个用户控件

<UserControl x:Class="TESTPROJECT.SecondUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:TESTPROJECT"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
    <Button Command="{Binding GetCommand}">
        Hello, i'm second user control!
    </Button>
</Grid>

第二个用户控件视图模型

namespace TESTPROJECT
{
class SecondUserControlViewModel : ViewModelBase
{
    //command that is binded to button
    private DelegateCommand getCommand;
    public ICommand GetCommand
    {
        get
        {
            if (getCommand == null)
                getCommand = new DelegateCommand(param => this.func(param), null);
            return getCommand;
        }
    }
    //method that will handle button click, and in it i'm sending a message
    //to MainWindowViewModel throug Mediator class 
    //and that is allso a problem place because in theory i should
    //pass the opposite UserControl object , but from here i have no 
    //acces to it
    private void func(object obj)
    {
        Mediator.Instance.NotifyColleagues(
            ViewModelMessages.UserWroteSomething,
            "PROBLEM PLACE");
    }
}

}

Class调解员 和 枚举 ViewModelMessages

namespace TESTPROJECT
{
//this enum holding some kind of event names fro example UserWroteSomething
// is a name of switching one UserControl to another
public enum ViewModelMessages { UserWroteSomething = 1 };

class Mediator
{
    //Singletone part
    private static Mediator instance;
    public static Mediator Instance
    {
        get
        {
            if (instance == null)
                instance = new Mediator();
            return instance;
        }
    }
    private Mediator() { }
    //Singletone part

    //collection listeners that holds event names and handler functions
    List<KeyValuePair<ViewModelMessages, Action<Object>>> internalList = 
        new List<KeyValuePair<ViewModelMessages, Action<Object>>>();


    //new listener registration
    public void Register(Action<object> callBack, ViewModelMessages message)
    {
        internalList.Add(
            new KeyValuePair<ViewModelMessages, Action<Object>>(message, callBack));
    }

    // notifying all the listener about some changes
    // and those whose names fits will react
    public void NotifyColleagues(ViewModelMessages message, object args)
    {
        foreach(KeyValuePair<ViewModelMessages, Action<Object>> KwP in internalList)
            if(KwP.Key == message)
                KwP.Value(args);
    }
}

}

应用起点

    public partial class App : Application
{
    private void Application_Startup(object sender, StartupEventArgs e)
    {
        FirstUserControl first = new FirstUserControl() { DataContext = new FirstUserControlViewModel() };
        SecondUserControl second = new SecondUserControl() { DataContext = new SecondUserControlViewModel() };

        new MainWindow()
        {
            DataContext = new MainWindowViewModel() { PagesControl = first }
        }.ShowDialog();
    }
}

如果我对您的理解正确,您想在当前活动视图模型上发生特定操作(例如,您按下按钮)时导航到另一个视图(或相应的视图模型)。

如果你想为此使用调解器,你可以这样构造它:

public class Mediator
{
    // These fields should be set via Constructor Injection
    private readonly MainWindowViewModel mainWindowViewModel;
    private readonly Dictionary<ViewModelId, IViewFactory> viewFactories;        

    public void NotifyColleagues(ViewModelId targetViewModelId, ViewModelArguments arguments)
    {
        var targetFactory = this.viewModelFactories[targetViewModelId];
        var view = targetFactory.Create(viewModelArguments);
        this.mainWindowViewModel.PagesControl = view;
    }

    // other members omitted to keep the example small
}

然后您将为每个视图-视图模型组合创建一个工厂。使用 ViewModelArguments,您可以将信息传递到源自其他视图模型的新创建的视图模型。 ViewModelId 可以像您的 ViewModelMessage 一样是一个简单的枚举,您也可以使用视图模型的 Type(我建议您使用)。

此外,我建议您不要在 Mediator class 上使用私有构造函数,否则您无法传入 mainWindowViewModel 和视图工厂的字典。您应该能够在您的应用程序启动方法中配置它。

此外,请注意还有许多其他方法可以构建 MVVM 应用程序,例如使用数据模板来实例化视图模型的视图 - 但我认为这对于您的小示例来说有点过分了。