사용자가 셀을 편집 모드로 전환하고 한 번의 클릭으로 셀이 포함 된 행을 강조 표시 할 수 있기를 바랍니다. 기본적으로 두 번 클릭합니다.
이것을 재정의하거나 구현하려면 어떻게해야합니까?
사용자가 셀을 편집 모드로 전환하고 한 번의 클릭으로 셀이 포함 된 행을 강조 표시 할 수 있기를 바랍니다. 기본적으로 두 번 클릭합니다.
이것을 재정의하거나 구현하려면 어떻게해야합니까?
답변:
이 문제를 해결 한 방법은 다음과 같습니다.
<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 셀의 Selected Event를 연결하고 DataGrid에서 BeginEdit ()를 호출합니다.
다음은 이벤트 처리기의 코드입니다.
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을 반환합니다.
Micael Bergeron의 답변은 저에게 적합한 솔루션을 찾는 좋은 출발점이되었습니다. 이미 편집 모드에있는 동일한 행의 셀에 대해서도 한 번 클릭 편집을 허용하려면 약간 조정해야했습니다. 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;
}
From : 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 의 솔루션은 저에게 잘 맞았지만 ResourceDictionary에 정의 된 Style을 사용하여 모든 DataGrid에 대해 활성화했습니다. 리소스 사전에서 핸들러를 사용하려면 여기에 코드 숨김 파일을 추가해야합니다. 방법은 다음과 같습니다.
다음은 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šan Knež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>
마우스가 위에있을 때 DataGridCell의 IsEditing 속성을 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가 실제로 DataGridCell이 아닌 DataGrid에 대해 실제로 호출된다는 것입니다. XAML의 DataGridCell 한정자는 중복됩니다.
대답에서 찾은 주요 문제는 Enter 키 동작이 손상되었다는 것입니다. Enter는 정상적인 DataGrid 동작에서 현재 셀 아래의 다음 셀로 이동해야합니다. 그러나 실제로 장면 뒤에서 일어나는 일은 GotFocus 이벤트가 두 번 호출된다는 것입니다. 현재 셀이 포커스를 잃으면 한 번, 새로운 셀이 포커스를 얻었을 때. 그러나 BeginEdit가 첫 번째 셀에서 호출되는 한 다음 셀은 활성화되지 않습니다. 결론은 한 번의 클릭으로 편집 할 수 있지만 말 그대로 그리드를 클릭하지 않는 사람은 누구나 불편할 것이며 사용자 인터페이스 디자이너는 모든 사용자가 마우스를 사용하고 있다고 가정해서는 안됩니다. (키보드 사용자는 Tab을 사용하여 주변을 둘러 볼 수 있지만 그래도 필요하지 않은 농구를 뛰어 넘고 있음을 의미합니다.)
그래서이 문제에 대한 해결책은? 셀에 대한 이벤트 KeyDown을 처리하고 키가 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>
보시다시피 DataGridBoundColumn에서 모든 것을 상속하는 내 자신의 DataGridTextColumn을 작성했습니다. GenerateElement 메서드를 재정의하고 바로 텍스트 상자 컨트롤을 반환하면 편집 요소를 생성하는 메서드가 호출되지 않습니다. 다른 프로젝트에서는 이것을 사용하여 Datepicker 열을 구현 했으므로 확인란과 콤보 상자에서도 작동합니다.
이것은 나머지 데이터 그리드 동작에 영향을 미치지 않는 것 같습니다. 적어도 부작용을 발견하지 못했고 지금까지 부정적인 피드백을받지 못했습니다.
셀이 텍스트 상자 (편집 모드와 비 편집 모드를 구분하지 않음)로 유지되는 것이 괜찮다면 간단한 솔루션입니다. 이런 식으로 한 번의 클릭으로 편집이 즉시 작동합니다. 이것은 콤보 상자 및 버튼과 같은 다른 요소에서도 작동합니다. 그렇지 않으면 업데이트 아래의 솔루션을 사용하십시오.
<DataGridTemplateColumn Header="My Column header">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding MyProperty } />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
나는 여기와 Google에서 찾은 모든 것을 시도했으며 심지어 내 버전을 만들어 보았습니다. 그러나 모든 답변 / 솔루션은 주로 텍스트 상자 열에 대해 작동했지만 다른 모든 요소 (체크 박스, 콤보 상자, 버튼 열)와 함께 작동하지 않았거나 다른 요소 열을 손상 시키거나 다른 부작용이있었습니다. 데이터 그리드가 그처럼 추악한 방식으로 동작하도록 만들어서 우리가 그러한 해킹을 만들도록 강요 해 주신 마이크로 소프트에게 감사드립니다. 그래서 다른 열에 영향을주지 않고 텍스트 상자 열에 직접 스타일을 적용 할 수있는 버전을 만들기로 결정했습니다.
이 솔루션과 @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;
}