【问题标题】:Can't complete pattern mediator in MVVM无法在 MVVM 中完成模式中介
【发布时间】:2016-05-08 18:23:36
【问题描述】:

我在 mvvm 中遇到了中介者模式的问题

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

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

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

  3. Class Mediator 是一个单例,我尝试将它用于我的 ViewModel 之间的迭代

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

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

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

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

对于这个问题有什么可能的解决方案,我是否应该创建另一个单例类并在那里收集所有 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);
        }
    }
}

FirstUserControl

<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 视图模型

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>

SecondUserControl 视图模型

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");
    }
}

}

类调解员 和 枚举 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();
    }
}

【问题讨论】:

    标签: c# wpf xaml mvvm user-controls


    【解决方案1】:

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

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

    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 类上使用私有构造函数,否则您无法传入 mainWindowViewModel 和视图工厂的字典。您应该可以在 Application-Startup 方法中进行配置。

    另外,请注意,还有许多其他方式来构建 MVVM 应用程序,例如使用数据模板来实例化视图模型的视图 - 但我认为这对于你的小例子来说有点过于紧张了。

    【讨论】:

    • 抱歉,我是编程和 wpf、mvvm 方面的新手。我只是不知道什么是“每个视图 - 视图模型组合的工厂”,你能解释一下,给一个链接吗?而且我也没有从你的例子中得到 viewModelFactories 是从哪里来的,你为什么把 MainWindowViewModel 放在 Mediator 类中?
    • 嗯,在面向对象编程中,我通常遵循一组称为 SOLID 原则的五项原则,这些原则往往会导致许多功能相对较小(适合解决一个特定问题)的类具有相互作用,形成一个有凝聚力的工作整体。这导致了一个名为“Composition Root”的构造,您可以在其中实例化所有必要的对象并将它们放在一起(将其想象成 LEGO)。查看我为我的 YouTube 频道制作的视频系列:youtube.com/…
    • 这就是为什么中介者有一个对 mainWindowViewModel 的引用的原因——后者持有并显示当前活动的视图,前者能够实例化视图和视图模型并从其他视图模型中传递参数.这些对象与工厂集合一起解决了“导航到另一个视图”的问题。
    • 工厂本身只是一个对象,当您调用它的 Create 方法(并传入我们想要的可选参数时,它可以创建另一个对象(在我们的例子中是带有视图模型的视图))案子)。对于视图 - 视图模型组合,我的意思是您有两个逻辑上链接在一起的类。如果你想展示一个新的视图,没有合适的视图模型就无法存在——因此工厂必须创建两个对象并将它们链接在一起,这样你的应用程序才能继续正常工作。
    • @feO2x,我遇到了同样的问题。您能否使用代码 ViewModelId、IViewFactory 和 ViewModelArguments 更详细地描述该决定?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多