web-dev-qa-db-ja.com

WPF DataGridでのシングルクリック編集

ユーザーがセルを編集モードにして、シングルクリックでセルが含まれる行を強調表示できるようにします。デフォルトでは、これはダブルクリックです。

これをオーバーライドまたは実装するにはどうすればよいですか?

86
Austin

この問題の解決方法は次のとおりです。

<DataGrid DataGridCell.Selected="DataGrid_GotFocus" 
          ItemsSource="{Binding Source={StaticResource itemView}}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Nom" Binding="{Binding Path=Name}"/>
        <DataGridTextColumn Header="Age" Binding="{Binding Path=Age}"/>
    </DataGrid.Columns>
</DataGrid>

このDataGridはCollectionViewSource(ダミーPersonオブジェクトを含む)にバインドされます。

そこで魔法が起こります:DataGridCell.Selected = "DataGrid_GotFocus"

DataGridセルのSelected Eventをフックし、DataGridでBeginEdit()を呼び出します。

イベントハンドラのコードビハインドは次のとおりです。

private void DataGrid_GotFocus(object sender, RoutedEventArgs e)
{
    // Lookup for the source to be DataGridCell
    if (e.OriginalSource.GetType() == typeof(DataGridCell))
    {
        // Starts the Edit on the row;
        DataGrid grd = (DataGrid)sender;
        grd.BeginEdit(e);
    }
}
72
Micael Bergeron

Micael Bergeronからの回答は、私にとって有効な解決策を見つける良い出発点でした。既に編集モードになっている同じ行のセルでもシングルクリック編集を可能にするには、少し調整する必要がありました。 SelectionUnit Cellを使用することは私にとって選択肢ではありませんでした。

行のセルが初めてクリックされたときにのみ発生するDataGridCell.Selectedイベントを使用する代わりに、DataGridCell.GotFocusイベントを使用しました。

<DataGrid DataGridCell.GotFocus="DataGrid_CellGotFocus" />

そうすると、常に正しいセルがフォーカスされ、編集モードになりますが、セル内のコントロールはフォーカスされません、これはこのように解決しました

private void DataGrid_CellGotFocus(object sender, RoutedEventArgs e)
{
    // Lookup for the source to be DataGridCell
    if (e.OriginalSource.GetType() == typeof(DataGridCell))
    {
        // Starts the Edit on the row;
        DataGrid grd = (DataGrid)sender;
        grd.BeginEdit(e);

        Control control = GetFirstChildByType<Control>(e.OriginalSource as DataGridCell);
        if (control != null)
        {
            control.Focus();
        }
    }
}

private T GetFirstChildByType<T>(DependencyObject prop) where T : DependencyObject
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(prop); i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild((prop), i) as DependencyObject;
        if (child == null)
            continue;

        T castedProp = child as T;
        if (castedProp != null)
            return castedProp;

        castedProp = GetFirstChildByType<T>(child);

        if (castedProp != null)
            return castedProp;
    }
    return null;
}
38
user2134678

From: http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing

XAML:

<!-- SINGLE CLICK EDITING -->
<Style TargetType="{x:Type dg:DataGridCell}">
    <EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown"></EventSetter>
</Style>

CODE-BEHIND:

//
// SINGLE CLICK EDITING
//
private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    DataGridCell cell = sender as DataGridCell;
    if (cell != null && !cell.IsEditing && !cell.IsReadOnly)
    {
        if (!cell.IsFocused)
        {
            cell.Focus();
        }
        DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
        if (dataGrid != null)
        {
            if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
            {
                if (!cell.IsSelected)
                    cell.IsSelected = true;
            }
            else
            {
                DataGridRow row = FindVisualParent<DataGridRow>(cell);
                if (row != null && !row.IsSelected)
                {
                    row.IsSelected = true;
                }
            }
        }
    }
}

static T FindVisualParent<T>(UIElement element) where T : UIElement
{
    UIElement parent = element;
    while (parent != null)
    {
        T correctlyTyped = parent as T;
        if (correctlyTyped != null)
        {
            return correctlyTyped;
        }

        parent = VisualTreeHelper.GetParent(parent) as UIElement;
    }

    return null;
}
8
m-y

http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing からのソリューションは私にとってはうまくいきましたが、ResourceDictionaryで定義されたスタイルを使用してすべてのDataGridで有効にしました。リソースディクショナリでハンドラーを使用するには、それに分離コードファイルを追加する必要があります。方法は次のとおりです。

