Использование команд в MVVM - PullRequest
       13

Использование команд в MVVM

1 голос
/ 23 февраля 2011

Я использовал очень хороший пример кода из ApuntasNotas , чтобы узнать больше о том, как эффективно использовать MVVM Light Toolkit .

В коде, в одном случае, автор, кажется, использует код, стоящий за этим, просто для установки DataContext с целью обработки события щелчка, которое я нахожу непонятным.

В XAML обработчик события EditNote_Click для MenuItem в ContextMenu cm обрабатывается в коде:

    <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 - которая уже написана и доступна - с атрибутом Command MenuItem в XAML?

Например, я попытался заменить следующее, которое компилировалось, но сгенерировало исключение (как показано ниже). Я подозреваю, что мой подход обоснован, но я что-то упускаю из-за передачи DataContext или, возможно, чего-то еще, в командный код. Я сбрасываю привязку DataContext к Main, чтобы упростить привязку команды:

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

Эта попытка генерирует следующее исключение в отношении other в EditNote - метод, вызываемый EditNoteCommand:

EditNote:

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:

1 Ответ

3 голосов
/ 23 февраля 2011

Вы определенно на правильном пути, если не хотите использовать выделенный код без необходимости, поэтому продолжайте следовать этому принципу в MVVM, и у вас все получится.

Я думаю, вы обнаружите, что автор здесь использует код для решения проблемы с получением правильного параметра команды. Для команды редактирования заметки - модель представления редактируемой заметки является обязательным параметром. Вот что они делают в коде здесь -

menuItem.DataContext as Model.Note

Проблема, с которой вы столкнулись, заключается в том, чтобы получить доступ к элементу меню, по которому щелкнули, и к модели основного вида в той же команде.

Если бы вы переместили EditNoteCommand в NotesViewModel (или какой-то другой класс Notes), вы могли бы сохранить свою команду в XAML с чем-то вроде:

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

НТН, Скотт

...