Как выполнить выбор одного клика в WPF DataGrid?

У меня есть DataGrid с первым столбцом в виде текстового столбца и вторым столбцом в качестве столбца CheckBox. Что я хочу, если я нажму на флажок. Это должно быть проверено.

Но для выбора требуется два щелчка, для первого щелчка выбирается ячейка, для второго щелчка устанавливается флажок. Как сделать так, чтобы флажок был отмечен / снят одним щелчком мыши.

Я использую WPF 4.0. Столбцы в DataGrid создаются автоматически.

 surfen20 дек. 2011 г., 02:00
Дубликат:stackoverflow.com/questions/1225836/..., но у этого есть лучшее название

Ответы на вопрос(11)

флажок внутриDataGridTemplateColumn и установитьUpdateSourceTrigger=PropertyChanged.

<DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
        <CheckBox IsChecked="{Binding Path=IsSelected, UpdateSourceTrigger=PropertyChanged}" />
    </DataTemplate>
</DataGridTemplateColumn.CellTemplate>
 user145426505 нояб. 2014 г., 16:42
Это также работает для ComboBox. Как в: пути, лучше, чем DataGridComboBoxColumn.
 Yola11 мар. 2015 г., 12:45
Это не так, когда я использую пробел, чтобы отметить / снять флажок и стрелки, чтобы перейти к другой ячейке.
 Tod18 окт. 2011 г., 00:42
ВАУ - я рад, что прочитал до конца. Это работает отлично и значительно менее сложно, IMO это должно быть отмечено как ответ.
 bugfixr16 февр. 2012 г., 15:13
У меня не работает в WPF 4.0
 AstralisSomnium06 июн. 2018 г., 09:49
Я интерпретировал этот ответ, что вы должны связать «IsSelected», но этоне правда! Вы можете просто использоватьDataGridTemplateColumn.CellTemplate сваш собственный Binding и это будет работать !!Ответ из @ weidian-huang помог мне понять это, спасибо!
<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

которые я нашел на других сайтах, но ни одно из них мне не помогло. В итоге я создал следующее решение.

Я создал свой собственный элемент управления, унаследованный от 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и переключить это значение.

Кажется, это работает для меня и является хорошим, легко используемым решением.

Обидно, что мынеобходимость написать код, чтобы сделать это все же. Объяснение того, что первый щелчок мыши (на CheckBox в DataGrid) «игнорируется», поскольку WPF использует его для перевода строки в режим редактирования, может показаться логичным, но в реальности это идет вразрез с тем, как работает каждое реальное приложение.

Если пользователь видит флажок на своем экране, он должен иметь возможность щелкнуть по нему один раз, чтобы поставить / снять галочку. Конец истории.

 Guge18 февр. 2011 г., 00:27
Спасибо, я попробовал кучу "решений", но это первое, что, кажется, действительно работает каждый раз. И это прекрасно вписывается в мою архитектуру приложений.
 Konstantin Salavatov04 нояб. 2011 г., 10:59
слишком сложно. смотри мой ответ. :)
 Fer R24 янв. 2017 г., 03:12
Через 5 лет этот код все еще экономит время на социальную жизнь :) Для некоторых простых требований достаточно решения @KonstantinSalavatov. В моем случае я смешал свой код с решением Майка для достижения динамической ассоциации событий с обработчиками, моя сетка имеет динамическое количество столбцов, и один щелчок в определенной ячейке должен сохранять изменения в базе данных.
 Justin Simon10 мая 2011 г., 06:47
Это решение приводит к проблемам с обновлением привязки, тогда как приведенная здесь:wpf.codeplex.com/wikipage?title=Single-Click%20Editing не.
 Mike Gledhill21 февр. 2011 г., 10:36
@ Guge - Отличный материал, спасибо за отзыв.

указанном в ответе Гоблина, но изменено для работы в .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;
    }
 Jiří Skála14 окт. 2015 г., 12:17
@ user3690202 Это как DoEvents в Windows.Forms. После вызова BeginEdit вам нужно подождать, пока ячейка действительно перейдет в режим редактирования.
 user369020217 июл. 2015 г., 07:58
Зачем вам нужно отправить пустое действие?
 Number830 дек. 2016 г., 18:14
Это прекрасно работает для флажка, но мешает мой обработчик DataGrid_OnSelectedCellsChanged () ... Обработчик называетсяне при нажатии на выпадающий список, но при нажатии во второй раз.
 user369020214 окт. 2015 г., 20:02
@ JiříSkála - Я не помню, чтобы мне приходилось делать это в моих решениях этой проблемы, но я понимаю, что вы говорите - спасибо!
 BrokeMyLegBiking15 янв. 2012 г., 15:49
Это решение работало лучше всего для меня. Моя связанная ViewModel не обновлялась с другими решениями.
 Angel10 июл. 2014 г., 09:20
