Я хочу, чтобы пользователь мог перевести ячейку в режим редактирования и выделить строку, в которой содержится ячейка, одним щелчком мыши. По умолчанию это двойной щелчок.
Как мне переопределить или реализовать это?
Я хочу, чтобы пользователь мог перевести ячейку в режим редактирования и выделить строку, в которой содержится ячейка, одним щелчком мыши. По умолчанию это двойной щелчок.
Как мне переопределить или реализовать это?
Ответы:
Вот как я решил эту проблему:
<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 и вызываю BeginEdit () в DataGrid.
Вот код обработчика событий:
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);
}
}
SelectionUnit
свойства DataGrid значение Cell
.
grd.BeginEdit(e)
я хочу, чтобы текстовое поле в этой ячейке было в фокусе. Как я могу это сделать? Я попытался вызвать FindName("txtBox")
как DataGridCell, так и DataGrid, но он вернул мне значение null.
Ответ от Микаэля Бержерона стал для меня хорошим началом поиска решения, которое работает для меня. Чтобы разрешить редактирование одним щелчком также для ячеек в той же строке, которая уже находится в режиме редактирования, мне пришлось немного отрегулировать ее. Использование 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;
}
От: 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;
}
Решение из http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing отлично сработало для меня, но я включил его для каждой DataGrid, используя стиль, определенный в ResourceDictionary. Чтобы использовать обработчики в словарях ресурсов, вам необходимо добавить в него файл кода программной части. Вот как это сделать:
Это словарь ресурсов 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.
}
Я предпочитаю этот способ, основанный на предложении Душана Кнежевича. нажимаешь вот и все))
<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>
Я решил эту проблему, добавив триггер, который устанавливает для свойства IsEditing DataGridCell значение True при наведении указателя мыши на него. Это решило большинство моих проблем. Он также работает с комбинированными списками.
<Style TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsEditing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
Я ищу редактирование ячейки одним щелчком мыши в MVVM, и это другой способ сделать это.
Добавление поведения в 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>
Класс 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);
}
}
Вуаля!
Есть две проблемы с ответом user2134678. Один очень незначительный и не имеет функционального воздействия. Другой довольно значительный.
Первая проблема заключается в том, что GotFocus на самом деле вызывается для DataGrid, а не для DataGridCell на практике. Квалификатор DataGridCell в XAML является избыточным.
Основная проблема, которую я обнаружил с ответом, заключается в том, что поведение клавиши Enter нарушено. Enter должен переместить вас в следующую ячейку под текущей ячейкой в обычном поведении DataGrid. Однако на самом деле за кулисами происходит то, что событие GotFocus будет вызываться дважды. Однажды текущая ячейка теряет фокус, а однажды новая ячейка получает фокус. Но пока 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, который не изменил фундаментального поведения готовой реализации, но поддерживает редактирование одним щелчком мыши.
Я знаю, что немного опаздываю на вечеринку, но у меня была та же проблема, и я нашел другое решение:
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>
Как видите, я написал свой собственный столбец DataGridTextColumn, унаследовавший все от DataGridBoundColumn. Переопределяя метод GenerateElement и возвращая элемент управления Textbox прямо здесь, метод для создания элемента редактирования никогда не вызывается. В другом проекте я использовал это для реализации столбца Datepicker, поэтому он должен работать и для флажков и полей со списком.
Похоже, это не влияет на поведение остальных датагридов ... По крайней мере, я не заметил никаких побочных эффектов и пока не получил никаких отрицательных отзывов.
Простое решение, если вас устраивает, что ваша ячейка остается текстовым полем (без различия между режимом редактирования и режимом без редактирования). Таким образом, редактирование одним щелчком работает прямо из коробки. Это работает и с другими элементами, такими как поле со списком и кнопки. В противном случае используйте решение под обновлением.
<DataGridTemplateColumn Header="My Column header">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding MyProperty } />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Я перепробовал все, что нашел здесь и в Google, и даже попытался создать свои собственные версии. Но каждый ответ / решение работал в основном для столбцов текстовых полей, но не работал со всеми другими элементами (флажками, полями со списком, столбцами кнопок) или даже нарушал эти столбцы других элементов или имел некоторые другие побочные эффекты. Спасибо, microsoft, за то, что datagrid заставили вести себя таким уродливым образом и заставили нас создавать такие хаки. Из-за этого я решил создать версию, которая может быть применена со стилем к столбцу текстового поля напрямую, не затрагивая другие столбцы.
Я использовал это решение и ответ @ 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;
}
}
}
<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;
}