2017-05-05 17 views
2

次の方法でDataGridの選択動作を変更したいと思います。通常、複数の行が選択されていて、すでに選択されている項目の1つをクリックすると、その選択はクリックされた項目のみにリセットされます。複数選択された行のいずれかがキーボード修飾子なしでクリックされた場合、その選択は変更されないように変更したいと思います。これの目的は、複数項目のドラッグアンドドロップを許可することです。DataGridの選択動作をどのようにオーバーライドできますか?

:私はDataGridCell.OnMouseLeftButtonDown、このような何かをオーバーライドすることで動作を変更することができるはずのよう

at System.Windows.Controls.DataGrid.OnSelectionChanged(SelectionChangedEventArgs e) 
at System.Windows.Controls.Primitives.Selector.SelectionChanger.End() 
at System.Windows.Controls.DataGrid.MakeFullRowSelection(ItemInfo info, Boolean allowsExtendSelect, Boolean allowsMinimalSelect) 
at System.Windows.Controls.DataGrid.HandleSelectionForCellInput(DataGridCell cell, Boolean startDragging, Boolean allowsExtendSelect, Boolean allowsMinimalSelect) 
at System.Windows.Controls.DataGridCell.OnAnyMouseLeftButtonDown(MouseButtonEventArgs e) 
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target) 
at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs) 
at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised) 
at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent) 
at System.Windows.UIElement.OnMouseDownThunk(Object sender, MouseButtonEventArgs e) 
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target) 

ので、それが見えます:私は、前述のデフォルトの動作が起動されると、コールスタックが含まれていることに気づい

class MultiDragDataGridCell : DataGridCell 
{ 
    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) 
    { 
     // This allows users to click-and-drag a multi-selection by handling the event before 
     // the default behavior (deselecting everything but the clicked cell) kicks in. 
     if (IsSelected && Keyboard.Modifiers == ModifierKeys.None) 
     { 
      e.Handled = true; 
     } 

     base.OnMouseLeftButtonDown(e); 
    } 
} 

しかし、DataGridCellをインスタンス化するクラスが内部であるため、通常のDataGridCellの代わりにDataGridにMultiDragDataGridCellを作成するのに問題があります。誰もが私がそれを達成する方法を知っている、または私が望む行動を達成する別の方法があるかどうか?

他のものは、私が試した:

  • のMouseLeftButtonDownにハンドラを追加するDataGridCellをスタイリング。選択が既に変更された後に実行されるため、これは機能しません。
  • DataGridCellのスタイルを設定して、PreviewMouseLeftButtonDownにハンドラを追加します。これは動作しますが、セル内のボタンなどをクリックすることができなくなります。

答えて

2

注:この回答は、質問に記載された次の問題に対する解決策を提供するだけです。グリッドの選択動作をオーバーライドする方法ではありません。私は一度あなたがカスタムDataGridCellを定位置に置くことを望むなら、それはあなたがやろうとしていることに対する良い出発点になることができます。

DataGridCellをインスタンス化するクラスが内部であるので、しかし、私は、代わりに通常のDataGridCellのMultiDragDataGridCellを作成するためのデータグリッドを得るトラブルを抱えています。誰もが、私はそれを達成することができます方法を知っている...

ソリューション:DataGridカスタムDataGridCellを使用していることを確実にするために - あなたはDataGridCellsPresenterの拡張バージョンを使用するように再テンプレートにあなたDataGridRowを必要とするものでターンあなたのカスタムDataGridCellを提供します。

次のサンプルコードを参照してください:データグリッドの拡張

は、XAML

public class ExtendedDataGrid : DataGrid 
{ 
    protected override DependencyObject GetContainerForItemOverride() 
    { 
     //This provides the DataGrid with a customized version for DataGridRow 
     return new ExtendedDataGridRow(); 
    } 
} 

public class ExtendedDataGridRow : DataGridRow { } 

public class ExtendedDataGridCellsPresenter : System.Windows.Controls.Primitives.DataGridCellsPresenter 
{ 
    protected override DependencyObject GetContainerForItemOverride() 
    { 
     //This provides the DataGrid with a customized version for DataGridCell 
     return new ExtendedDataGridCell(); 
    } 
} 

