development

WPF DataGrid에서 한 번의 클릭으로 편집

big-blog 2020. 9. 14. 21:29
반응형

WPF DataGrid에서 한 번의 클릭으로 편집


사용자가 셀을 편집 모드로 전환하고 한 번의 클릭으로 셀이 포함 된 행을 강조 표시 할 수 있기를 바랍니다. 기본적으로 두 번 클릭합니다.

이것을 재정의하거나 구현하는 방법은 무엇입니까?


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

<DataGrid DataGridCell.Selected="DataGrid_GotFocus" 
          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 = "DataGrid_GotFocus" .

DataGrid 셀의 Selected Event를 연결하고 DataGrid에서 BeginEdit ()를 호출합니다.

다음은 이벤트 처리기의 코드입니다.

private void DataGrid_GotFocus(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의 답변은 저에게 적합한 솔루션을 찾는 좋은 출발점이되었습니다. 이미 편집 모드에있는 동일한 행의 셀에 대해서도 한 번 클릭 편집을 허용하려면 약간 조정해야했습니다. 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;
}

보낸 사람 : 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>

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

Voila !


There are two issues with user2134678's answer. One is very minor and has no functional effect. The other is fairly significant.

The first issueis that the GotFocus is actually being called against the DataGrid, not the DataGridCell in practice. The DataGridCell qualifier in the XAML is redundant.

The main problem I found with the answer is that the Enter key behavior is broken. Enter should move you to the next cell below the current cell in normal DataGrid behavior. However, what actually happens behind the scenes is GotFocus event will be called twice. Once upon the current cell losing focus, and once upon the new cell gaining focus. But as long as BeginEdit is called on that first cell, the next cell will never be activated. The upshot is that you have one-click editing, but anyone who isn't literally clicking on the grid is going to be inconvenienced, and a user-interface designer should not assume that all users are using mouses. (Keyboard users can sort of get around it by using Tab, but that still means they're jumping through hoops that they shouldn't need to.)

So the solution to this problem? Handle event KeyDown for the cell and if the Key is the Enter key, set a flag that stops BeginEdit from firing on the first cell. Now the Enter key behaves as it should.

To begin with, add the following Style to your DataGrid:

<DataGrid.Resources>
    <Style TargetType="{x:Type DataGridCell}" x:Key="SingleClickEditingCellStyle">
        <EventSetter Event="KeyDown" Handler="DataGridCell_KeyDown" />
    </Style>
</DataGrid.Resources>

Apply that style to "CellStyle" property the columns for which you want to enable one-click.

Then in the code behind you have the following in your GotFocus handler (note that I'm using VB here because that's what our "one-click data grid requesting" client wanted as the development language):

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

Then you add your handler for the KeyDown event:

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

Now you have a DataGrid that hasn't changed any fundamental behavior of the out-of-the-box implementation and yet supports single-click editing.


I solved it by adding a trigger that sets IsEditing property of the DataGridCell to True when the mouse is over it. It solved most of my problems. It works with comboboxes too.

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

I know that I am a bit late to the party but I had the same problem and came up with a different solution:

     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>

As you can see I wrote my own DataGridTextColumn inheriting everything vom the DataGridBoundColumn. By overriding the GenerateElement Method and returning a Textbox control right there the Method for generating the Editing Element never gets called. In a different project I used this to implement a Datepicker column, so this should work for checkboxes and comboboxes as well.

This does not seem to impact the rest of the datagrids behaviours..At least I haven't noticed any side effects nor have I got any negative feedback so far.


 <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

반응형