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


92

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

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


WPFツールキット内にあるDataGridを使用していますか?
myermian

4
あなたが試したことと、それがどのように機能しないかについて、もう少し情報を提供することは可能でしょうか?
ザックジョンソン

回答:


76

これが私がこの問題をどのように解決したかです:

<DataGrid DataGridCell.Selected="DataGridCell_Selected" 
          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 = "DataGridCell_Selected"

DataGridセルのSelectedイベントをフックし、DataGridでBeginEdit()を呼び出すだけです。

以下は、イベントハンドラのコードビハインドです。

private void DataGridCell_Selected(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);
    }
}

8
SelectionUnitDataGridのプロパティをに設定すると、すでに選択されている行の問題を回避できますCell
Matt Winckler、

DataGridCellにTextBoxがあるとします。を呼び出した後grd.BeginEdit(e)、そのセルのTextBoxにフォーカスを設定します。どうやってやるの?FindName("txtBox")DataGridCellとDataGridの両方を呼び出してみましたが、nullが返されました。
user1214135 2013年

GotFocus = "DataGrid_GotFocus"がないようですが?
2013年

4
これはうまく機能しますが、これを行うことはお勧めしません。私はこれを自分のプロジェクトで使用し、標準のDG動作にロールバックすることにしました。将来、DGが大きくなり複雑になると、検証、新しい行の追加、その他の奇妙な動作の問題が発生します。
white.zaz

1
標準のDG動作にロールバックした後、@ white.zazはクライアントに満足しましたか?この質問をする主な理由は、DGが編集モードになる前に何回もクリックする必要があるため、標準のDG機能での編集はユーザーフレンドリーではないためです。
AEMLoviji

42

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;
}

3
チェックボックスが機能しないようです。それでもダブルクリックする必要があります
Thomas Klammer、

9

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>

コードビハインド:

//
// 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;
}

1
これは特定のケースでは機能せず、Micael Bergeronsソリューションよりも複雑です。
SwissCoder 2010

私にとって、これはほとんど解決策でした。「PreviewMouseLeftButtonUp」イベントハンドラーを追加して、まったく同じコードを配置する必要がありました。
ネストル・サンチェス・A.

コンボボックスがあると、これは機能しません。プレビュークリックはコンボボックスのポップアップのクリックを確認し、次にcell.focus呼び出しはすべてをねじ込みます。最も簡単な修正は、マウスイベントの元のソースを表示するセクションを追加し、FindVisualParentを使用して、データグリッド内にあるかどうかを確認することです。そうでない場合は、他の作業を行わないでください。
ジョンガードナー、

7

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.
}

リンクは
無効です

4

私は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>

これは、コンボボックスが編集テンプレートとして使用されている場合は機能しません。マウスイベントをキャプチャするチェックボックスなども壊れると思います
Steve

私にとってこれはコンボボックス列で機能しますが、「新しいアイテム行」(最後の行)のテキストボックスは奇妙な動作をします。最初にクリックすると、入力フォーカスが表示され、入力できます。 、テキストボックスの値が消えます。さらに入力すると、新しく入力したテキストが正しく保存されます(必要に応じて新しいエントリが作成されます)。これは、ComboboxColumnでも発生します。
FrankM

最初は正常に動作しているように見えますが、Datagridを完全に混乱させました。ソートしようとすると、これらのすべての値が失われます。このコードを使用しないと、ソートで正常に動作します。
Chandraprakash

3

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

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

1
動作しません...マウスがセルを離れるとすぐに編集が失われます。したがって、1)編集するセルを左クリックします。2)マウスを邪魔にならない場所に移動します。3)入力を開始します。セルが編集モードではなくなったため、入力できません。
Skarsnik 2017

1
私にとっても機能しません。テキストボックスを編集できない
Blechdose

しかし、このアプローチには1つの問題があります。最初の列を編集用にロックしました。このアプローチでは、これにより1番目の列も編集可能になります。
Chandraprakash

3

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);
        }
    }

出来上がり!


1

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

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

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

この問題の解決策は?セルのイベントKeyDownを処理し、Keyが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ができました。


0

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

     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コントロールをすぐに返すことにより、編集要素を生成するためのメソッドが呼び出されることはありません。別のプロジェクトでこれを使用してDatepicker列を実装したので、これはチェックボックスとコンボボックスでも機能するはずです。

これは、残りのデータグリッドの動作に影響を与えるようには見えません。


-1

更新

