WPF DataGridでシングルクリックチェックボックスを選択する方法


143

最初の列がテキスト列で、2番目の列がCheckBox列であるDataGridがあります。チェックボックスをクリックしたいのですが。チェックされるはずです。

ただし、選択するには2クリックかかります。1回目のクリックではセルが選択され、2回目のクリックではチェックボックスがオンになります。ワンクリックでチェックボックスをオン/オフにする方法。

WPF 4.0を使用しています。DataGridの列はAutoGeneratedです。


4
重複:stackoverflow.com/questions/1225836/…、ただし、こちらの方がタイトルが良い
2011

回答:


189

シングルクリックDataGridチェックボックスの場合は、通常のチェックボックスコントロールを内部に配置DataGridTemplateColumnして設定するだけUpdateSourceTrigger=PropertyChangedです。

<DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
        <CheckBox IsChecked="{Binding Path=IsSelected, UpdateSourceTrigger=PropertyChanged}" />
    </DataTemplate>
</DataGridTemplateColumn.CellTemplate>

4
WOW-最後まで読んでよかった。これは完全に機能し、かなり複雑ではありません。IMOこれは答えとしてマークする必要があります。
Tod

2
これはComboBoxでも機能します。同様に、DataGridComboBoxColumnよりもWAYの方が優れています。
user1454265 14

2
チェック/チェック解除にスペースバーを使用し、別のセルに移動するために矢印を使用する場合はそうではありません。
Yola、2015年

1
「IsSelected」をバインドする必要があると私はこの答えを解釈しましたが、それは真実ではありません!あなたは自分のBindingDataGridTemplateColumn.CellTemplate一緒に使うだけでうまくいきます!! @ weidian-huang の答えは、それを理解するのに役立ちました、ありがとう!
AstralisSomnium

62

私はこれを次のスタイルで解決しました:

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

もちろん、これを特定のカラムにさらに適合させることも可能です...


8
いいね。私はそれをMultiTriggerに変更し、ReadOnly = Falseの条件を追加しましたが、基本的なアプローチは、キーボードナビゲーションが重要ではない私の単純なケースで機能しました。
MarcE、2011年

そのスタイルをグリッドに追加すると、Operationsの例外が発生します。ItemsSourceの使用中は無効です。代わりに、ItemsControl.ItemsSourceを使用して要素にアクセスして変更します。
Alkampfer

1
これは今まで見た中で最もきれいな方法です!いいね!(IsReadOnly = "True"の場合、MultiTriggerが機能します)
FooBarTheLittle

2
このソリューションには、予期しない/望ましくない動作があります。stackoverflow.com/q/39004317/2881450を
jHilscher

2
バインディングを機能させるには、UpdateSourceTrigger = PropertyChanged
AQuirky

27

まず、これはかなり古い質問であることはわかっていますが、それでも答えてみようと思いました。

数日前に同じ問題が発生し、驚くほど短い解決策が見つかりました(このブログを参照)。基本的にDataGridCheckBoxColumnは、XAML の定義を次のように置き換えるだけです。

<DataGridTemplateColumn Header="MyCheckBoxColumnHeader">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding Path=MyViewModelProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

このソリューションの利点は明白です。これはXAMLのみです。したがって、追加のUIロジックを使用してコードバックに負担をかけることを効果的に抑制し、MVVM熱狂者の目でステータスを維持するのに役立ちます;)。


1
これはコンスタンチン・サラバトフの答えに似ており、これは私にとってうまくいきました。+1が含まれていないコードサンプルを含めるため。古い質問への良い答えをありがとう。
Don Herod 14

1
この問題は、コンボボックス列を使用すると、その列のすべてのセルに小さなドロップダウンボタンが常に表示されることです。セルをクリックしたときだけではありません。
user3690202 2015

18

ようにするにはコンスタンチンSalavatovの回答で仕事をAutoGenerateColumnsするイベントハンドラを追加し、DataGridAutoGeneratingColumn次のコードで:

if (e.Column is DataGridCheckBoxColumn && !e.Column.IsReadOnly)
{
    var checkboxFactory = new FrameworkElementFactory(typeof(CheckBox));
    checkboxFactory.SetValue(FrameworkElement.HorizontalAlignmentProperty, HorizontalAlignment.Center);
    checkboxFactory.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Center);
    checkboxFactory.SetBinding(ToggleButton.IsCheckedProperty, new Binding(e.PropertyName) { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged });

    e.Column = new DataGridTemplateColumn
        {
            Header = e.Column.Header,
            CellTemplate = new DataTemplate { VisualTree = checkboxFactory },
            SortMemberPath = e.Column.SortMemberPath
        };
}

