我想澄清一些关于MVVM违规的问题。因此,我已经创建了一个解决方案,其中包含一些项目来演示案例。 这是解决方案的定义(项目):
我建议您下载整个解决方案以便更好地了解 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
感谢。
答案 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}}也有点代码味道,因为该命令什么也没说。命令可能类似于OnMouseClick
或UserSelectedCommand
......也就是更具体而不仅仅是点击了某些内容&#39;。
你想试着打破那些&#39;某些东西&#39;在此时发出一个清晰明确的命令的逻辑,而不是尝试使用GrapefuitSliceRequestedCommand
并在代码中决定这意味着什么 - 您的UI应该决定这意味着什么,并向您的VM发出命令。< / p>
因此,您的各个UI元素应该具有命令和绑定,而不是尝试在布局UI级别设置命令。如果单击MouseClickEventArgs
表示某些特定内容,则单击Image
的行表示特定内容,如果拖动DataGrid
则表示特定内容。创建你的XAML,以便它触发那些特定的命令,并且不要将这个责任推到一个模糊的“我的整个ui被点击”,现在我将使用代码来找出究竟是什么&#39;思维方式。