MVVM违规

时间:2014-10-09 06:55:44

标签: c# wpf design-patterns mvvm

我想澄清一些关于MVVM违规的问题。因此,我已经创建了一个解决方案,其中包含一些项目来演示案例。 这是解决方案的定义(项目):

  1. 查看(它是一个WPF类Libraray,显然它有视图)
  2. ViewModel(它是一个类Libraray,显然它有视图模型)
  3. 模型(它是一个类Libraray,显然它有模型)
  4. Domain(它是一个类Libraray,它有应用程序dataModels)
  5. Core(它是一个类Libraray,它的核心是像RelayCommnd或EventToCommand一样的wpf)
  6. 应用程序(它是一个wpf应用程序和启动项目)
  7. ExternalCustomControl(由假想的第三方公司创建的wpf自定义控件库)
  8. 我建议您下载整个解决方案以便更好地了解 Here

    第一期: 我在 MainWindow.xaml 中为关闭窗口中的 EventToCommand 并将其附加到 MainWindowClosingCommand PassEventArgsToCommand 设置为True,然后在 MainViewModel 中为名为 OnMainWindowClosing

    的命令提供处理程序
    private void OnMainWindowClosing(object parameter)
    {
      var arg = parameter as CancelEventArgs;
    
      // What is the best way to show message dialog to user?
      // Do i have to send message to the View to show the messageBox dialog and then the window send me the answer back to continue?
      // What about IMessageBoxService? Doesn't it violates MVVM?
    
      // Doesn't following code violates the MVVM? 
      // Cancel the Closing of a Window isnt a UI side duty?
      arg.Cancel = true;
    
    }
    

    并且只要您想设置 e.Handled e.Cancel ,您就会遇到这个问题。所以您知道其他任何不需要的方式吗?将参数转换为 CancelEventArgs

    第二期: 我在网格 PreviewMouseDown MainWindow.xaml 中有 EventToCommand ,并将其附加到 MouseClickCommand PassEventArgsToCommand 设置为True,然后在 MainViewModel 中为名为 OnMouseClick 的命令提供处理程序:

     private void OnMouseClick(object parameter)
    {
      //      var arg = parameter as MouseButtonEventArgs; 
    
      // This is the violation of MVVM : To cast the parameter to MouseButtonEventArgs i have to add a refrence  
      //                                 to PresentationCore.dll in the ViewModel Project  
    
      // The next and worse step is that in most cases we need to know the Original Source of the event 
      //    (maybe its a StackPanel or a Label or etc) and this again vioaltes the MVVM
    
      // So Whats the WorkAround?
    
    }
    

    第三期: 我在我的 MainWindow 中使用了ThirdParty控件(Imagine Infragistics或DevExpress或任何其他第三方控件,但这里作为一个例子,我在我的解决方案中创建了虚拟控件作为ExternalCustomControl项目)像这样:

        <thirdParty:ThirdPartyCustomControl Grid.Row="1"
                                        ItemsSource="{Binding MyItemsSource,Converter={StaticResource converterKey}}" />
    

    ThirdPartyCustomControl 具有类型IEnumarabe<CustomControlDataModel>的属性(CustomControlDataModel是ExternalCustomControl程序集中存在的类型)但是如您所知,是否要在 MainViewModel中创建属性对于具有CustomControlDataModel类型的控件,您必须在 ViewModel 项目中添加对 ExternalCustomControl.dll 的引用,这违反了MVVM,因此我创建了一个名为 MyDataModel 并将控件的ItemsSource绑定到 MainViewModel 中的 MyItemsSource 属性:

        // If i define MyItemsSource as List<CustomControlDataModel> i have to add a refrence to ExternalCustomControl.dll
    // and i think its again violate the MVVM (because ExternalCustomControl.dll is a UI Side Controls Assembly) 
    public List<MyDataModel> MyItemsSource { get; set; }
    

    所以我将 CustomControlDataModel 类型的属性绑定到类型为 MyDataModel 的属性,当然我需要一个转换器:

    public object Convert(object value, Type targetType, object parameter, c     System.Globalization.CultureInfo culture)
    {
      // Imagine when the source data (MyDataModel) is huge (for example 1 milion) it (this dummy Conversion)
      // affects the performance
    
      if (value is List<MyDataModel>)
      {
        var result = new List<CustomControlDataModel>();
    
        (value as List<MyDataModel>).ForEach(myVal =>
          {
            var custDataModel = new CustomControlDataModel();
            custDataModel.ID = myVal.ID;
            custDataModel.Name = myVal.Name;
            custDataModel.Age = myVal.Age;
    
            result.Add(custDataModel);
          });
    
        return result;
      }
      return value;
    }
    

    问题是你知道比这个虚拟转换更好的方法,或者你通常将你的第三方程序集添加到你的视图和viewmodel中吗?

    这些是我所面临的问题,如果您了解其他问题并向所有人分享您的专业知识,那么我会感激不尽。

    UPADTE

    对于 MessageBox 第一期的部分内容,我建议使用此链接 MesageBox

    感谢。

