Подтвердить что ты не робот

WPF Datagrid с некоторыми рядами только для чтения

Мне нужно показать некоторые из моих строк WPat Datagrid как прочитанные или не зависящие от свойства на моей связанной модели.

Как это можно сделать?

4b9b3361

Ответ 1

У меня была та же проблема. Используя информацию, предоставленную в jsmith-ответе, и в блоге Найджела Спенсера, я придумал решение, которое не требует изменения исходного кода WPF DataGrid, подклассификации или добавления кода для просмотра кода. Как вы можете видеть, мое решение очень MVVM Friendly.

В нем используется Механизм принудительного взаимодействия с выражениями выражения, поэтому вам нужно установить SDK Expression Blend и добавить ссылку на Microsoft.Expression.Interactions.dll, но это поведение может быть легко преобразовано в родное вложенное поведение, если вам это не нравится.

Применение:

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

Ответ 2

Я нашел пару простых решений этой проблемы. На мой взгляд, лучшим было подключение к событию BeginningEdit в DataGrid. Это похоже на то, что сделал Найджел Спенсер в своем посте, но вам не нужно переопределять это из 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>

Ответ 3

Я думаю, что самый простой способ сделать это - добавить свойство IsReadOnly в класс DataGridRow. Существует подробная статья Найджел Спенсера о том, как сделать это здесь.