public class ExtendedDataGridCell : DataGridCell 
{ 
    // Your custom/overridden implementation can be added here 
} 

再テンプレートDataGridRow(より包括的なtemplate can be found at this linkを制御 - 私は唯一の水で薄めたバージョンを使用しています読みやすいように)。

<Style TargetType="{x:Type local:ExtendedDataGridRow}"> 
     <Setter Property="Template"> 
      <Setter.Value> 
       <ControlTemplate TargetType="{x:Type local:ExtendedDataGridRow}"> 
        <Border x:Name="DGR_Border" 
          BorderBrush="{TemplateBinding BorderBrush}" 
          BorderThickness="{TemplateBinding BorderThickness}" 
          SnapsToDevicePixels="True"> 
         <SelectiveScrollingGrid> 
          <SelectiveScrollingGrid.ColumnDefinitions> 
           <ColumnDefinition Width="Auto" /> 
           <ColumnDefinition Width="*" /> 
          </SelectiveScrollingGrid.ColumnDefinitions> 
          <SelectiveScrollingGrid.RowDefinitions> 
           <RowDefinition Height="*" /> 
           <RowDefinition Height="Auto" /> 
          </SelectiveScrollingGrid.RowDefinitions> 

       <!-- Make sure to register your custom DataGridCellsPresenter here as following --> 

          <local:ExtendedDataGridCellsPresenter Grid.Column="1" 
            ItemsPanel="{TemplateBinding ItemsPanel}" 
            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> 
          <DataGridDetailsPresenter Grid.Column="1" 
            Grid.Row="1" 
            Visibility="{TemplateBinding DetailsVisibility}" 
            SelectiveScrollingGrid.SelectiveScrollingOrientation= 
             "{Binding AreRowDetailsFrozen, 
             ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical}, 
             Converter={x:Static DataGrid.RowDetailsScrollingConverter}, 
             RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/> 
          <DataGridRowHeader Grid.RowSpan="2" 
           SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical" 
           Visibility="{Binding HeadersVisibility, 
            ConverterParameter={x:Static DataGridHeadersVisibility.Row}, 
            Converter={x:Static DataGrid.HeadersVisibilityConverter}, 
            RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />  
         </SelectiveScrollingGrid> 
        </Border> 
       </ControlTemplate> 
      </Setter.Value> 
     </Setter> 
    </Style> 

そして、あなたの拡張DataGridのビジュアルツリーのカスタムデータグリッド細胞を持っている:

enter image description here

また、提供するDataGrid、またはDataGridRowを拡張することは必須ではありませんので、予めご了承くださいカスタムDataGridCell - 同じ結果を得るには、DataGridCellsPresenterを拡張してください(そして、DataGridRowのコントロールテンプレートを拡張版を使用して更新する)

1

私があるように、より良い、それを使用していない、大きなハックのように感じているを考え出すことができる唯一の事。しかし、それはあなた自身の解決策を見つけるための出発点かもしれません。