これは、DataGridStyles.xamlリソースディクショナリです。

<ResourceDictionary x:Class="YourNamespace.DataGridStyles"
            x:ClassModifier="public"
            xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml">
    <Style TargetType="DataGrid">
        <!-- Your DataGrid style definition goes here -->

        <!-- Cell style -->
        <Setter Property="CellStyle">
            <Setter.Value>
                <Style TargetType="DataGridCell">                    
                    <!-- Your DataGrid Cell style definition goes here -->
                    <!-- Single Click Editing -->
                    <EventSetter Event="PreviewMouseLeftButtonDown"
                             Handler="DataGridCell_PreviewMouseLeftButtonDown" />
                </Style>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

ルート要素のx:Class属性に注意してください。クラスファイルを作成します。この例では、DataGridStyles.xaml.csになります。内部にこのコードを入れてください:

using System.Windows.Controls;
using System.Windows;
using System.Windows.Input;

namespace YourNamespace
{
    partial class DataGridStyles : ResourceDictionary
    {

        public DataGridStyles()
        {
          InitializeComponent();
        }

     // The code from the myermian's answer goes here.
}
6
Andikki

私はMVVMでシングルクリックでセルを編集したいと思っていますが、これは別の方法です。

  1. Xamlでの動作の追加

    <UserControl xmlns:i="http://schemas.Microsoft.com/expression/2010/interactivity"
                 xmlns:myBehavior="clr-namespace:My.Namespace.To.Behavior">
    
        <DataGrid>
            <i:Interaction.Behaviors>
                <myBehavior:EditCellOnSingleClickBehavior/>
            </i:Interaction.Behaviors>
        </DataGrid>
    </UserControl>
    
  2. EditCellOnSingleClickBehaviorクラスはSystem.Windows.Interactivity.Behaviorを拡張します。

    public class EditCellOnSingleClick : Behavior<DataGrid>
    {
        protected override void OnAttached()
        {
            base.OnAttached();
            this.AssociatedObject.LoadingRow += this.OnLoadingRow;
            this.AssociatedObject.UnloadingRow += this.OnUnloading;
        }
    
        protected override void OnDetaching()
        {
            base.OnDetaching();
            this.AssociatedObject.LoadingRow -= this.OnLoadingRow;
            this.AssociatedObject.UnloadingRow -= this.OnUnloading;
        }
    
        private void OnLoadingRow(object sender, DataGridRowEventArgs e)
        {
            e.Row.GotFocus += this.OnGotFocus;
        }
    
        private void OnUnloading(object sender, DataGridRowEventArgs e)
        {
            e.Row.GotFocus -= this.OnGotFocus;
        }
    
        private void OnGotFocus(object sender, RoutedEventArgs e)
        {
            this.AssociatedObject.BeginEdit(e);
        }
    }
    

出来上がり!

2
Jouan Antoine

dušanKneževićの提案に基づいて、この方法を好みます。あなたはそれをクリックします))

<DataGrid.Resources>

    <Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
        <Style.Triggers>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsMouseOver"
                                   Value="True" />
                        <Condition Property="IsReadOnly"
                                   Value="False" />
                    </MultiTrigger.Conditions>
                    <MultiTrigger.Setters>
                        <Setter Property="IsEditing"
                                Value="True" />
                    </MultiTrigger.Setters>
                </MultiTrigger>
        </Style.Triggers>
    </Style>

</DataGrid.Resources>
2
René Hankel

マウスが上にあるときにDataGridCellのIsEditingプロパティをTrueに設定するトリガーを追加することで解決しました。それは私の問題のほとんどを解決しました。コンボボックスでも動作します。

<Style TargetType="DataGridCell">
     <Style.Triggers>
         <Trigger Property="IsMouseOver" Value="True">
             <Setter Property="IsEditing" Value="True" />
         </Trigger>
     </Style.Triggers>
 </Style>
1

User2134678の回答には2つの問題があります。 1つは非常にマイナーで、機能的な効果はありません。もう1つはかなり重要です。

最初の問題は、GotFocusが実際にはDataGridCellではなく、DataGridに対して実際に呼び出されていることです。 XAMLのDataGridCell修飾子は冗長です。