これにより、のすべてDataGridの自動生成チェックボックス列が「シングルクリック」で編集可能になります。


自動生成された列のアプローチに記入してくれてありがとう、これは私に適切な方向を簡単に示します。
el2iot2 2014

17

Goblinの回答で参照されているブログに基づいていますが、.NET 4.0および行選択モードで動作するように変更されています。

また、編集モードに入り、シングルクリックまたはテキスト入力でドロップダウンを表示することにより、DataGridComboBoxColumnの編集が高速化されることに注意してください。

XAML:

        <Style TargetType="{x:Type DataGridCell}">
            <EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown" />
            <EventSetter Event="PreviewTextInput" Handler="DataGridCell_PreviewTextInput" />
        </Style>

コードビハインド:

    private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        DataGridCell cell = sender as DataGridCell;
        GridColumnFastEdit(cell, e);
    }

    private void DataGridCell_PreviewTextInput(object sender, TextCompositionEventArgs e)
    {
        DataGridCell cell = sender as DataGridCell;
        GridColumnFastEdit(cell, e);
    }

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

        DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
        if (dataGrid == null)
            return;

        if (!cell.IsFocused)
        {
            cell.Focus();
        }

        if (cell.Content is CheckBox)
        {
            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;
                }
            }
        }
        else
        {
            ComboBox cb = cell.Content as ComboBox;
            if (cb != null)
            {
                //DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
                dataGrid.BeginEdit(e);
                cell.Dispatcher.Invoke(
                 DispatcherPriority.Background,
                 new Action(delegate { }));
                cb.IsDropDownOpen = true;
            }
        }
    }


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

このソリューションは私にとって最も効果的でした。バインドされたViewModelが他のソリューションで更新されませんでした。
BrokeMyLegBiking 2012年

@surfen、データグリッドが含まれているページが多数ある場合、上記のスタイルとコードをすべてのページとそのコードビハインドに配置する必要がありますか?スタイルとコードを、すべてのページ
Angel

空のアクションをディスパッチする必要があるのはなぜですか?
user3690202

@ user3690202 Windows.FormsのDoEventsに似ています。BeginEditを呼び出した後、セルが実際に編集モードに入るのを待つ必要があります。
ジリ・スカラ

@JiříSkála-この問題の解決策でこれを行う必要があったことを思い出しませんが、私はあなたの言っていることを理解しています-ありがとう!
user3690202

10

私はこれらの提案を試し、他のサイトで見つけた他の提案もたくさん試しましたが、どれも私にとってはうまくいきませんでした。最終的に、次のソリューションを作成しました。

独自のDataGrid継承コントロールを作成し、次のコードを追加しました。

public class DataGridWithNavigation : Microsoft.Windows.Controls.DataGrid
{
    public DataGridWithNavigation()
    {
        EventManager.RegisterClassHandler(typeof(DataGridCell), 
            DataGridCell.PreviewMouseLeftButtonDownEvent,
            new RoutedEventHandler(this.OnPreviewMouseLeftButtonDown));
    }


    private void OnPreviewMouseLeftButtonDown(object sender, RoutedEventArgs e)
    {
        DataGridCell cell = sender as DataGridCell;
        if (cell != null && !cell.IsEditing && !cell.IsReadOnly)
        {
          DependencyObject obj = FindFirstControlInChildren(cell, "CheckBox");
            if (obj != null)
            {
                System.Windows.Controls.CheckBox cb = (System.Windows.Controls.CheckBox)obj;
                cb.Focus();
                cb.IsChecked = !cb.IsChecked;
            }
        }
    }

    public DependencyObject FindFirstControlInChildren(DependencyObject obj, string controlType)
    {
        if (obj == null)
            return null;

        // Get a list of all occurrences of a particular type of control (eg "CheckBox") 
        IEnumerable<DependencyObject> ctrls = FindInVisualTreeDown(obj, controlType);
        if (ctrls.Count() == 0)
            return null;

        return ctrls.First();
    }

    public IEnumerable<DependencyObject> FindInVisualTreeDown(DependencyObject obj, string type)
    {
        if (obj != null)
        {
            if (obj.GetType().ToString().EndsWith(type))
            {
                yield return obj;
            }

            for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
            {
                foreach (var child in FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type))
                {
                    if (child != null)
                    {
                        yield return child;
                    }
                }
            }
        }
        yield break;
    }
}

これは何をするのですか?

まあ、DataGridのセルをクリックするたびに、セル内にCheckBoxコントロールが含まれているかどうかを確認します。含まれている場合、フォーカスをそのCheckBoxに設定し、その値を切り替えます

