无法在MVVM

时间:2016-05-08 18:23:36

标签: c# wpf xaml mvvm user-controls

我在mvvm

中遇到了中介模式的问题

我会描述几乎所有课程,以便更好地理解我的问题。

  1. 我得到了MainWindow和ViewModel,它非常简单,只是拿着我的一个UserControls,但是在ViewModel中有一个绑定到MainWindow中的ContentControl.Content的UserControl属性。

  2. UserControls相同,每个只有一个按钮, 另外还有两个带有处理clikcs命令的ViewModel。

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

  4. 所以我要做的是在UserControls之间切换,而不是在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之外的所有内容。

    这个问题的可能解决方案是什么,我应该创建另一个单一类并收集所有userControls引用并在UserControlsViewModels中使用它们,或者其他什么东西?

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

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

    哦,这不是真正的应用程序,我只想获得ViewModel之间的消息系统的想法(概念),而不是混合ViewModel而不是在其他ViewModel中创建Views和他们的ViewModel ...

    再次感谢!

    的MainView

    <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>
    

    MainView ViewModel

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

    }

    SecondUserControl

    <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 ViewModel

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

    }

    App起点

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

1 个答案:

答案 0 :(得分:2)

如果我理解正确,您希望在当前活动视图模型上发生某个操作时(例如,您按一个按钮)导航到另一个视图(或分别是视图模型)。

如果你想使用你的中介,你可以像这样构建它:

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应用程序,例如:使用数据模板来实例化视图模型的视图 - 但我认为这对你的小例子来说有点过于紧张。