2009-10-13 18 views
7

Quiero controlar la visibilidad de la columna DataGrid a través de ContextMenu disponible para el usuario haciendo clic derecho en el encabezado de la columna. El ContextMenu muestra los nombres de todas las columnas disponibles. Estoy usando el patrón de diseño de MVVM.WPF DataGrid: Enlace DataGridColumn visibilidad a ContextMenu MenuItems IsChecked (MVVM)

Mi pregunta es: ¿Cómo ato Visibility propiedad del DataGridColumn 's de la propiedad de un IsCheckedMenuItem situado en el ContextMenu.

Parte del código maqueta:

<UserControl.Resources>   
    <ContextMenu x:Key="ColumnHeaderContextMenu"> 
     <MenuItem Header="Menu Item..1" IsCheckable="True" /> 
    </ContextMenu> 
    <Style x:Key="ColumnHeaderStyle" 
      TargetType="{x:Type toolkit:DataGridColumnHeader}"> 
     <Setter Property="ContextMenu" 
       Value="{StaticResource ColumnHeaderContextMenu}" /> 
    </Style> 
    <BooleanToVisibilityConverter x:Key="booleanToVisibilityConverter" /> 
</UserControl.Resources> 

... flaf flaf flaf

<toolkit:DataGrid x:Name="MyGrid" AutoGenerateColumns="False" 
    ItemsSource="{Binding MyCollection, Mode=Default}" 
    EnableColumnVirtualization="True" IsReadOnly="True" 
    ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}"> 
    <toolkit:DataGrid.Columns> 
     <toolkit:DataGridTextColumn Binding="{Binding Path=MyEntry}" 
      Header="MyEntry" Visibility="{Binding IsChecked, Converter= 
       {StaticResource booleanToVisibilityConverter}.... /> 
    </toolkit:DataGrid.Columns>  
</toolkit:DataGrid> 

Si estoy siendo poco claro por favor hágamelo saber y voy a intentar dar más detalles.

Saludos,

Respuesta

18

acabo escribió una entrada de blog sobre este tema. Permite que DataGridColumns se muestre u oculte mediante un ContextMenu al que se puede acceder haciendo clic con el botón derecho en cualquier encabezado de columna. Esta tarea se realiza únicamente a través de propiedades adjuntas, por lo que es compatible con MVVM.

See blog post

+0

Solo lo miro y parece sólido. Te daría un voto pero falta 1 en reputación :) – Fubzot

+0

Esto funcionó maravillosamente !! Cosas geniales. Ahora necesito estudiarlo en detalle una vez que mi fecha límite termine :) – BloggerDude

+2

Las reglas de SO sugieren que vincular a un blog en lugar de publicar el contenido explícito no es ideal. ¿Puedes responder la Q aquí? – Webreaper

-1

Traté de ge este se una al ContextMenu usando 'ElementName', pero al final, conseguí que funcione utilizando Propiedades de la máquina virtual, por ejemplo,

bool _isHidden; 
public bool IsHidden 
{ 
    get { return _isHidden; } 
    set 
    { 
    if (value != _isHidden) 
    { 
     _isHidden = value; 
     RaisePropertyChanged("IsHidden"); 
     RaisePropertyChanged("IsVisible"); 
    } 
    } 
} 

public Visibility IsVisible 
{ 
    get { return IsHidden ? Visibility.Hidden : Visibility.Visible; } 
} 

y en el XAML:

<Window.ContextMenu> 
    <ContextMenu> 
    <MenuItem Header="Hidden" IsCheckable="True" IsChecked="{Binding IsHidden}" /> 
    </ContextMenu> 
</Window.ContextMenu> 

<toolkit:DataGrid x:Name="MyGrid" AutoGenerateColumns="False" ItemsSource="{Binding MyCollection, Mode=Default}" EnableColumnVirtualization="True" IsReadOnly="True" ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}"> 
    <toolkit:DataGrid.Columns> 
    <toolkit:DataGridTextColumn Binding="{Binding Path=MyEntry}" Header="MyEntry" Visibility="{Binding Path=IsVisible, Mode=OneWay}" /> 
    </toolkit:DataGrid.Columns> 
</toolkit:DataGrid> 
+0

