【问题标题】:Using Commands in MVVM在 MVVM 中使用命令
【发布时间】:2011-02-23 10:15:46
【问题描述】:

我一直在使用来自ApuntasNotas 的非常好的示例代码来了解有关如何有效使用MVVM Light Toolkit 的更多信息。

在代码中,在一个例子中,作者似乎利用后面的代码来设置 DataContext 以处理点击事件,我觉得这很混乱。

在 XAML 中,ContextMenu cm 中 MenuItem 的 EditNote_Click 事件处理程序在代码隐藏中处理:

    <Window x:Class="ApuntaNotas.MainWindow" Icon="Icons/app_48.ico"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Apunta Notas"
            Height="480"
            x:Name="Ventana"
            Width="640"
            Background="Beige"
            DataContext="{Binding Main, Source={StaticResource Locator}}">
.
.
.
<ScrollViewer VerticalScrollBarVisibility="Auto">
            <ItemsControl Background="Beige" Padding="15" Tag="Hello" x:Name="IC"
                      ItemsSource="{Binding Notes}">
                <ItemsControl.LayoutTransform>
                    <ScaleTransform ScaleX="{Binding Value, ElementName=zoomSlider}" ScaleY="{Binding Value, ElementName=zoomSlider}" />
                </ItemsControl.LayoutTransform>
                <ItemsControl.ContextMenu>
                    <ContextMenu Name="icCM">
                        <MenuItem Header="{Binding Source={StaticResource LocStrings}, Path=DeleteAllNotes}" Command="{Binding DeleteAllNotesCommand}" />
                    </ContextMenu>
                </ItemsControl.ContextMenu>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="auto" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <Grid.ContextMenu>
                                <ContextMenu Name="cm">
                                    <MenuItem Header="{Binding Source={StaticResource LocStrings}, Path=Edit}" Click="EditNote_Click"/>
                                    <MenuItem Header="{Binding Source={StaticResource LocStrings}, Path=Delete}" Click="DeleteNote_Click" />
                                    <Separator />
                                    <ComboBox Loaded="CmbNoteCategory_Loaded" SelectionChanged="CmbNoteCategory_SelectionChanged">
                                        <ComboBox.ItemTemplate>
                                            <DataTemplate>
                                                <TextBlock Text="{Binding Name}" />
                                            </DataTemplate>
                                        </ComboBox.ItemTemplate>
                                    </ComboBox>
                                </ContextMenu>
.
.
.

在代码隐藏中,EditNote_Click 处理程序定义如下:

private void EditNote_Click(object sender, RoutedEventArgs e)
        {
            var menuItem = e.Source as MenuItem;
            if (menuItem != null)
                ViewModel.EditNoteCommand.Execute(menuItem.DataContext as Model.Note);
        }

EditNoteCommand 具有以下签名:

public RelayCommand<Note> EditNoteCommand { get; private set; }

我的问题是,为什么作者不将 EditNoteCommand 命令(已经编写并可用)链接到 XAML 中 MenuItemCommand 属性?

例如,我尝试替换以下内容,它已编译,但产生了异常(如下所示)。我怀疑我的方法是合理的,但是我错过了将 DataContext 或其他东西传递给命令代码的一些东西。我将 DataContext Binding 重置为Main 以方便命令绑定:

<MenuItem Header="{Binding Source={StaticResource LocStrings}, Path=Edit}" DataContext="{Binding Main, Source={StaticResource Locator}}" Command="{Binding EditNoteCommand}"/>

此尝试生成以下引用 EditNote 内的 other 的异常 - 由 EditNoteCommand 调用的方法:

编辑注释:

private void EditNote(Note other)
        {
            ActualNote = other;
            SelectedCategory = other.Category;
        }

例外:

System.NullReferenceException was unhandled   Message=Object reference not set to an instance of an object.   Source=ApuntaNotas   StackTrace:
       at ApuntaNotas.ViewModel.MainViewModel.EditNote(Note other) in C:\Documents and Settings\wcatlan\My Documents\Visual Studio 2010\Projects\ApuntaNotas\trunk\ApuntaNotas\ViewModel\MainViewModel.cs:line 171
       at GalaSoft.MvvmLight.Command.RelayCommand`1.Execute(Object parameter)
       at MS.Internal.Commands.CommandHelpers.CriticalExecuteCommandSource(ICommandSource commandSource, Boolean userInitiated)
       at System.Windows.Controls.MenuItem.InvokeClickAfterRender(Object arg)
       at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
       at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
       at System.Windows.Threading.DispatcherOperation.InvokeImpl()
       at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
       at System.Threading.ExecutionContext.runTryCode(Object userData)
       at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Windows.Threading.DispatcherOperation.Invoke()
       at System.Windows.Threading.Dispatcher.ProcessQueue()
       at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
       at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
       at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
       at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
       at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
       at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
       at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
       at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
       at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
       at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
       at System.Windows.Threading.Dispatcher.Run()
       at System.Windows.Application.RunDispatcher(Object ignore)
       at System.Windows.Application.RunInternal(Window window)
       at System.Windows.Application.Run(Window window)
       at System.Windows.Application.Run()
       at ApuntaNotas.App.Main() in C:\Documents and Settings\wcatlan\My Documents\Visual Studio 2010\Projects\ApuntaNotas\trunk\ApuntaNotas\obj\Debug\App.g.cs:line 0
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart() InnerException:

【问题讨论】:

  • 我们可能不需要完整的堆栈跟踪。 :)
  • 我认为完整的堆栈跟踪没有问题。

标签: wpf mvvm mvvm-light


【解决方案1】:

除非需要,否则不要使用代码隐藏,这绝对是正确的做法 - 因此,请继续在 MVVM 中遵循该原则,您会做得很好。

我想你会发现这里的作者正在使用代码隐藏来解决使命令参数正确的问题。 对于编辑注释命令 - 正在编辑的注释的视图模型是必需的参数。这是他们在后面的代码中所做的 -

menuItem.DataContext as Model.Note

您遇到的问题是关于在同一命令中访问被单击的菜单项和“主”视图模型。

如果您要将 EditNoteCommand 移动到 NotesViewModel(或任何类别的 Notes)中,您可以将命令保留在 XAML 中,如下所示:

<MenuItem Header="{Binding Source={StaticResource LocStrings}, Path=Edit}" Command="{Binding EditNoteCommand}" />

HTH, 斯科特

【讨论】:

  • @Phil - 您应该编辑答案以修正拼写。然后具有适当级别代表的人会出现并批准您的更改。请记住,您现在可以这样做。
  • 谢谢斯科特。 Model.Note 是 Note 的模型,而不是视图模型。注意实际上没有视图模型。 Model.Note 是模型而不是视图模型这一事实会改变您的分析吗? FWIW,Notes 只是一个 ObservableCollection.
  • @Bill - 欢迎 Bill - 我认为 Note 现在是拥有 VM 的最佳人选,即使它以前只是一个模型。特别是因为它现在具有命令性,而且毫无疑问 INotifyPropertyChanged 实现指日可待(用于在编辑结束后更新 UI)。
  • 再次感谢斯科特。您对模型对象或其他对象何时可能“毕业”以保证 ViewModel 的额外见解对我非常有帮助。如果您能想到将 UI 元素映射到 ViewModel 的任何其他经验法则或参考,那将非常受欢迎。
猜你喜欢
  • 2013-04-25
  • 1970-01-01
  • 1970-01-01
  • 2016-05-03
  • 1970-01-01
  • 2013-02-21
  • 2015-04-14
  • 1970-01-01
  • 2011-07-22
相关资源
最近更新 更多