2009-10-13 6 views
7

Je souhaite contrôler DataGrid la visibilité des colonnes via un ContextMenu disponible pour l'utilisateur en cliquant avec le bouton droit sur l'en-tête de colonne. Le ContextMenu affiche les noms de toutes les colonnes disponibles. J'utilise le modèle de conception MVVM.WPF DataGrid: Liaison DataGridColumn de liaison à ContextMenu MenuItems IsChecked (MVVM)

Ma question est: Comment puis-je lier la propriété de VisibilityDataGridColumn à la propriété d'un IsCheckedMenuItem situé dans le ContextMenu.

Certains Code mockup:

<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 je suis pas clair s'il vous plaît laissez-moi savoir et je vais essayer d'élaborer.

Cheers,

Répondre

18

Je viens d'écrire un billet de blog sur ce sujet. Il permet à DataGridColumns d'être affiché ou masqué via un ContextMenu accessible en cliquant avec le bouton droit sur un en-tête de colonne. Cette tâche est accomplie uniquement à travers les propriétés jointes, donc elle est compatible MVVM.

See blog post

+0

Juste regardé à travers elle et il semble solide. Je vous donnerais un vote, mais il manque 1 dans la réputation :) – Fubzot

+0

Cela a fonctionné à merveille !! Des trucs géniaux. Maintenant, j'ai besoin de l'étudier en détail une fois ma date limite est terminée :) – BloggerDude

+2

SO règles suggèrent que la liaison à un blog plutôt que d'afficher le contenu explicite n'est pas idéal. Pouvez-vous réellement répondre au Q ici? – Webreaper

-1

J'ai essayé de ge cela de se lier au ContextMenu en utilisant « ElementName », mais à la fin, il a obtenu du travail avec des propriétés dans la machine virtuelle, par exemple

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

et dans le 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

Bonjour Ian pourriez-vous l'élaborer? J'ai un DataGrid qui lie à la collection. Je veux cacher la colonne (visibilité) en vérifiant la propriété booléenne. E.g Classe ContextClass {CollectionForGrid; C'est actif; } Je lie CollectionForGrid à la grille et je veux définir la visibilité d'une base de colonne sur la propriété IsActive. – RockWorld

+0

@Rakesh - mon meilleur conseil serait d'ouvrir une nouvelle question, et je (avec le reste de la communauté SO) vais essayer de répondre pour vous :) – kiwipom

1

Ok, cela a été tout à fait l'exercice pour un n00b WPF.

IanR merci pour la suggestion J'ai utilisé une approche similaire, mais il dosent vous prendre tout le chemin.

Voici ce que je suis venu avec si quelqu'un peut trouver une façon plus cohérente de le faire, je vous saurais gré des commentaires:

Entraves:

  1. DataGridColumnHeader ne prend pas en charge un menu contextuel. Par conséquent, le menu contextuel doit être appliqué en tant que style. Le menu contextuel a son propre datacontexte. Nous devons donc utiliser findancestor pour le lier au datacontex de ViewModels.

  2. ATM le contrôle DataGrid n'analyse pas son contexte de données à ses colonnes. Cela pourrait être résolu dans codebehind mais nous utilisons le modèle MVVM alors j'ai décidé de suivre jamiers approche

Solution:

Placez les deux blocs suivants du code dans 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> 

Le DataGrid ressemble alors quelque chose comme ça dans 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}}"/> 

Ainsi, la propriété de visibilité sur DataGridColumn et la propriété ischeked sont toutes les deux liées à la propriété IsHidden sur viewModel.

Dans le ViewModel:

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

La classe Helper définie par 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); 
      } 
     } 
    } 
} 

instancié dans le viewmodel (juste pour montrer fait par l'unité dans le projet réel)

private static DataGridSupport dc = new DataGridSupport(); 

Cheers,

0

Au lieu de booleanToVisibilityConverter vous pouvez utiliser x: statique

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

Statics en XAML: http://msdn.microsoft.com/en-us/library/ms742135.aspx

+0

Voté seulement parce que l'électeur précédent qui a voté cette réponse a fait ne fournit aucune explication concernant la raison du vote. – Ahmad

6

Je sais que c'est un peu vieux. Mais je cherchais à faire et ce poste est beaucoup plus 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/

Tout ce que vous devez faire est de mettre DataContext sur les colonnes puis se lier à votre visibilité ViewModel selon la normale! :) Simple et efficace

+0

Fonctionne bien pour moi dans 3.5 + WPF Toolkit –

+0

+1 C'est la meilleure explication du modèle que j'ai vu, merci. – briantyler

12

J'ai cherché un générique, XAML (pas de code-behind), automatique et simples exemple d'un menu chooser de colonne de contexte qui se lie à un WPF En-tête de colonne DataGrid. J'ai lu littéralement des centaines d'articles, mais aucun d'entre eux semble faire exactement la bonne chose, ou ils ne sont pas assez génériques. Voici donc ce que je pense être la meilleure solution combinée:

D'abord, mettez-les dans le dictionnaire de ressources. Je vais laisser au lecteur le soin d'écrire le convertisseur Visibilité/Booléen pour s'assurer que les cases à cocher vérifient quand la colonne est visible et vice-versa. Notez qu'en définissant x: Shared = "False" pour la ressource du menu contextuel, il obtiendra l'état spécifique à l'instance, ce qui signifie que vous pouvez utiliser ce modèle/ressource unique pour tous vos DataGrid et qu'ils conserveront tous leur propre état.

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

Définissez ensuite la DataGrid comme suit (où OrdersQuery est une source de données exposées par la vue modèle):

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

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

</sdk:DataGrid> 

Cela vous donnera les éléments suivants:

  1. Un contexte menu lié aux en-têtes de colonne qui agit comme un sélecteur de colonne.
  2. Un menu contextuel lié aux éléments de la grille (pour effectuer des actions sur les éléments eux-mêmes - encore une fois, la liaison des actions est un exercice pour le lecteur).

Espérons que cela aide les personnes qui ont recherché un exemple comme celui-ci.

+2

Cette réponse devrait être plus élevée. Solution simple et élégante. –

+1

Solution très soignée! Cependant, une note: cela ne fonctionne que si les colonnes sont générées automatiquement. Évidemment, bien sûr, mais je tiens à le mentionner. –

+0

Etes-vous sûr? Assez sûr que cela fonctionne pour les colonnes générées automatiquement ou explicitement (mais ne l'a pas essayé depuis quelques années, donc cela pourrait être faux). – Webreaper

Questions connexes