Hola Ian, ¿podrías explicarlo? Tengo una cuadrícula de datos que se une a la colección. Quiero ocultar la columna (visibilidad) marcando la propiedad booleana. Por ejemplo, Class ContextClass {CollectionForGrid; Está activo; } Estoy vinculando CollectionForGrid a Grid y quiero establecer la visibilidad de una base de columna en la propiedad IsActive. – RockWorld

+0

@Rakesh: mi mejor consejo sería abrir una nueva pregunta, y yo (junto con el resto de la comunidad SO) intentaré responderla :) – kiwipom

1

Ok, esto ha sido bastante el ejercicio de un n00b WPF.

IanR gracias por la sugerencia Utilicé un enfoque similar, pero lo llevé todo el camino.

Aquí es lo que he llegado con si alguien puede encontrar una manera más consistente de hacerlo apreciaré cualquier comentario:

Impedimentos:

  1. DataGridColumnHeader no soporta un menú contextual. Por lo tanto, el menú contextual debe aplicarse como un Estilo.

  2. El menú contextual tiene su propio contexto de datos, por lo que tenemos que usar findancestor para vincularlo al contexto de datos ViewModels.

  3. ATM el control DataGrid no analiza su contexto de datos en sus Columnas. Esto podría ser resuelto de código subyacente, sin embargo estamos utilizando el patrón MVVM así que decidí seguir jamiers enfoque

Solución:

Coloque el siguiente dos bloques de código en Window.Resources

<Style x:Key="ColumnHeaderStyle" 
      TargetType="{x:Type toolkit:DataGridColumnHeader}"> 
     <Setter Property="ContextMenu" 
       Value="{StaticResource ColumnHeaderContextMenu}" /> 
    </Style> 

    <ContextMenu x:Key="ColumnHeaderContextMenu"> 
     <MenuItem x:Name="MyMenuItem" 
        IsCheckable="True" 
        IsChecked="{Binding DataContext.IsHidden, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type toolkit:DataGrid}}}"/> 
    </ContextMenu> 

La cuadrícula de datos a continuación, se ve algo como esto en XAML

 <toolkit:DataGrid x:Name="MyGrid" 
          AutoGenerateColumns="False" 
          ItemsSource="{Binding SampleCollection, Mode=Default}" 
          EnableColumnVirtualization="True" 
          IsReadOnly="True" 
          ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}"> 
      <toolkit:DataGrid.Columns> 
       <toolkit:DataGridTextColumn Binding="{Binding Path=SamplingUser}" 
              Header="{Binding (FrameworkElement.DataContext).IsHidden, RelativeSource={x:Static RelativeSource.Self}}" 
              Visibility="{Binding (FrameworkElement.DataContext).IsHidden, 
               RelativeSource={x:Static RelativeSource.Self}, 
               Converter={StaticResource booleanToVisibilityConverter}}"/> 

Por lo tanto, la propiedad de visibilidad en DataGridColumn y la propiedad ischeked están ambas enlazadas a la propiedad IsHidden en viewModel.

En el modelo de vista:

public bool IsHidden 
    { 
     get { return isHidden; } 
     set 
     { if (value != isHidden) 
      { 
       isHidden = value; 
       OnPropertyChanged("IsHidden"); 
       OnPropertyChanged("IsVisible"); 
      } 
     } 
    } 

La Clase auxiliar definido por Jaimer:

class DataGridSupport 
{ 
    static DataGridSupport() 
    { 

     DependencyProperty dp = FrameworkElement.DataContextProperty.AddOwner(typeof(DataGridColumn)); 
     FrameworkElement.DataContextProperty.OverrideMetadata (typeof(DataGrid), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnDataContextChanged))); 

    } 

    public static void OnDataContextChanged (DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     DataGrid grid = d as DataGrid ; 
     if (grid != null ) 
     {     
      foreach (DataGridColumn col in grid.Columns) 
      { 
       col.SetValue (FrameworkElement.DataContextProperty, e.NewValue); 
      } 
     } 
    } 
} 

instanciado en el modelo de vista (sólo para mostrar realiza a través de la Unidad de proyecto real)

private static DataGridSupport dc = new DataGridSupport(); 

Cheers,

0

En lugar de booleanToVisibilityConverter puede utilizar x: estática

<Setter TargetName="UIElement" Property="UIElement.Visibility" Value="x:Static Visibility.Hidden" /> 

Estática en XAML: http://msdn.microsoft.com/en-us/library/ms742135.aspx

+0

