Tôi muốn người dùng có thể đặt ô vào chế độ chỉnh sửa và đánh dấu hàng chứa ô chỉ bằng một cú nhấp chuột. Theo mặc định, đây là nhấp đúp.
Làm cách nào để ghi đè hoặc triển khai điều này?
Tôi muốn người dùng có thể đặt ô vào chế độ chỉnh sửa và đánh dấu hàng chứa ô chỉ bằng một cú nhấp chuột. Theo mặc định, đây là nhấp đúp.
Làm cách nào để ghi đè hoặc triển khai điều này?
Câu trả lời:
Đây là cách tôi giải quyết vấn đề này:
<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 này được liên kết với một CollectionViewSource (Chứa các đối tượng Người giả ).
Điều kỳ diệu xảy ra ở đó: DataGridCell.Selected = "DataGridCell_Selected" .
Tôi chỉ cần nối Sự kiện đã chọn của ô DataGrid và gọi BeginEdit () trên DataGrid.
Đây là mã phía sau cho trình xử lý sự kiện:
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
tính trên DataGrid thành Cell
.
grd.BeginEdit(e)
, tôi muốn TextBox trong ô đó có tiêu điểm. Làm thế nào tôi có thể làm điều đó? Tôi đã thử gọi FindName("txtBox")
trên cả DataGridCell và DataGrid, nhưng nó trả về null cho tôi.
Câu trả lời từ Micael Bergeron là một khởi đầu tốt để tôi tìm ra giải pháp phù hợp với mình. Để cho phép chỉnh sửa bằng một cú nhấp chuột cũng cho các Ô ở cùng hàng đã ở chế độ chỉnh sửa, tôi đã phải điều chỉnh nó một chút. Sử dụng SelectionUnit Cell không phải là lựa chọn cho tôi.
Thay vì sử dụng Sự kiện DataGridCell.Selected chỉ được kích hoạt lần đầu tiên một ô của hàng được nhấp vào, tôi đã sử dụng Sự kiện DataGridCell.GotFocus.
<DataGrid DataGridCell.GotFocus="DataGrid_CellGotFocus" />
Nếu bạn làm như vậy, bạn sẽ luôn có tiêu điểm ô chính xác và ở chế độ chỉnh sửa, nhưng không có điều khiển nào trong ô sẽ được lấy tiêu điểm, điều này tôi đã giải quyết như thế này
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;
}
Từ: http://wpf.codeplex.com/wikipage?title=Single-Click%20E Chỉnh sửa
XAML:
<!-- SINGLE CLICK EDITING -->
<Style TargetType="{x:Type dg:DataGridCell}">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown"></EventSetter>
</Style>
MÃ ẨN:
//
// 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;
}
Giải pháp từ http://wpf.codeplex.com/wikipage?title=Single-Click%20E Chỉnh sửa rất hiệu quả với tôi, nhưng tôi đã bật nó cho mọi DataGrid bằng cách sử dụng Kiểu được xác định trong ResourceDictionary. Để sử dụng trình xử lý trong từ điển tài nguyên, bạn cần thêm tệp mã phía sau vào đó. Đây là cách bạn làm điều đó:
Đây là Từ điển Tài nguyên 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>
Lưu ý đến thuộc tính x: Class trong phần tử gốc. Tạo một tệp lớp. Trong ví dụ này, nó sẽ là DataGridStyles.xaml.cs . Đặt mã này bên trong:
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.
}
tôi thích cách này hơn dựa trên gợi ý của Dušan Knežević. bạn nhấp vào đó là nó))
<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>
Tôi đã giải quyết nó bằng cách thêm một trình kích hoạt đặt thuộc tính IsE Chỉnh sửa của DataGridCell thành True khi con chuột ở trên nó. Nó đã giải quyết hầu hết các vấn đề của tôi. Nó cũng hoạt động với các hộp kết hợp.
<Style TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsEditing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
Tôi đang tìm cách chỉnh sửa ô chỉ với một cú nhấp chuột trong MVVM và đây là một cách khác để làm điều đó.
Thêm hành vi trong 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>
Lớp EditCellOnSingleClickBehavior mở rộng 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);
}
}
Thì đấy!
Có hai vấn đề với câu trả lời của người dùng2134678. Một là rất nhỏ và không có tác dụng chức năng. Cái khác là khá quan trọng.
Vấn đề đầu tiên là GotFocus thực sự được gọi chống lại DataGrid, không phải DataGridCell trong thực tế. Bộ định tính DataGridCell trong XAML là dư thừa.
Vấn đề chính mà tôi tìm thấy với câu trả lời là hành vi phím Enter bị hỏng. Enter sẽ chuyển bạn đến ô tiếp theo bên dưới ô hiện tại trong hành vi DataGrid bình thường. Tuy nhiên, những gì thực sự xảy ra ở hậu trường là sự kiện GotFocus sẽ được gọi hai lần. Một khi ô hiện tại mất tiêu điểm và một khi ô mới có được tiêu điểm. Nhưng miễn là BeginEdit được gọi trên ô đầu tiên đó, ô tiếp theo sẽ không bao giờ được kích hoạt. Kết quả là bạn có thể chỉnh sửa bằng một cú nhấp chuột, nhưng bất kỳ ai không thực sự nhấp vào lưới sẽ thấy bất tiện và nhà thiết kế giao diện người dùng không nên cho rằng tất cả người dùng đang sử dụng chuột. (Người dùng bàn phím có thể tìm thấy nó bằng cách sử dụng Tab, nhưng điều đó vẫn có nghĩa là họ đang nhảy qua các vòng mà họ không cần phải làm.)
Vậy giải pháp cho vấn đề này? Xử lý sự kiện KeyDown cho ô và nếu Key là phím Enter, hãy đặt cờ ngăn BeginEdit kích hoạt trên ô đầu tiên. Bây giờ phím Enter hoạt động như bình thường.
Để bắt đầu, hãy thêm Kiểu sau vào DataGrid của bạn:
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}" x:Key="SingleClickEditingCellStyle">
<EventSetter Event="KeyDown" Handler="DataGridCell_KeyDown" />
</Style>
</DataGrid.Resources>
Áp dụng kiểu đó cho thuộc tính "CellStyle" các cột mà bạn muốn bật một cú nhấp chuột.
Sau đó, trong đoạn mã phía sau, bạn có phần sau trong trình xử lý GotFocus của bạn (lưu ý rằng tôi đang sử dụng VB ở đây vì đó là những gì ứng dụng khách "yêu cầu lưới dữ liệu một lần nhấp" của chúng tôi muốn làm ngôn ngữ phát triển):
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)
.
.
.
Sau đó, bạn thêm trình xử lý của mình cho sự kiện 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
Bây giờ bạn có một DataGrid không thay đổi bất kỳ hành vi cơ bản nào của việc triển khai out-of-the-box và vẫn hỗ trợ chỉnh sửa bằng một cú nhấp chuột.
Tôi biết rằng tôi đến bữa tiệc hơi muộn nhưng tôi đã gặp phải vấn đề tương tự và đã đưa ra một giải pháp khác:
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>
Như bạn có thể thấy, tôi đã viết DataGridTextColumn của riêng mình kế thừa mọi thứ nôn DataGridBoundColumn. Bằng cách ghi đè Phương thức GenerateElement và trả về một điều khiển Hộp văn bản ngay tại đó, Phương thức tạo Phần tử chỉnh sửa không bao giờ được gọi. Trong một dự án khác, tôi đã sử dụng cột này để triển khai cột Người chọn ngày, vì vậy cột này cũng sẽ hoạt động với hộp kiểm và hộp tổ hợp.
Điều này dường như không ảnh hưởng đến phần còn lại của hành vi datagrids.. Ít nhất tôi đã không nhận thấy bất kỳ tác dụng phụ nào cũng như không nhận được bất kỳ phản hồi tiêu cực nào cho đến nay.
Một giải pháp đơn giản nếu bạn ổn là ô của bạn vẫn là một hộp văn bản (không phân biệt giữa chế độ chỉnh sửa và không chỉnh sửa). Bằng cách này, chỉnh sửa một cú nhấp chuột hoạt động hiệu quả. Điều này cũng hoạt động với các phần tử khác như combobox và các nút. Nếu không, hãy sử dụng giải pháp bên dưới bản cập nhật.
<DataGridTemplateColumn Header="My Column header">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding MyProperty } />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Tôi đã thử mọi thứ tôi tìm thấy ở đây và trên google và thậm chí đã thử tạo các phiên bản của riêng mình. Nhưng mọi câu trả lời / giải pháp đều hoạt động chủ yếu cho các cột hộp văn bản nhưng không hoạt động với tất cả các phần tử khác (hộp kiểm, hộp tổ hợp, cột nút), hoặc thậm chí phá vỡ các cột phần tử khác hoặc có một số tác dụng phụ khác. Cảm ơn microsoft đã làm cho datagrid hoạt động theo cách xấu xí đó và buộc chúng tôi phải tạo ra những bản hack đó. Do đó, tôi quyết định tạo một phiên bản có thể được áp dụng với một kiểu cho cột hộp văn bản trực tiếp mà không ảnh hưởng đến các cột khác.
Tôi đã sử dụng giải pháp này và câu trả lời của @ my và sửa đổi chúng thành một hành vi đính kèm. http://wpf-tutorial-net.blogspot.com/2016/05/wpf-datagrid-edit-cell-on-single-click.html
Thêm Phong cách này. Điều BasedOn
quan trọng là khi bạn sử dụng một số kiểu ưa thích cho datagrid của mình và bạn không muốn mất chúng.
<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>
Áp dụng kiểu với CellStyle
mỗi DataGridTextColumns
kiểu như thế này của bạn :
<DataGrid ItemsSource="{Binding MyData}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="My Header" Binding="{Binding Comment}" CellStyle="{StaticResource SingleClickEditStyle}" />
</DataGrid.Columns>
</DataGrid>
Và bây giờ hãy thêm lớp này vào cùng một không gian tên với MainViewModel của bạn (hoặc một Không gian tên khác. Nhưng sau đó, bạn sẽ cần sử dụng tiền tố không gian tên khác local
). Chào mừng đến với thế giới mã soạn sẵn xấu xí của các hành vi đính kèm.
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;
}