example - WPF Datagrid con algunas filas de solo lectura
wpf datagrid binding (3)
Creo que la manera más fácil de hacerlo es agregar una propiedad IsReadOnly a la clase DataGridRow. Hay un artículo detallado de Nigel Spencer sobre cómo hacer esto aquí .
Tengo la necesidad de mostrar algunas de mis filas de WPF Datagrid como de solo lectura o no, dependiendo de una propiedad en mi modelo encuadernado.
¿Cómo puede hacerse esto?
Yo tuve el mismo problema. Utilizando la información provista en la respuesta de jsmith y en el blog de Nigel Spencer, he encontrado una solución que no requiere cambiar el código fuente de WGD DataGrid, crear subclases o agregar código para ver el código subyacente . Como puede ver, mi solución es muy amigable con MVVM.
Utiliza el mecanismo Expression Blend Attached Behavior, por lo que deberá instalar Expression Blend SDK y agregar una referencia a Microsoft.Expression.Interactions.dll, pero este comportamiento podría convertirse fácilmente en comportamiento nativo si no le gusta.
Uso:
<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;
}
}
}
Encontré un par de soluciones simples para este problema. Lo mejor en mi opinión fue conectarme al evento BeginningEdit de DataGrid. Esto es similar a lo que hizo Nigel Spencer en su publicación, pero no tiene que anularlo desde DataGrid. Esta solución es excelente, ya que no permite al usuario editar ninguna de las celdas de esa fila, pero sí le permite seleccionar la fila .
En código detrás:
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;
}
}
En 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>
Solución diferente ... Esto no le permite al usuario seleccionar la fila, pero no requiere código adicional en el código.
<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 -->
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}"
Header="Name"/>
<DataGridTextColumn Binding="{Binding Age}"
Header="Age"/>
</DataGrid.Columns>
</DataGrid>