Votó solo porque el votante anterior que votó esta respuesta lo hizo no proporcionar ninguna explicación con respecto al motivo de la votación. – Ahmad

6

Sé que esto es un poco viejo. Pero yo estaba buscando en hacer esto y este post es mucho más simple: http://iimaginec.wordpress.com/2011/07/25/binding-wpf-toolkit%E2%80%99s-datagridcolumn-to-a-viewmodel-datacontext-propogation-for-datagrid-columns-the-mvvm-way-to-interact-with-datagridcolumn/

Todo lo que necesita hacer es establecer DataContext en las columnas y luego unirse visibilidad a este modelo de vista con normalidad! :) simple y eficaz

+0

Funciona muy bien para mí en 3.5 + WPF Toolkit –

+0

+1 Esa es la mejor explicación del patrón que he visto, gracias. – briantyler

12

He estado buscando un genérica, XAML (es decir, ningún código subyacente), automática y sencilla ejemplo de un menú contextual selector de columnas que se une a un WPF Encabezado de columna DataGrid. He leído literalmente cientos de artículos, pero ninguno de ellos parece hacer exactamente lo correcto, o no son lo suficientemente genéricos. Así que esto es lo que creo que es la mejor solución combinada:

Primero, ponga esto en el diccionario de recursos. Lo dejaré como ejercicio para que el lector escriba el convertidor Visibilidad/Booleano para asegurarse de que las casillas de verificación verifiquen cuándo la columna está visible y viceversa. Tenga en cuenta que al definir x: Shared = "False" para el recurso de menú contextual, obtendrá un estado específico de la instancia, lo que significa que puede usar esta única plantilla/recurso para toda la cuadrícula de datos y todos mantendrán su propio estado.

<Converters:VisiblityToInverseBooleanConverter x:Key="VisiblityToInverseBooleanConverter"/> 

<ContextMenu x:Key="ColumnChooserMenu" x:Shared="False" 
      DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}" 
      ItemsSource="{Binding Columns, RelativeSource={RelativeSource AncestorType={x:Type sdk:DataGrid}}}"> 
    <ContextMenu.ItemContainerStyle> 
     <Style TargetType="MenuItem"> 
      <Setter Property="Header" Value="{Binding Header}"/> 
      <Setter Property="AutomationProperties.Name" Value="{Binding Header}"/> 
      <Setter Property="IsCheckable" Value="True" /> 
      <Setter Property="IsChecked" Value="{Binding Visibility, Mode=TwoWay, Converter={StaticResource VisiblityToInverseBooleanConverter}}" /> 
     </Style> 
    </ContextMenu.ItemContainerStyle> 
</ContextMenu> 

<Style x:Key="ColumnHeaderStyle" TargetType="{x:Type Primitives:DataGridColumnHeader}"> 
    <Setter Property="ContextMenu" Value="{StaticResource ColumnChooserMenu}" /> 
</Style> 

<ContextMenu x:Key="GridItemsContextMenu" > 
    <MenuItem Header="Launch Do Some other action"/> 
</ContextMenu> 

A continuación, defina la cuadrícula de datos de la siguiente manera (donde OrdersQuery es una fuente de datos expuestos por el Vista-modelo):

<sdk:DataGrid ItemsSource="{Binding OrdersQuery}" 
       AutoGenerateColumns="True" 
       ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}" 
       ContextMenu="{StaticResource GridItemsContextMenu}"> 

    <!-- rest of datagrid stuff goes here --> 

</sdk:DataGrid> 

Esto le dará la siguiente:

  1. Un contexto menú enlazado a los encabezados de columna que actúa como selector de columna.
  2. Un menú de contexto vinculado a los elementos de la grilla (para realizar acciones sobre los elementos; de nuevo, la vinculación de las acciones es un ejercicio para el lector).

Espero que esto ayude a las personas que han estado buscando un ejemplo como este.

+2

Esta respuesta debería ser mayor. Solución simple y elegante. –

+1

¡Solución muy ordenada! Sin embargo, una nota: solo funciona si las columnas se generan automáticamente. Obviamente, por supuesto, pero vale la pena mencionarlo. –

+0

¿Estás seguro? Es bastante seguro que funciona para columnas generadas automáticamente o generadas explícitamente (pero no lo han probado durante un par de años, así que podría ser incorrecto). – Webreaper

Cuestiones relacionadas