Я хочу, щоб користувач міг перевести клітинку в режим редагування та виділити рядок, в якому міститься клітинка, одним клацанням миші. За замовчуванням це подвійне клацання.
Як це замінити чи реалізувати?
Я хочу, щоб користувач міг перевести клітинку в режим редагування та виділити рядок, в якому міститься клітинка, одним клацанням миші. За замовчуванням це подвійне клацання.
Як це замінити чи реалізувати?
Відповіді:
Ось як я вирішив цю проблему:
<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)
я хочу, щоб TextBox у цій комірці мав фокус. Як я можу це зробити? Я спробував зателефонувати FindName("txtBox")
як DataGridCell, так і DataGrid, але він повертає для мене значення null.
Відповідь Мікаеля Бержерона став для мене хорошим початком пошуку рішення, яке працює на мене. Щоб дозволити редагування одним кліком також для клітинок у тому самому рядку, що вже перебуває в режимі редагування, мені довелося трохи його відрегулювати. Використання комірки SelectionUnit для мене не було можливості.
Замість того, щоб використовувати подію 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%20Редагування
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;
}