【问题标题】:WPF Datagrid with some read-only rows带有一些只读行的 WPF Datagrid
【发布时间】:2011-01-03 01:28:57
【问题描述】:

我需要根据绑定模型上的属性将我的一些 WPF Datagrid 行显示为只读或不只读。

如何做到这一点?

【问题讨论】:

    标签: wpf datagrid wpftoolkit readonly


    【解决方案1】:

    我遇到了同样的问题。 使用 jsmith 的回答和 Nigel Spencer 的博客中提供的信息,我提出了一个解决方案,不需要更改 WPF DataGrid 源代码、子类化或添加代码到视图的代码隐藏。如您所见,我的解决方案对 MVVM 非常友好。

    它使用Expression Blend Attached Behavior mechanism,因此您需要安装Expression Blend SDK 并添加对Microsoft.Expression.Interactions.dll 的引用,但如果您不喜欢这种行为,可以轻松地将其转换为native attached behavior

    用法:

    <DataGrid 
        xmlns:Behaviors="clr-namespace:My.Common.Behaviors"
    ...
    >
        <i:Interaction.Behaviors>
             <Behaviors:DataGridRowReadOnlyBehavior/>
        </i:Interaction.Behaviors>
        <DataGrid.Resources>
            <Style TargetType="{x:Type DataGridRow}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsReadOnly}" Value="True"/>
                        <Setter Property="Behaviors:ReadOnlyService.IsReadOnly" Value="True"/>
                        <Setter Property="Foreground" Value="LightGray"/>
                        <Setter Property="ToolTipService.ShowOnDisabled" Value="True"/>
                        <Setter Property="ToolTip" Value="Disabled in ViewModel"/>
                    </DataTrigger>
    
                </Style.Triggers>
            </Style>
          </DataGrid.Resources>
    ...
    </DataGrid>
    

    ReadOnlyService.cs

    using System.Windows;
    
    namespace My.Common.Behaviors
    {
        internal class ReadOnlyService : DependencyObject
        {
            #region IsReadOnly
    
            /// <summary>
            /// IsReadOnly Attached Dependency Property
            /// </summary>
            private static readonly DependencyProperty BehaviorProperty =
                DependencyProperty.RegisterAttached("IsReadOnly", typeof(bool), typeof(ReadOnlyService),
                    new FrameworkPropertyMetadata(false));
    
            /// <summary>
            /// Gets the IsReadOnly property.
            /// </summary>
            public static bool GetIsReadOnly(DependencyObject d)
            {
                return (bool)d.GetValue(BehaviorProperty);
            }
    
            /// <summary>
            /// Sets the IsReadOnly property.
            /// </summary>
            public static void SetIsReadOnly(DependencyObject d, bool value)
            {
                d.SetValue(BehaviorProperty, value);
            }
    
            #endregion IsReadOnly
        }
    }
    

    DataGridRowReadOnlyBehavior.cs

    using System;
    using System.Windows.Controls;
    using System.Windows.Interactivity;
    
    namespace My.Common.Behaviors
    {
        /// <summary>
        /// Custom behavior that allows for DataGrid Rows to be ReadOnly on per-row basis
        /// </summary>
        internal class DataGridRowReadOnlyBehavior : Behavior<DataGrid>
        {
            protected override void OnAttached()
            {
                base.OnAttached();
                if (this.AssociatedObject == null)
                    throw new InvalidOperationException("AssociatedObject must not be null");
    
                AssociatedObject.BeginningEdit += AssociatedObject_BeginningEdit;
            }
    
            private void AssociatedObject_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
            {
                var isReadOnlyRow = ReadOnlyService.GetIsReadOnly(e.Row);
                if (isReadOnlyRow)
                    e.Cancel = true;
            }
    
            protected override void OnDetaching()
            {
                AssociatedObject.BeginningEdit -= AssociatedObject_BeginningEdit;
            }
        }
    }
    

    【讨论】:

    • 谢谢。正确的答案就在这里。它对我很有用。
    • 您需要在某处使用 IsReadOnly 属性才能完成这项工作,因此我添加了一个接口并将 e.Row.Item 转换为它,从而使 ReadOnlyService 变得不必要,IMO。尽管如此,大+1。干杯
    【解决方案2】:

    我找到了几个简单的解决方案来解决这个问题。我认为最好的方法是连接到 DataGrid 的BeginningEdit 事件。这类似于 Nigel Spencer 在他的帖子中所做的,但您不必从 DataGrid 中覆盖它。这个解决方案很棒,因为它不允许用户编辑该行中的任何单元格,但它确实允许他们选择行

    在代码后面:

    private void MyList_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
    {
      if (((MyCustomObject)e.Row.Item).IsReadOnly)  //IsReadOnly is a property set in the MyCustomObject which is bound to each row
      {
        e.Cancel = true;
      }
    }
    

    在 XAML 中:

    <DataGrid ItemsSource="{Binding MyObservableCollection}"
              BeginningEdit="MyList_BeginningEdit">
      <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Name}"
                            Header="Name"/>
        <DataGridTextColumn Binding="{Binding Age}"
                            Header="Age"/>
      </DataGrid.Columns>
    </DataGrid>
    

    不同的解决方案...这根本不允许用户选择行,但不需要在后面的代码中添加额外的代码。

    <DataGrid ItemsSource="{Binding MyObservableCollection}">
      <DataGrid.Resources>
        <Style TargetType="{x:Type DataGridRow}">
          <Style.Triggers>
            <DataTrigger Binding="{Binding IsReadOnly}"
                         Value="True" >
            <Setter Property="IsEnabled"
                    Value="False" />   <!-- You can also set "IsHitTestVisble" = False but please note that this won't prevent the user from changing the values using the keyboard arrows -->
            </DataTrigger>
    
          </Style.Triggers>
        </Style>
      </DataGrid.Resources>
    
      <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Name}"
                            Header="Name"/>
        <DataGridTextColumn Binding="{Binding Age}"
                            Header="Age"/>
      </DataGrid.Columns>
    </DataGrid>
    

    【讨论】:

    • XAML 唯一的方法更好更干净:)
    【解决方案3】:

    我认为最简单的方法是将 IsReadOnly 属性添加到 DataGridRow 类。 Nigel Spencer 有一篇关于如何做到这一点的详细文章here

    【讨论】:

    • 谢谢,我看过这篇文章。我希望有一些更容易的东西。我不喜欢修改源代码的想法(新版本出来时的可维护性问题)。
    • 是的,很遗憾,他们还没有向 DataGridRow 添加 IsReadOnly 属性,但是您可以使用 IsEnabled 功能。
    猜你喜欢
    • 1970-01-01
    • 2020-05-21
    • 2014-08-28
    • 2012-02-08
    • 2011-01-30
    • 1970-01-01
    • 2011-10-01
    • 1970-01-01
    • 2016-02-12
    相关资源
    最近更新 更多