@surfen, мне нужно разместить вышеупомянутый стиль и код на каждой странице и ее коде, если у меня много страниц с сеткой данных. Можно ли использовать стиль и код в общем месте вместо того, чтобы создавать его в каждая страница
 Number830 дек. 2016 г., 19:55
Попытка добавить это программно через стиль:cellStyle.Setters.Add(new EventSetter(DataGridCell.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(CheckBoxDataGridCell_PreviewMouseLeftButtonDown)));        Вызывается обработчик, но cell.Content - ContentPresenter, и я не могу найти CheckBox. Предложения?

я знаю, что это довольно старый вопрос, но я все еще думал, что попытаюсь ответить на него.

У меня была такая же проблема пару дней назад, и я нашел неожиданно короткое решение для нее (см.этот блог). По сути, все, что вам нужно сделать, это заменить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; таким образом, он эффективно удерживает вас от обременения вашего кода дополнительной логикой пользовательского интерфейса и помогает поддерживать ваш статус в глазах фанатов MVVM;).

 Dan09 сент. 2014 г., 01:27
круто, это сработало для меня.
 user369020217 июл. 2015 г., 07:59
Проблема в том, что если вы сделаете это со столбцами со списком, маленькая выпадающая кнопка будет видна для всех ячеек в этом столбце, все время. Не только когда вы нажимаете на ячейку.
 Don Herod03 июл. 2014 г., 22:52
Это похоже на ответ Константина Салаватова, и этот работал для меня. +1 за включение примера кода, где его нет. Спасибо за хороший ответ на старый вопрос.

Я решил с этим:

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

Флажок активен по одному клику!

 JGeerWM28 мар. 2017 г., 16:40
Мне не нужно было оборачивать флажок в ViewBox, но этот ответ работал для меня.
 kenjara08 июн. 2017 г., 17:41
Для меня это гораздо более чистое решение, чем принятый ответ. Нет необходимости для Viewbox либо. Забавно, как это работает лучше, чем определенный столбец Checkbox.

вашего стиля может быть пустым.

<DataGridCheckBoxColumn>
     <DataGridCheckBoxColumn.ElementStyle>
          <Style TargetType="CheckBox">
           </Style>
     </DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>
 YantingChen20 апр. 2018 г., 10:48
Нажатие на клавишу пробела для проверки / снятия галочки переместит CheckBox слева на середину. Добавление <Setter Property = "HorizontalAlignment" Value = "Center" /> в стиле предотвратит перемещение CheckBox.

ДелатьОтвет Константина Салаватова работать сAutoGenerateColumnsдобавить обработчик события вDataGrid«sAutoGeneratingColumn со следующим кодом:

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автоматически сгенерированные столбцы флажков будут доступны для редактирования одним щелчком.

 el2iot230 июл. 2014 г., 21:06
Спасибо за то, что заполнили автоматически созданный подход к колонкам, это легко указывает мне подходящее направление.

Я решил это с помощью следующего стиля:

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

Конечно, это можно адаптировать для конкретных столбцов ...

 Mike de Klerk20 нояб. 2012 г., 10:34
Большой! Это сделало это для меня.
 MarcE30 мая 2011 г., 20:27
Ницца. Я изменил его на MultiTrigger и добавил условие для ReadOnly = False, но базовый подход работал для моего простого случая, когда навигация с помощью клавиатуры не важна.
 Alkampfer13 дек. 2012 г., 13:03
Я решил использовать триггер на DataGridRow, а не ячейку и принудительно установить IsSelected на true, когда мышь нажата, это не лучший подход, но он работает.
 B.K.12 окт. 2013 г., 03:13
Довольно аккуратный ответ.
 FooBarTheLittle02 сент. 2014 г., 17:16
Это самый чистый способ, который я видел до сих пор! Ницца! (для IsReadOnly = "True" MultiTrigger сделает эту работу)
 Alkampfer13 дек. 2012 г., 12:12
Добавление этого стиля в мою сетку вызывает исключение операции, которая недопустима, пока используется ItemSource. Получите доступ к элементам и измените их с помощью ItemsControl.ItemsSource.
 jHilscher18 авг. 2016 г., 08:41
Это решение имеет некоторое неожиданное / нежелательное поведение. Увидетьstackoverflow.com/q/39004317/2881450
 AQuirky23 апр. 2017 г., 04:59
Чтобы привязка работала, вам понадобится UpdateSourceTrigger = PropertyChanged

Основывается наДжим Адорно ответ и комментарии на его пост, это решение с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>

Здесь есть гораздо более простое решение.

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

Если вы используетеDataGridCheckBoxColumn для реализации, первый щелчок, чтобы сосредоточиться, второй щелчок, чтобы проверить.

Но используяDataGridTemplateColumn Для реализации нужен только один клик.

Разница в использованииDataGridComboboxBoxColumn и реализацияDataGridTemplateColumn тоже похоже.

 AstralisSomnium06 июн. 2018 г., 09:53
Хорошее объяснение для меня и работает мгновенно, спасибо!

Ваш ответ на вопрос