基本的な考え方:

  • でもEventManager.RegisterClassHandlerで取り扱うイベントにいくつかのイベントハンドラを実行します。 左マウスでクリックした後にのみドラッグ&ドロップを考えてみましょう
  • 修飾せずに、選択したセルの上にマウスを左クリックしたときにを復元セル選択のための登録
  • をこれは、いくつかの改善を必要とするか、アプリケーション全体のすべてのセルをいじり終わります以前セルSEを削除登録され、細胞が
  • 非選択されている場合は、選択したセル(そうでないユーザー体験が要件の組み合わせのために本当に奇妙になります)
  • は、選択したセルを復元しますマルチセル選択と

    public class MyDataGrid : DataGrid 
    { 
        static MyDataGrid() 
        { 
         EventManager.RegisterClassHandler(typeof(DataGridCell), UIElement.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(PreviewMouseLeftButtonDownHandler)); 
         EventManager.RegisterClassHandler(typeof(DataGridCell), UIElement.MouseLeftButtonUpEvent, new MouseButtonEventHandler(MouseLeftButtonUpHandler), true); 
         EventManager.RegisterClassHandler(typeof(DataGridCell), UIElement.MouseMoveEvent, new MouseEventHandler(MouseMoveHandler), true); 
        } 
    
        private static bool restoreNextCells = false; 
        private static bool isSelectedCell = false; 
        private static void PreviewMouseLeftButtonDownHandler(object sender, MouseButtonEventArgs e) 
        { 
         var cell = sender as DataGridCell; 
         isSelectedCell = cell.IsSelected; 
         restoreNextCells = cell.IsSelected && Keyboard.Modifiers == ModifierKeys.None; 
        } 
        private static void MouseMoveHandler(object sender, MouseEventArgs e) 
        { 
         var cell = sender as DataGridCell; 
         if (isSelectedCell && e.LeftButton == MouseButtonState.Pressed && cell.IsSelected && Keyboard.Modifiers == ModifierKeys.None) 
         { 
          DragDrop.DoDragDrop(cell, new ObjectDataProvider(), DragDropEffects.All); 
         } 
         restoreNextCells = false; 
         isSelectedCell = false; 
        } 
    
        private static void MouseLeftButtonUpHandler(object sender, MouseButtonEventArgs e) 
        { 
         restoreNextCells = false; 
         isSelectedCell = false; 
        } 
        protected override void OnSelectedCellsChanged(SelectedCellsChangedEventArgs e) 
        { 
         if (restoreNextCells && e.RemovedCells.Count > 0) 
         { 
          foreach (DataGridCellInfo item in e.RemovedCells) 
          { 
           SelectedCells.Add(item); 
          } 
          restoreNextCells = false; 
         } 
         base.OnSelectedCellsChanged(e); 
        } 
    } 
    

    用途:lectionは復元またはマウスがない場合、他のもの(マウスアップまたはマウス移動)

は、データグリッドコードをカスタマイズした後登録を復元します。

<local:MyDataGrid SelectionMode="Extended" SelectionUnit="Cell"> 

私の説明では重要な部分は除外しませんでした。不明な点があれば尋ねてください。

+0

を、私はこのアウトを試してみましたが、それは動作しますが、何らかの理由でそれがセル選択の表示を台無しに。表示はまだ古い動作に従っているようですが、実際のSelectedItemsはあなたが期待するものです。 – hypehuman

1

実際には、DataGridCellのスタイリングを作成してイベントハンドラを設定しましたが、イベントハンドラに論理エラーがあったとします。e.Handledtrueに設定しました。DataGridCellを選択した場合、 DataGridのデフォルトの動作では最初に行/セルを選択/選択解除してから内部コントロールを操作するため、複数の選択肢がある場合は、クリックされた行/セルが選択されているので、 複数の選択の場合にクリックされる行/セルの選択を防止します

私はあなたが期待したように、これは動作するはずとします

<DataGrid.Resources> 
      <Style TargetType="DataGridCell"> 
       <EventSetter Event="PreviewMouseLeftButtonDown" Handler="PreviewMouseDown"/> 
      </Style> 
     </DataGrid.Resources> 


private void PreviewMouseDown(object sender, MouseButtonEventArgs e) 
     { 
      var cell = sender as DataGridCell; if (cell == null) { return; } 
      DataGrid parGrid = null; 
      var visParent = VisualTreeHelper.GetParent(cell); 
      while (parGrid==null && visParent != null) 
      { 
       parGrid = visParent as DataGrid; 
       visParent = VisualTreeHelper.GetParent(visParent); 
      } 
      if (parGrid==null) { return; } 

      e.Handled = cell.IsSelected && Keyboard.Modifiers == ModifierKeys.None && parGrid.SelectedItems.Count > 1; 
     } 
+0

私は、visParentがセルでない場合は、必ずイベントを処理すると想定していると思います。しかし、私はそれが常に正しいとは思わない。 – hypehuman

+0

@hypehuman実際にはvisParentは決してセルではありません。あなたは、ループの中でセルのビジュアルペアレントを通過します。後で行レベルで開始することができます。その場合、行のスタイルを設定する必要があります。 – Rekshino

+0

ああ私は誤解しました。私は今それを得ると思います。しかし、行が選択されると、行/セル内のコントロールを操作できなくなると思います。 – hypehuman

関連する問題