これは私にとってはうまくいくようで、素晴らしい、簡単に再利用可能なソリューションです。

ただし、これを行うにはコードを記述する必要があるのは残念です。WPFが行を編集モードにするために使用するため、最初のマウスクリック(DataGridのCheckBoxで)が「無視」されるという説明は論理的に聞こえるかもしれませんが、実際には、これはすべての実際のアプリケーションの動作に反します。

ユーザーが画面にチェックボックスを表示した場合、チェックボックスを1回クリックしてチェックマークを付ける/外すことができるはずです。物語の終わり。


1
ありがとう、私はたくさんの「解決策」を試しましたが、これは毎回本当にうまくいくと思われる最初のものです。そして、それは私のアプリケーションアーキテクチャに美しく収まります。
なGuge

このソリューションではバインディングの更新に問題が発生しますが、wpf.codeplex.com / wikipage?title = Single-Click%20Editingでは発生しません。
ジャスティンサイモン、

2
複雑すぎる。私の答えを見てください。:)
Konstantin Salavatov

1
5年後も、このコードは社会生活の時間を節約します:)いくつかの単純な要件については、@ KonstantinSalavatovソリューションで十分です。私の場合、コードをMikeのソリューションと混合してハンドラーとの動的イベント関連付けを実現しました。グリッドには動的な列数があり、特定のセルを1回クリックするだけでデータベースに変更を保存する必要があります。
Fer R

8

ここにはもっと簡単な解決策があります。

          <DataGridTemplateColumn MinWidth="20" >
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Grid>
                            <CheckBox VerticalAlignment="Center" HorizontalAlignment="Center"/>
                        </Grid>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

を使用DataGridCheckBoxColumnして実装する場合、最初のクリックはフォーカスすることであり、2番目のクリックはチェックすることです。

ただしDataGridTemplateColumn、実装に使用するのはワンクリックのみです。

DataGridComboboxBoxColumnによる使用と実装の違いDataGridTemplateColumnも同様です。


私には良い説明があり、すぐに動作しました、ありがとう!
AstralisSomnium

8

私はこれで解決しました:

<DataGridTemplateColumn>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <Viewbox Height="25">
                <CheckBox IsChecked="{Binding TheProperty, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
            </Viewbox>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

シングルクリックでアクティブなチェックボックス!


2
CheckBoxをViewBoxでラップする必要はありませんでしたが、この答えはうまくいきました。
JGeerWM 2017年

3
これは、受け入れられた答えよりもはるかに明確な解決策です。Viewboxも必要ありません。これが定義済みのチェックボックス列よりもうまく機能するのはおかしい。
ケンジャラ2017年

6

上の基本ジム・アドルノの答えは、彼のポストのコメント、これが持つソリューションですMultiTrigger

<Style TargetType="DataGridCell">
  <Style.Triggers>
    <MultiTrigger>
      <MultiTrigger.Conditions>
    <Condition Property="IsReadOnly" Value="False" />
    <Condition Property="IsMouseOver" Value="True" />
      </MultiTrigger.Conditions>
      <Setter Property="IsEditing" Value="True" />
    </MultiTrigger>
  </Style.Triggers>
</Style>

5

さらに別の簡単な解決策は、このスタイルをDataGridColumnに追加することです。スタイルの本体は空にすることができます。

<DataGridCheckBoxColumn>
     <DataGridCheckBoxColumn.ElementStyle>
          <Style TargetType="CheckBox">
           </Style>
     </DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>

2
スペースバーを押してチェック/チェックを外すと、チェックボックスが左から中央に移動します。スタイルに<Setter Property = "Horizo​​ntalAlignment" Value = "Center" />を追加すると、CheckBoxが移動しなくなります。
YantingChen

1
<Style x:Key="StilCelula" TargetType="DataGridCell"> 
<Style.Triggers>
 <Trigger Property="IsMouseOver" Value="True">
   <Setter Property="IsEditing" 
     Value="{Binding RelativeSource={x:Static RelativeSource.Self}, 
     Converter={StaticResource CheckBoxColumnToEditingConvertor}}" />
 </Trigger>
</Style.Triggers>
<Style>
Imports System.Globalization
Public Class CheckBoxColumnToEditingConvertor
    Implements IValueConverter
    Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.Convert
        Try

            Return TypeOf TryCast(value, DataGridCell).Column Is DataGridCheckBoxColumn
        Catch ex As Exception
            Return Visibility.Collapsed
        End Try
    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
        Throw New NotImplementedException()
    End Function
End Class
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.