3 个答案:

答案 0 :(得分:1)

优秀的问题!

1)我个人认为你是对的,使用服务违反了MVVM。几周前我写了一篇关于这个确切主题的非常冗长的文章,名为Implementing Dialog Boxes in MVVM。在那篇文章中,我为MVVM对话框的整体问题提供了一个“纯粹”的解决方案,但是我花了11页来解释我是如何得出这个设计的。幸运的是,实际的实现非常简单,类似于数据模板,支持您指定的多项目设计,并且它适用于第三方库。阅读,我总是欣赏客观反馈。

2)如果您正在使用MVVM Lite,则EventToCommand允许您指定参数转换器。这是一个例子,我用它将窗口鼠标移动消息参数转换为我的视图模型中的等效表示:

<i:EventTrigger EventName="MouseMove">
    <cmd:EventToCommand Command="{Binding ElementName=_this, Mode=OneWay, Path=MouseMoveCommand}" PassEventArgsToCommand="True" EventArgsConverter="{StaticResource MouseEventArgsConverter}" />
</i:EventTrigger>

3)如果我正确理解你的问题,我添加对视图和视图模型项目的引用,至少在我的项目结构时。说实话虽然我通常会在同一个项目中放置我的视图并查看模型,例如MyProject.UI,按类别文件夹排序。我在为一家大型国际公司工作的合同中看到了这一点,并且在实践中它非常有效,因为您通常会同时编辑视图和相应的视图模型。在解决方案窗口中并排使用它们确实使整个开发过程更加容易。显然,一些纯粹主义者并不喜欢它,但我个人并不认为仅仅将它们放在同一个项目中会破坏MVVM,前提是你仍然严格遵守该架构。我也从来没有在单元测试等方面产生任何问题,只需要创建视图模型。

答案 1 :(得分:0)

我的结束代码看起来像这样,我不认为这违反了mvvm

XAML

<Window>
 <i:Interaction.Triggers>
    <i:EventTrigger EventName="Closing">
        <cmd:EventToCommand Command="{Binding ClosingCommand}" PassEventArgsToCommand="True" />
    </i:EventTrigger>
  </i:Interaction.Triggers>

mainviewmodel.cs

    public ICommand ClosingCommand
    {
        get
        {
            return this._closingCommand ?? (this._closingCommand = new DelegateCommand<CancelEventArgs>((args) =>
                {
                    //i set a property in app.xaml.cs when i shut down the app there with
                    //Application.Current.Shutdown();
                    if (App.IsShutDown) return;


                if (this.HasChanges)
                {
                    var result = _msgService.Show("blup blup", "blup", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);

                    if (result == MessageBoxResult.No)
                    {
                        args.Cancel = true;
                    }
                }
                else
                {                      
                    var result = MessageBox.Show("Blup blup", "blup", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);

                    if (result == MessageBoxResult.No)
                    {
                        args.Cancel = true;
                    } 
                }                    
            }));
        }
    }

第三个问题(ThirdParty Control):如果控件需要一种集合,然后通过你的VM暴露这些合并,我就不会遇到你的问题。

第二个问题:很难说。我使用这样的东西,我会说是mvvm喜欢;)

 <DataGrid x:Name="myGrd">
   <i:Interaction.Triggers>
      <i:EventTrigger EventName="MouseDoubleClick">
         <Commanding:EventToCommand  Command="{Binding Path=OpenCommand}" 
                                     CommandParameter="{Binding ElementName=myGrd, Path=SelectedItem}"/>
      </i:EventTrigger>
    </i:Interaction.Triggers>

最后我做了mvvm,但总是记住做简单的事情

答案 2 :(得分:0)

对于你的第二个问题,我认为你只需要重新思考你想要实现的目标和原因。

如果您正在连接网格上的处理程序,然后根据该网格的特定UI元素点击代码做出决定,那么您可能做错了。名为Command的{​​{1}}也有点代码味道,因为该命令什么也没说。命令可能类似于OnMouseClickUserSelectedCommand ......也就是更具体而不仅仅是点击了某些内容&#39;。

你想试着打破那些&#39;某些东西&#39;在此时发出一个清晰明确的命令的逻辑,而不是尝试使用GrapefuitSliceRequestedCommand并在代码中决定这意味着什么 - 您的UI应该决定这意味着什么,并向您的VM发出命令。< / p>

因此,您的各个UI元素应该具有命令和绑定,而不是尝试在布局UI级别设置命令。如果单击MouseClickEventArgs表示某些特定内容,则单击Image的行表示特定内容,如果拖动DataGrid则表示特定内容。创建你的XAML,以便它触发那些特定的命令,并且不要将这个责任推到一个模糊的“我的整个ui被点击”,现在我将使用代码来找出究竟是什么&#39;思维方式。