2013-07-02 16 views
17

Ho un Datagrid e non mi piace la mia soluzione alternativa per attivare un comando doppio clic sul mio viewmodel per la riga selezionata (aka selezionata).Associare il comando DoubleClick da DataGrid Row a VM

Vista:

<DataGrid EnableRowVirtualization="True" 
       ItemsSource="{Binding SearchItems}" 
       SelectedItem="{Binding SelectedItem}" 
       SelectionMode="Single" 
       SelectionUnit="FullRow"> 

     <i:Interaction.Triggers> 
      <i:EventTrigger EventName="MouseDoubleClick"> 
       <cmd:EventToCommand Command="{Binding MouseDoubleClickCommand}" PassEventArgsToCommand="True" /> 
      </i:EventTrigger> 
     </i:Interaction.Triggers> 
     ... 
    </DataGrid> 

ViewModel:

public ICommand MouseDoubleClickCommand 
    { 
     get 
     { 
      if (mouseDoubleClickCommand == null) 
      { 
       mouseDoubleClickCommand = new RelayCommand<MouseButtonEventArgs>(
        args => 
        { 
         var sender = args.OriginalSource as DependencyObject; 
         if (sender == null) 
         { 
          return; 
         } 
         var ancestor = VisualTreeHelpers.FindAncestor<DataGridRow>(sender); 
         if (ancestor != null) 
         { 
          MessengerInstance.Send(new FindDetailsMessage(this, SelectedItem.Name, false)); 
         } 
        } 
        ); 
      } 
      return mouseDoubleClickCommand; 
     } 
    } 

voglio eliminare il codice relativo vista (quello con l'oggetto di dipendenza e l'albero di supporto visivo) nel mio modello vista, in quanto ciò rompe la testabilità in qualche modo. Ma d'altra parte in questo modo evito che qualcosa accada quando l'utente non fa clic su una riga ma sull'intestazione, ad esempio.

PS: Ho provato a dare un'occhiata ai comportamenti collegati, ma non riesco a scaricare da Skydrive al lavoro, quindi una soluzione "integrata" sarebbe la soluzione migliore.

risposta

23

Perché non usi semplicemente lo CommandParameter?

<DataGrid x:Name="myGrd" 
      ItemsSource="{Binding SearchItems}" 
      SelectedItem="{Binding SelectedItem}" 
      SelectionMode="Single" 
      SelectionUnit="FullRow"> 

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

Il comando è qualcosa di simile:

public ICommand MouseDoubleClickCommand 
{ 
    get 
    { 
     if (mouseDoubleClickCommand == null) 
     { 
      mouseDoubleClickCommand = new RelayCommand<SearchItem>(
       item => 
       { 
        var selectedItem = item; 
       }); 
     } 

     return mouseDoubleClickCommand; 
    } 
} 

EDIT: Io uso questo, invece di Interaction.Triggers:

<DataGrid.InputBindings> 
    <MouseBinding MouseAction="LeftDoubleClick" 
        Command="{Binding Path=MouseDoubleClickCommand}" 
        CommandParameter="{Binding ElementName=myGrd, Path=SelectedItem}" /> 
</DataGrid.InputBindings> 
+1

Il problema non è ottenere l'elemento selezionato (è un database comunque sulla VM), ma ottenendo il comando di non eseguire quando, ad esempio, le intestazioni del datagrid vengono duplicate. – metacircle

+2

se vuoi impedire a mousedoubleclick puoi provare PreviewMouseDoubleClick e impostare e.Handled = true per le tue condizioni. così puoi rielaborare il codice da viewmodel e metterlo in codebehind per il tuo datagrid – blindmeis

+0

Ottima idea. In realtà ho fatto la stessa cosa nel mio codebehind per OnContextMenuOpening da sempre. A volte semplicemente non hai le idee giuste al momento giusto in mente. Grazie. Segnalo come risposta. – metacircle

0

Puoi provare questa soluzione:

<DataGrid EnableRowVirtualization="True" 
      ItemsSource="{Binding SearchItems}" 
      SelectedItem="{Binding SelectedItem}" 
      SelectionMode="Single" 
      SelectionUnit="FullRow"> 
    <DataGrid.Columns> 
     <DataGridTemplateColumn Header="....."> 
      <DataGridTemplateColumn.CellTemplate> 
       <DataTemplate> 
         <TextBlock .....> 
          <i:Interaction.Triggers> 
           <i:EventTrigger EventName="MouseDoubleClick"> 
            <cmd:EventToCommand Command="{Binding MouseDoubleClickCommand}" PassEventArgsToCommand="True" /> 
           </i:EventTrigger> 
          </i:Interaction.Triggers> 
         </TextBlock> 
       </DataTemplate> 
    ................... 

