programing

WPF DataGrid에서 원클릭 편집

magicmemo 2023. 4. 16. 14:50
반응형

WPF DataGrid에서 원클릭 편집

사용자가 셀을 편집 모드로 전환하고 셀이 포함된 행을 클릭 한 번으로 강조 표시할 수 있도록 합니다.기본적으로는 더블클릭입니다.

이를 덮어쓰거나 구현하려면 어떻게 해야 합니까?

이 문제를 해결한 방법은 다음과 같습니다.

<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는 CollectionView에 바인딩되어 있습니다.원본(더미 사용자 개체 포함).

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);
    }
}

Micael Bergeron의 답변은 나에게 맞는 해결책을 찾기 위한 좋은 시작이었습니다.이미 편집 모드로 되어 있는 같은 행의 셀도 원클릭으로 편집할 수 있도록 하려면 조금 조정해야 합니다.선택 사용유닛 셀은 내게 선택권이 없었다.

DataGridCell을 사용하는 대신.행의 셀을 처음 클릭할 때만 실행되는 선택된 이벤트에서는 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의 솔루션은 잘 작동했지만 ResourceDictionary에 정의된 스타일을 사용하여 모든 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.
}

마우스를 올려놓았을 때 DataGridCell의 IsEditing 속성을 True로 설정하는 트리거를 추가하여 해결했습니다.그게 내 문제를 대부분 해결했어콤보박스와도 잘 어울려요.

<Style TargetType="DataGridCell">
     <Style.Triggers>
         <Trigger Property="IsMouseOver" Value="True">
             <Setter Property="IsEditing" Value="True" />
         </Trigger>
     </Style.Triggers>
 </Style>

나는 Dushan Knejevich의 제안에 근거한 이 방법을 선호한다.를 클릭하면 끝입니다.

<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>

MVVM에서 클릭 한 번으로 편집 셀을 찾고 있는데, 이것도 다른 방법입니다.

  1. 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>
    
  2. Edit Cell On Single Click 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);
        }
    }
    

으악!

나는 Dushan Knejevich의 솔루션을 약간 편집한다.

<DataGrid.Resources>
   <Style x:Key="ddlStyle" TargetType="DataGridCell">
      <Style.Triggers>
         <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="IsEditing" Value="True" />
         </Trigger>
      </Style.Triggers>
    </Style>
</DataGrid.Resources>

그리고 나의 욕망란에 스타일을 적용시킨다.

<DataGridComboBoxColumn CellStyle="{StaticResource ddlStyle}">

사용자 2134678의 답변에는 두 가지 문제가 있습니다.하나는 매우 경미하고 기능적인 효과가 없다.다른 하나는 꽤 중요하다.

첫 번째 문제는 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는 기본 제공 구현의 기본 동작을 변경하지 않으면서 클릭 한 번으로 편집할 수 있습니다.

이 답변들 중 몇 개는 저에게 영감을 주었고, 이 블로그 게시물에도 영감을 주었지만, 각각의 답변에는 부족한 점이 있었습니다.제품의 가장 좋은 점을 조합해, 유저 익스피리언스를 정확하게 만족시키는, 꽤 우아한 솔루션을 생각해 냈습니다.

여기에는 C# 9 구문이 사용되므로 어디에서나 정상적으로 동작합니다.단, 프로젝트 파일에서 이 구문을 설정해야 할 수도 있습니다.

<LangVersion>9</LangVersion>

첫 번째로 클릭 수가 3개에서 2개로 줄어듭니다.

프로젝트에 이 클래스 추가:

public static class WpfHelpers
{
    internal static void DataGridPreviewMouseLeftButtonDownEvent(object sender, RoutedEventArgs e)
    {
        // The original source for this was inspired by https://softwaremechanik.wordpress.com/2013/10/02/how-to-make-all-wpf-datagrid-cells-have-a-single-click-to-edit/
        DataGridCell? cell = e is MouseButtonEventArgs { OriginalSource: UIElement clickTarget } ? FindVisualParent<DataGridCell>(clickTarget) : null;
        if (cell is { IsEditing: false, IsReadOnly: false })
        {
            if (!cell.IsFocused)
            {
                cell.Focus();
            }

            if (FindVisualParent<DataGrid>(cell) is DataGrid dataGrid)
            {
                if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
                {
                    if (!cell.IsSelected)
                    {
                        cell.IsSelected = true;
                    }
                }
                else
                {
                    if (FindVisualParent<DataGridRow>(cell) is DataGridRow { IsSelected: false } row)
                    {
                        row.IsSelected = true;
                    }
                }
            }
        }
    }

