ฉันต้องการให้ผู้ใช้สามารถนำเซลล์เข้าสู่โหมดแก้ไขและไฮไลต์แถวที่เซลล์นั้นอยู่ในคลิกเดียว ตามค่าเริ่มต้นนี่คือดับเบิลคลิก
ฉันจะลบล้างหรือใช้สิ่งนี้ได้อย่างไร
ฉันต้องการให้ผู้ใช้สามารถนำเซลล์เข้าสู่โหมดแก้ไขและไฮไลต์แถวที่เซลล์นั้นอยู่ในคลิกเดียว ตามค่าเริ่มต้นนี่คือดับเบิลคลิก
ฉันจะลบล้างหรือใช้สิ่งนี้ได้อย่างไร
คำตอบ:
นี่คือวิธีที่ฉันแก้ไขปัญหานี้:
<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 (มีวัตถุบุคคลจำลอง)
ความมหัศจรรย์ที่เกิดขึ้นที่นั่น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 แต่มันกลับเป็นโมฆะสำหรับฉัน
คำตอบจาก Micael Bergeron เป็นการเริ่มต้นที่ดีสำหรับฉันในการหาวิธีแก้ปัญหาที่เหมาะกับฉัน เพื่อให้สามารถแก้ไขด้วยคลิกเดียวสำหรับเซลล์ในแถวเดียวกันกับที่อยู่ในโหมดแก้ไขแล้วฉันต้องปรับแต่งเล็กน้อย การใช้ SelectionUnit Cell ไม่ใช่ตัวเลือกสำหรับฉัน
แทนที่จะใช้ DataGridCell.Selected Event ซึ่งจะเริ่มทำงานเพียงครั้งแรกที่มีการคลิกเซลล์ของแถวฉันใช้ DataGridCell.GotFocus Event
<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 โดยใช้ Style ที่กำหนดใน 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.
}
ฉันชอบวิธีนี้ตามคำแนะนำของDušanKnežević คุณคลิกนั่นแหล่ะ))
<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 เมื่อเมาส์อยู่เหนือมัน มันแก้ปัญหาส่วนใหญ่ของฉันได้ มันทำงานร่วมกับ comboboxes ด้วย
<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 qualifier ใน XAML ซ้ำซ้อน
ปัญหาหลักที่ฉันพบกับคำตอบคือการทำงานของคีย์ Enter เสีย Enter ควรย้ายคุณไปยังเซลล์ถัดไปด้านล่างเซลล์ปัจจุบันในลักษณะการทำงานของ DataGrid ปกติ อย่างไรก็ตามสิ่งที่เกิดขึ้นจริงเบื้องหลังคือเหตุการณ์ GotFocus จะถูกเรียกสองครั้ง เมื่อเซลล์ปัจจุบันสูญเสียโฟกัสและเมื่อเซลล์ใหม่ได้รับโฟกัส แต่ตราบใดที่มีการเรียกใช้ BeginEdit ในเซลล์แรกนั้นเซลล์ถัดไปจะไม่ถูกเปิดใช้งาน ผลลัพธ์คือคุณมีการแก้ไขด้วยคลิกเดียว แต่ใครก็ตามที่ไม่ได้คลิกบนกริดจริงๆจะไม่สะดวกและผู้ออกแบบส่วนต่อประสานผู้ใช้ไม่ควรคิดว่าผู้ใช้ทั้งหมดกำลังใช้ mouses (ผู้ใช้แป้นพิมพ์สามารถจัดการกับมันได้โดยใช้ 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 Method และส่งคืนตัวควบคุม Textbox ที่นั่นจะทำให้ Method สำหรับการสร้าง Editing Element ไม่เคยถูกเรียก ในโครงการอื่นฉันใช้สิ่งนี้เพื่อติดตั้งคอลัมน์ Datepicker ดังนั้นสิ่งนี้ควรใช้ได้กับช่องทำเครื่องหมายและกล่องคอมโบด้วย
สิ่งนี้ดูเหมือนจะไม่ส่งผลกระทบต่อพฤติกรรมของดาต้ากริดที่เหลือ .. อย่างน้อยฉันก็ยังไม่สังเกตเห็นผลข้างเคียงใด ๆ และจนถึงตอนนี้ฉันยังไม่ได้รับข้อเสนอแนะเชิงลบใด ๆ
วิธีแก้ปัญหาง่ายๆหากคุณพอใจกับการที่เซลล์ของคุณยังคงเป็นช่องข้อความ (ไม่มีการแยกแยะระหว่างโหมดแก้ไขและโหมดไม่แก้ไข) การแก้ไขด้วยการคลิกเพียงครั้งเดียวนี้ทำงานได้ทันที สิ่งนี้ใช้ได้กับองค์ประกอบอื่น ๆ เช่นคอมโบบ็อกซ์และปุ่มเช่นกัน มิฉะนั้นให้ใช้วิธีแก้ไขปัญหาด้านล่างการอัปเดต
<DataGridTemplateColumn Header="My Column header">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding MyProperty } />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
ฉันลองทุกอย่างที่พบที่นี่และใน Google และแม้แต่ลองสร้างเวอร์ชันของตัวเอง แต่ทุกคำตอบ / วิธีแก้ปัญหาทำงานกับคอลัมน์กล่องข้อความเป็นหลัก แต่ใช้ไม่ได้กับองค์ประกอบอื่น ๆ ทั้งหมด (ช่องทำเครื่องหมายคอมโบบ็อกซ์คอลัมน์ปุ่ม) หรือแม้แต่ทำให้คอลัมน์องค์ประกอบอื่น ๆ เสียหายหรือมีผลข้างเคียงอื่น ๆ ขอบคุณไมโครซอฟท์ที่ทำให้ดาต้ากริดมีพฤติกรรมที่น่าเกลียดและบังคับให้เราสร้างแฮ็กเหล่านั้น ด้วยเหตุนี้ฉันจึงตัดสินใจสร้างเวอร์ชันที่สามารถใช้กับสไตล์กับคอลัมน์กล่องข้อความได้โดยตรงโดยไม่ส่งผลกระทบต่อคอลัมน์อื่น ๆ
ฉันใช้วิธีแก้ปัญหานี้และคำตอบของ @ และแก้ไขให้เป็นพฤติกรรมที่แนบมา 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;
}