WPF: Как прикрепить события мыши к модели представления? - PullRequest
5 голосов
/ 15 мая 2009

Я пытаюсь использовать шаблон MVVM в первый раз. Итак, у меня есть ItemsControl, заполненный моими объектами модели представления, отображенными с использованием DataTemplate; объекты - это "узлы" и "ребра", представленные в DataTemplate с Thumb и Polyline объектами, и я хочу иметь возможность обнаруживать щелчки и перетаскивания на ItemsControl, чтобы перемещать узлы и ребра .

Два вопроса:

  • Как мне прикрепить обработчики событий мыши к Polyline и Thumb для обработки маленькими моделями представления? (Я мог бы присоединить обработчик Thumb.DragDelta к ItemsControl и e.OriginalSource к Thumb, но как мне получить соответствующий объект viewmodel?)
  • Как мне прикрепить обработчики событий мыши к ItemsControl для обнаружения щелчков мышью и перетаскивания на пустое место? (ответ ниже)

Примечание: я знаю, что он не может считаться надлежащей ViewModel, если он непосредственно обрабатывает события View. Но важный момент заключается в том, что мне нужно обрабатывать события мыши, и я не уверен, как их прикрепить.

Ответы [ 6 ]

6 голосов
/ 16 мая 2009

Я нашел способ обработки событий, вызванных объектами в DataTemplate.

(1) присоединяет обработчики событий к ItemsControl

<ItemsControl x:Name="_itemsControl" 
              Thumb.DragStarted="Node_DragStarted"
              Thumb.DragDelta="Node_DragDelta"
              Thumb.DragCompleted="Node_DragCompleted"
              MouseDoubleClick="OnMouseDoubleClick"
              .../>

(2), чтобы выяснить, к какому элементу относится событие, обработать OriginalSource как FrameworkElement и получить его DataContext:

void Node_DragStarted(object sender, DragStartedEventArgs e)
{
    var os = (FrameworkElement)e.OriginalSource;
    var vm = os.DataContext as ItemViewModel;
    if (vm != null)
        // do something with the item ViewModel
}
3 голосов
/ 16 мая 2009

ViewModel должен быть отключен от GUI, поэтому он ничего не знает о элементах управления или щелчках мыши.

Два варианта:

  • Вызовите команду во ViewModel, как предложено Томасом
  • Привязать положение большого пальца к свойству в ViewModel, затем, когда элемент управления перемещается, WPF обновит значения положения в ViewModel.
2 голосов
/ 15 мая 2009

Я разобрался с ответом на второй вопрос. Мне нужен был ItemsControl, который поддерживал прокрутку, и мне нужно было иметь элементы в Grid, а не в StackPanel по умолчанию. Чтобы выполнить оба требования, я использовал ControlTemplate:

<!--In the resources...-->
<ControlTemplate x:Key="GraphTemplate" TargetType="ItemsControl">
    <ScrollViewer Name="ScrollViewer"
                  Padding="{TemplateBinding Padding}"
                  HorizontalScrollBarVisibility="Auto">
        ...
            <Grid Name="Panel" IsItemsHost="True"
                  Background="{TemplateBinding ItemsControl.Background}"/>
        ...
    </ScrollViewer>
</ControlTemplate>
<!--Later...-->
<ItemsControl x:Name="_itemsControl" 
              ItemsSource="{Binding Items}"
              Template="{StaticResource GraphTemplate}"
              Background="LightYellow"/>

Чтобы получить события мыши со значимыми координатами мыши (то есть координатами в прокручиваемом пространстве), необходимо было получить ссылку на сетку, используя странное заклинание:

Grid grid = (Grid)_itemsControl.Template.FindName("Panel", _itemsControl);

Затем вы присоединяете обработчики событий к сетке, а внутри обработчиков событий мыши получаете координаты мыши w.r.t. сетка с использованием

Point p = e.GetPosition((IInputElement)sender);

Чтобы получить события мыши на всей поверхности, элемент управления (фактически сетка) должен иметь фон, поэтому я установил выше Background = "LightYellow", который распространяется на сетку через привязку в ControlTemplate.

1 голос
/ 20 мая 2009

У Bea Stollnitz есть пример перетаскивания под названием «Как я могу перетаскивать элементы между привязанными данными ItemsControls?». Я бы опубликовал ссылку, но StackOverflow не пускает меня.

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

Однако я бы согласился с Томасом и Кэмероном выше. Вы захотите ограничить смешивание / сопоставление обработки событий и привязки данных. Если вы идете по маршруту обработки событий, вы, возможно, не захотите избегать использования термина «Модель представления» для своих объектов, поскольку он обычно обозначает альтернативу привязки данных.

1 голос
/ 16 мая 2009

Есть способы сделать это без кода ...

Вы можете использовать прикрепленный шаблон поведения для сопоставления событий с командами, см. Реализацию Марлона Греча здесь

Вы также можете использовать расширение разметки , которое я написал для привязки InputBindings к командам ViewModel, например:

<UserControl.InputBindings>
    <MouseBinding Gesture="LeftClick" Command="{input:CommandBinding SomeCommand}"/>
</UserControl.InputBindings>

Однако я не уверен, что это соответствует вашим конкретным потребностям ...

0 голосов
/ 02 ноября 2010

Я использую гораздо более элегантный метод. Я использую Prism 2 и шаблоны данных. Итак, что я сделал, это:

<ItemsControl x:Name="SearchImagesList" ItemTemplate="{StaticResource SearchResultsAlbum}"   

и в ItemTemplate я только что создал кнопку внутри!

<DataTemplate x:Key="SearchResultsAlbum">                        
    <Button CommandParameter="{Binding}"                 
            Command="{Binding Source={x:Static PhotoBookPRMainModule:ServiceProvider.DesignEditorViewManager}, Path=NavigationCommands.NavigateSearchResultAction}">
...