在WPF DataGrid中单击编辑


92

我希望用户能够将单元格置于编辑模式,并通过单击突出显示单元格所在的行。默认情况下,这是双击。

我该如何覆盖或实施?


您是否正在使用WPF工具包中的DataGrid?
myermian

4
您能否为我们提供更多有关您尝试过的方法以及如何不起作用的信息?
扎克·约翰逊

Answers:


76

这是我解决此问题的方法:

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

8
通过将SelectionUnitDataGrid 的属性设置为,可以解决已经选择的行问题Cell
马特·温克勒

假设我的DataGridCell中有一个TextBox。致电后grd.BeginEdit(e),我希望该单元格中的TextBox具有焦点。我怎样才能做到这一点?我尝试同时调用FindName("txtBox")DataGridCell和DataGrid,但它为我返回null。
user1214135 2013年

GotFocus =“ DataGrid_GotFocus”似乎丢失了?
2013年

4
这很好用,但是我不建议这样做。我在项目中使用了此方法,并决定回滚到标准DG行为。将来,当DG增长并变得复杂时,您将遇到验证问题,添加新行和其他奇怪行为。
white.zaz

1
回滚到标准DG行为后,@ white.zaz对客户很满意吗?因为问这个问题的主要原因是标准DG功能中的编辑操作不方便,因为在DG进入“编辑”模式之前需要多次单击。
AEMLoviji '18

42

Micael Bergeron的答案对我来说是找到适合我的解决方案的一个很好的开始。为了允许对已经处于编辑模式的同一行中的单元格进行单击编辑,我必须对其进行一些调整。对我来说,使用SelectionUnit Cell是没有选择的。

我使用了DataGridCell.GotFocus事件,而不是使用仅在第一次单击行的单元格时才触发的DataGridCell.Selected事件。

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

3
复选框似乎对我不起作用?我仍然必须双击它们
Thomas Klammer

9

来源: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;
}

1
这在某些情况下不起作用,其解决方案比Micael Bergerons更复杂。
SwissCoder

对我来说,这几乎是解决方案。我需要添加“ PreviewMouseLeftButtonUp”事件处理程序,并在其中放置完全相同的代码。
内斯托尔·桑切斯A.

一旦有了组合框,此功能也将无法使用。预览点击会看到组合框弹出窗口上的点击,然后cell.focus调用将所有内容搞砸了。最简单的解决方法是添加一个查看鼠标事件原始源的部分,在该部分上使用FindVisualParent来查看其是否在数据网格内。如果没有,请不要执行任何其他工作。
John Gardner

7

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

链接已死(15个字符限制)
Blechdose

4

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

如果将组合框用作编辑模板,这将不起作用,我假设其他类似捕获鼠标事件的复选框也会中断
Steve

对我来说,这适用于组合框列,但是“新项目行”(最后一行)的文本框有一个奇怪的行为:第一次单击时,我获得了输入焦点并可以输入内容。当我将鼠标移出单元格时,文本框的值消失。进一步键入时,新输入的文本将正确保存(它会根据需要创建一个新条目)。即使使用ComboboxColumn,也会发生这种情况。
FrankM

最初看起来工作正常,但是完全弄乱了我的Datagrid,当我尝试排序时,所有这些值都消失了,没有此代码,一切都可以很好地进行排序。
Chandraprakash

3

我通过添加一个触发器将鼠标悬停在其上方,该触发器将DataGridCell的IsEditing属性设置为True来解决该问题。它解决了我的大多数问题。它也适用于组合框。

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

1
不起作用...只要鼠标离开单元格,它就会放弃编辑。因此,您1)左键单击要编辑的单元格。2)将鼠标移开。3)开始键入。您的键入无效,因为该单元格不再处于编辑模式。
Skarsnik '17

1
也不适合我。阻止为我编辑文本框
Blechdose

但是这种方法有一个问题,我已经锁定了第一列进行编辑,通过这种方法,这使得第一列也可以编辑!
Chandraprakash

3

我正在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);
        }
    }

瞧!


1

user2134678的答案有两个问题。一个很小,没有功能作用。另一个相当重要。

第一个问题是实际上是针对DataGrid而不是DataGridCell调用GotFocus。XAML中的DataGridCell限定符是多余的。

我发现答案的主要问题是Enter键行为已损坏。Enter会将您移动到正常DataGrid行为中当前单元格下方的下一个单元格。但是,实际发生在幕后的是GotFocus事件将被调用两次。曾经是当前单元格失去了焦点,一次是新单元格获得了焦点。但是只要在第一个单元格上调用BeginEdit,下一个单元格就永远不会被激活。结果是您可以进行一键式编辑,但是从字面上单击网格的任何人都将感到不便,并且用户界面设计人员不应假定所有用户都在使用鼠标。(键盘用户可以通过使用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,它没有更改即用型实现的任何基本行为,但支持单击编辑。


0

我知道我参加聚会有点晚了,但是我遇到了同样的问题,并提出了不同的解决方案:

     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方法并在此处返回文本框控件,永远不会调用用于生成Editing Element的方法。在另一个项目中,我用它来实现Datepicker列,因此这也适用于复选框和组合框。

这似乎并没有影响到其余的数据网格行为。


-1

更新资料

一个简单的解决方案,如果您满意,您的单元格将保留一个文本框(不区分编辑和非编辑模式)。这样,单击编辑即可使用。这也适用于组合框和按钮等其他元素。否则,请使用更新下面的解决方案。

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

更新结束

兰特

我尝试了在Google和Google上找到的所有内容,甚至尝试创建了自己的版本。但是每个答案/解决方案主要适用于文本框列,但不适用于所有其他元素(复选框,组合框,按钮列),甚至破坏了其他那些元素列或产生了一些其他副作用。感谢Microsoft使datagrid采取这种丑陋的方式,并迫使我们创建这些黑客。因此,我决定制作一个可以以样式直接应用于文本框列的版本,而不会影响其他列。

特征

  • 后面没有代码。MVVM友好。
  • 单击相同或不同行中的不同文本框单元格时,它可以工作。
  • TAB和ENTER键起作用。
  • 它不会影响其他列。

资料来源

我使用了此解决方案和@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;
        }
    }
}

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

 }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.