    internal static T? GetFirstChildByType<T>(DependencyObject prop)
        where T : DependencyObject
    {
        int count = VisualTreeHelper.GetChildrenCount(prop);
        for (int i = 0; i < count; i++)
        {
            if (VisualTreeHelper.GetChild(prop, i) is DependencyObject child)
            {
                T? typedChild = child as T ?? GetFirstChildByType<T>(child);
                if (typedChild is object)
                {
                    return typedChild;
                }
            }
        }

        return null;
    }

    private static T? FindVisualParent<T>(UIElement element)
        where T : UIElement
    {
        UIElement? parent = element;
        while (parent is object)
        {
            if (parent is T correctlyTyped)
            {
                return correctlyTyped;
            }

            parent = VisualTreeHelper.GetParent(parent) as UIElement;
        }

        return null;
    }
}

이 항목을 에 추가합니다.App.xaml.cs파일:

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);

    EventManager.RegisterClassHandler(
        typeof(DataGrid),
        DataGrid.PreviewMouseLeftButtonDownEvent,
        new RoutedEventHandler(WpfHelpers.DataGridPreviewMouseLeftButtonDownEvent));
}

다음으로 2대 1로 계산하겠습니다.

이 값을 페이지 뒤에 있는 코드에 추가합니다.DataGrid:

private void TransactionDataGrid_PreparingCellForEdit(object sender, DataGridPreparingCellForEditEventArgs e)
{
    WpfHelpers.GetFirstChildByType<Control>(e.EditingElement)?.Focus();
}

배선(XAML):PreparingCellForEdit="TransactionDataGrid_PreparingCellForEdit"

파티에 조금 늦은 건 알지만 같은 문제가 있어서 다른 해결책을 생각해 냈어요.

     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 메서드를 덮어쓰고 텍스트 상자 컨트롤을 바로 반환하면 편집 요소를 생성하는 메서드가 호출되지 않습니다.다른 프로젝트에서는 Datepicker 컬럼을 구현하기 위해 이것을 사용했기 때문에 체크박스나 콤보박스에서도 사용할 수 있을 것입니다.

이것은 나머지 데이터그리드 행동에 영향을 주지 않는 것으로 보인다.적어도 지금까지 부작용이나 부정적인 피드백을 받은 적은 없습니다.

갱신하다

셀이 텍스트 상자(편집 모드와 비편집 모드를 구분하지 않음)를 유지해도 문제가 없는 경우 간단한 해결 방법입니다.이렇게 하면 클릭 한 번으로 편집할 수 있습니다.이것은 콤보 박스나 버튼등의 다른 요소와도 동작합니다.그렇지 않으면 업데이트 아래의 솔루션을 사용하십시오.

<DataGridTemplateColumn Header="My Column header">
   <DataGridTemplateColumn.CellTemplate>
      <DataTemplate>
         <TextBox Text="{Binding MyProperty } />
      </DataTemplate>
   </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

업데이트 종료

랜트

나는 여기와 구글에서 찾은 모든 것을 시도해 보았고, 심지어 내 버전을 만들어 보기도 했다.그러나 모든 응답/솔루션은 주로 텍스트 상자 열에서 작동하지만 다른 모든 요소(체크박스, 콤보 상자, 버튼 열)에서는 작동하지 않거나 다른 요소 열에서도 작동되지 않거나 일부 부작용이 있었습니다.데이터그리드에게 추한 행동을 하게 하고 우리에게 해커들을 만들도록 강요해준 마이크로소프트에게 감사한다.그래서 다른 열에 영향을 주지 않고 직접 텍스트 상자 열에 스타일을 적용할 수 있는 버전을 만들기로 했습니다.

특징들

  • 뒤에 코드는 없습니다.MVVM 친화적
  • 이 기능은 동일하거나 다른 행에서 다른 텍스트 상자 셀을 클릭할 때 작동합니다.
  • Tab 키와 ENTER 키는 기능합니다.
  • 다른 열에는 영향을 주지 않습니다.

원천

이 솔루션과 @m-y의 답변을 사용하여 첨부 동작으로 수정했습니다.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>

를 적용하다CellStyleDataGridTextColumns음음음같 뭇매하다

<DataGrid ItemsSource="{Binding MyData}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="My Header" Binding="{Binding Comment}" CellStyle="{StaticResource SingleClickEditStyle}" />         
    </DataGrid.Columns>
</DataGrid>

이제 이 클래스를 MainViewModel(또는 다른 네임스페이스)과 같은 네임스페이스에 추가합니다. 그 접두사 '아니다'가 아닌 네임스페이스 .local) behavior 것을 부착행동의 추악한 보일러 플레이트 코드 세계에 오신 것을 환영합니다.

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;

 }

언급URL : https://stackoverflow.com/questions/3426765/single-click-edit-in-wpf-datagrid

반응형