In questo caso è necessario specificare DataTemplate per ogni colonna nel DataGrid

+0

Credo che questo avrebbe funzionato, ma se dovessi scegliere tra questo e la mia soluzione, probabilmente bastone con la mia. Se cambio colonne, devo sempre ripetere il codice XAML per ogni colonna. – metacircle

11

Ecco come è possibile implementare utilizzando un comportamento allegato:

MODIFICA: Ora registra il comportamento su DataGridRow anziché DataGrid in modo che i clic DataGridHeader siano ignorati ORed.

Comportamento:

public class Behaviours 
{ 
    public static DependencyProperty DoubleClickCommandProperty = 
     DependencyProperty.RegisterAttached("DoubleClickCommand", typeof(ICommand), typeof(Behaviours), 
              new PropertyMetadata(DoubleClick_PropertyChanged)); 

    public static void SetDoubleClickCommand(UIElement element, ICommand value) 
    { 
     element.SetValue(DoubleClickCommandProperty, value); 
    } 

    public static ICommand GetDoubleClickCommand(UIElement element) 
    { 
     return (ICommand)element.GetValue(DoubleClickCommandProperty); 
    } 

    private static void DoubleClick_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var row = d as DataGridRow; 
     if (row == null) return; 

     if (e.NewValue != null) 
     { 
      row.AddHandler(DataGridRow.MouseDoubleClickEvent, new RoutedEventHandler(DataGrid_MouseDoubleClick)); 
     } 
     else 
     { 
      row.RemoveHandler(DataGridRow.MouseDoubleClickEvent, new RoutedEventHandler(DataGrid_MouseDoubleClick)); 
     } 
    } 

    private static void DataGrid_MouseDoubleClick(object sender, RoutedEventArgs e) 
    { 
     var row= sender as DataGridRow; 

     if (row!= null) 
     { 
      var cmd = GetDoubleClickCommand(row); 
      if (cmd.CanExecute(row.Item)) 
       cmd.Execute(row.Item); 
     } 
    } 
} 

Xaml:

<DataGrid x:Name="grid" EnableRowVirtualization="True" 
      SelectedItem="{Binding SelectedItem}" 
      SelectionMode="Single" 
      SelectionUnit="FullRow" ItemsSource="{Binding SearchItems}"> 
     <DataGrid.RowStyle> 
      <Style TargetType="DataGridRow"> 
       <Setter Property="Behaviours.DoubleClickCommand" Value="{Binding ElementName=grid, Path=DataContext.SortStateCommand}"/> 
      </Style> 
     </DataGrid.RowStyle> 

Sarà quindi necessario modificare il vostro MouseDoubleClickCommand per rimuovere il parametro MouseButtonEventArgs e sostituirla con il tipo di SelectedItem.

+0

Mi piace come soluzione quasi perfetta (leggi come il migliore).Dopotutto questo è il significato degli eventi attched: http://www.codeproject.com/Articles/28959/Introduction-to-Attached-Behaviors-in-WPF – James

7

modo più semplice rispetto a qualsiasi delle soluzioni proposte qui.

Sto usando questo.

<!-- 
requires IsSynchronizedWithCurrentItem 
for more info on virtualization/perf https://stackoverflow.com/questions/9949358/datagrid-row-virtualization-display-issue 
--> 
     <DataGrid ItemsSource="{Binding SearchItems}" 
        IsSynchronizedWithCurrentItem="True" 
        AutoGenerateColumns="false" CanUserAddRows="False" CanUserDeleteRows="False" IsReadOnly="True" EnableRowVirtualization="True" 
        > 

      <!-- for details on ICollection view (the magic behind {Binding Accounts/} https://marlongrech.wordpress.com/2008/11/22/icollectionview-explained/ --> 

      <DataGrid.InputBindings> 
       <MouseBinding 
        MouseAction="LeftDoubleClick" 
        Command="{Binding MouseDoubleClickCommand}" 
        CommandParameter="{Binding SearchItems/}" /> 
      </DataGrid.InputBindings> 
     </DataGrid> 

da WPF DataGrid: CommandBinding to a double click instead of using Events

+0

Grazie per la prima fonte di collegamento: è stato utile. Per gli altri che leggono questo commento: prestare particolare attenzione alla barra in avanti alla fine del valore dell'attributo CommandParameter (ad esempio "SearchItems /"). –

Problemi correlati