私が答えで見つけた主な問題は、Enterキーの動作が壊れていることです。 Enterキーを押すと、通常のDataGrid動作で現在のセルの下の次のセルに移動します。ただし、実際に舞台裏で行われるのは、GotFocusイベントが2回呼び出されることです。現在のセルがフォーカスを失うと、新しいセルがフォーカスを取得します。ただし、その最初のセルでBeginEditが呼び出される限り、次のセルはアクティブになりません。要するに、ワンクリックで編集できますが、グリッド上で文字通りクリックしていない人は不便になりますし、ユーザーインターフェイス設計者はすべてのユーザーがマウスを使用していると想定すべきではありません。 (キーボードのユーザーは、Tabを使用することでそれを回避できますが、それでも、必要のないフープを飛び越えていることを意味します。)

この問題の解決策は?セルのイベントKeyDownを処理し、キーがEnterキーの場合、BeginEditが最初のセルで発生するのを停止するフラグを設定します。これで、Enterキーは正常に動作します。

まず、次のスタイルをDataGridに追加します。

<DataGrid.Resources>
    <Style TargetType="{x:Type DataGridCell}" x:Key="SingleClickEditingCellStyle">
        <EventSetter Event="KeyDown" Handler="DataGridCell_KeyDown" />
    </Style>
</DataGrid.Resources>

そのスタイルを、ワンクリックを有効にする列の「CellStyle」プロパティに適用します。

次に、背後のコードでは、GotFocusハンドラーに次のものがあります(ここでVBを使用していることに注意してください。これは、「ワンクリックデータグリッド要求」クライアントが開発言語として必要だったためです) :

Private _endEditing As Boolean = False

Private Sub DataGrid_GotFocus(ByVal sender As Object, ByVal e As RoutedEventArgs)
    If Me._endEditing Then
        Me._endEditing = False
        Return
    End If

    Dim cell = TryCast(e.OriginalSource, DataGridCell)

    If cell Is Nothing Then
        Return
    End If

    If cell.IsReadOnly Then
        Return
    End If

    DirectCast(sender, DataGrid).BeginEdit(e)
    .
    .
    .

次に、KeyDownイベントのハンドラーを追加します。

Private Sub DataGridCell_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
    If e.Key = Key.Enter Then
        Me._endEditing = True
    End If
End Sub

これで、すぐに使用可能な実装の基本的な動作を変更せず、シングルクリック編集をサポートするDataGridができました。

1
GrantA

私はパーティーに少し遅れていることを知っていますが、私は同じ問題を抱えていて、別の解決策を思いつきました:

     public class DataGridTextBoxColumn : DataGridBoundColumn
 {
  public DataGridTextBoxColumn():base()
  {
  }

  protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
  {
   throw new NotImplementedException("Should not be used.");
  }

  protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
  {
   var control = new TextBox();
   control.Style = (Style)Application.Current.TryFindResource("textBoxStyle");
   control.FontSize = 14;
   control.VerticalContentAlignment = VerticalAlignment.Center;
   BindingOperations.SetBinding(control, TextBox.TextProperty, Binding);
    control.IsReadOnly = IsReadOnly;
   return control;
  }
 }

        <DataGrid Grid.Row="1" x:Name="exportData" Margin="15" VerticalAlignment="Stretch" ItemsSource="{Binding CSVExportData}" Style="{StaticResource dataGridStyle}">
        <DataGrid.Columns >
            <local:DataGridTextBoxColumn Header="Sample ID" Binding="{Binding SampleID}" IsReadOnly="True"></local:DataGridTextBoxColumn>
            <local:DataGridTextBoxColumn Header="Analysis Date" Binding="{Binding Date}" IsReadOnly="True"></local:DataGridTextBoxColumn>
            <local:DataGridTextBoxColumn Header="Test" Binding="{Binding Test}" IsReadOnly="True"></local:DataGridTextBoxColumn>
            <local:DataGridTextBoxColumn Header="Comment" Binding="{Binding Comment}"></local:DataGridTextBoxColumn>
        </DataGrid.Columns>
    </DataGrid>

ご覧のとおり、DataGridBoundColumnをすべて継承する独自のDataGridTextColumnを作成しました。 GenerateElementメソッドをオーバーライドし、すぐにTextboxコントロールを返すことにより、Editing Elementを生成するメソッドが呼び出されることはありません。別のプロジェクトでは、これを使用してDatepicker列を実装したため、これはチェックボックスとコンボボックスでも機能するはずです。

これは、残りのデータグリッドの動作に影響を与えないようです。少なくとも、これまでのところ、副作用に気づかず、否定的なフィードバックもありません。

0
Troillius