セルがテキストボックスのままで問題ない場合の簡単な解決策(編集モードと非編集モードを区別しない)。このように、シングルクリック編集はそのまま使用できます。これは、コンボボックスやボタンなどの他の要素でも機能します。それ以外の場合は、更新プログラムの下のソリューションを使用してください。

<DataGridTemplateColumn Header="My Column header">
   <DataGridTemplateColumn.CellTemplate>
      <DataTemplate>
         <TextBox Text="{Binding MyProperty } />
      </DataTemplate>
   </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

更新終了

暴れる

私はここで、そしてGoogleで見つけたすべてを試し、自分のバージョンを作成してみました。しかし、すべての回答/解決策は主にテキストボックス列で機能しましたが、他のすべての要素(チェックボックス、コンボボックス、ボタン列)では機能しませんでした。データグリッドを醜い方法で動作させ、ハックを作成するように強制してくれたマイクロソフトに感謝します。そのため、他の列に影響を与えることなく、テキストボックス列にスタイルを直接適用できるバージョンを作成することにしました。

特徴

  • 背後にコードはありません。MVVMフレンドリー。
  • 同じまたは異なる行の異なるテキストボックスセルをクリックすると機能します。
  • TabキーとEnterキーが機能します。
  • 他の列には影響しません。

出典

私はこのソリューションと@myの答えを使用し、それらを添付された動作になるように変更しました。 http://wpf-tutorial-net.blogspot.com/2016/05/wpf-datagrid-edit-cell-on-single-click.html

どうやって使うのですか

このスタイルを追加します。これBasedOnは、データグリッドに派手なスタイルを使用してて、それらを失いたくない場合に重要です。

<Window.Resources>
    <Style x:Key="SingleClickEditStyle" TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource {x:Type DataGridCell}}">
        <Setter Property="local:DataGridTextBoxSingleClickEditBehavior.Enable" Value="True" />
    </Style>
</Window.Resources>

次のようにスタイルCellStyleをそれぞれに適用しますDataGridTextColumns

<DataGrid ItemsSource="{Binding MyData}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="My Header" Binding="{Binding Comment}" CellStyle="{StaticResource SingleClickEditStyle}" />         
    </DataGrid.Columns>
</DataGrid>

次に、このクラスをMainViewModelと同じ名前空間(または別の名前空間。ただし、以外の名前空間接頭辞を使用する必要があります)に追加しますlocal。アタッチされた動作の醜い定型コードの世界へようこそ。

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace YourMainViewModelNameSpace
{
    public static class DataGridTextBoxSingleClickEditBehavior
    {
        public static readonly DependencyProperty EnableProperty = DependencyProperty.RegisterAttached(
            "Enable",
            typeof(bool),
            typeof(DataGridTextBoxSingleClickEditBehavior),
            new FrameworkPropertyMetadata(false, OnEnableChanged));


        public static bool GetEnable(FrameworkElement frameworkElement)
        {
            return (bool) frameworkElement.GetValue(EnableProperty);
        }


        public static void SetEnable(FrameworkElement frameworkElement, bool value)
        {
            frameworkElement.SetValue(EnableProperty, value);
        }


        private static void OnEnableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is DataGridCell dataGridCell)
                dataGridCell.PreviewMouseLeftButtonDown += DataGridCell_PreviewMouseLeftButtonDown;
        }


        private static void DataGridCell_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            EditCell(sender as DataGridCell, e);
        }

        private static void EditCell(DataGridCell dataGridCell, RoutedEventArgs e)
        {
            if (dataGridCell == null || dataGridCell.IsEditing || dataGridCell.IsReadOnly)
                return;

            if (dataGridCell.IsFocused == false)
                dataGridCell.Focus();

            var dataGrid = FindVisualParent<DataGrid>(dataGridCell);
            dataGrid?.BeginEdit(e);
        }


        private static T FindVisualParent<T>(UIElement element) where T : UIElement
        {
            var parent = VisualTreeHelper.GetParent(element) as UIElement;

            while (parent != null)
            {
                if (parent is T parentWithCorrectType)
                    return parentWithCorrectType;

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

            return null;
        }
    }
}

-3
 <DataGridComboBoxColumn.CellStyle>
                        <Style TargetType="DataGridCell">
                            <Setter Property="cal:Message.Attach" 
                            Value="[Event MouseLeftButtonUp] = [Action ReachThisMethod($source)]"/>
                        </Style>
                    </DataGridComboBoxColumn.CellStyle>
 public void ReachThisMethod(object sender)
 {
     ((System.Windows.Controls.DataGridCell)(sender)).IsEditing = true;

 }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.