2 votes

ItemsControl pour afficher plusieurs couches de données

J'ai une ObservableCollection d'objets classés avec un nom et un ensemble de coordonnées 3D. J'ai également une ObservableCollection de couches, chacune devant contenir une grille 2D.

Configuration du Problème

L'objectif est d'utiliser un ItemsControl, probablement imbriqué, pour afficher ces objets de la manière suivante : si la coordonnée Z détermine la couche, et les coordonnées X et Y spécifient la position de chaque objet sur la grille, alors l'objet devrait être affiché dans un TabControl, où le TabItem correspond à la coordonnée Z et accueille une Grid où les attributs Grid.Row et Grid.Column déterminent où le nom de l'objet est écrit sur le TabItem.

Important : bien que chaque coordonnée 3D ne soit utilisée qu'une seule fois, un objet "Entry" peut avoir plusieurs coordonnées 3D, et peut donc apparaître plusieurs fois sur une grille et/ou différents TabItems.

Remarque : le modèle de données n'est pas gravé dans la pierre; si le problème peut être résolu avec un autre modèle de données, je suis ouvert à le changer. Cependant, l'affichage est une exigence du client — il pourrait être modifié, mais j'aurais besoin d'arguments extrêmement convaincants à cet égard.

Informations de Contexte

Les objets ressemblent à ceci (BindableBase provient de la Prism Library) :

public class Entry : BindableBase {
  public string Name { set; get; }

  private EntryCoordinates coordinates;
  public EntryCoordinates Coordinates {
    set { SetProperty(ref coordinates, value); }
    get { return coordinates; }
  }
}

public class EntryCoordinates : BindableBase {
  private int x;
  public int X {
    set { SetProperty(ref x, value); }
    get { return x; }
  }

  private int y;
  public int Y {
    set { SetProperty(ref y, value); }
    get { return y; }
  }

  private int z;
  public int Z {
    set { SetProperty(ref z, value); }
    get { return z; }
}

Les objets Entry sont hébergés dans des "couches d'entrée" :

public class EntryLayer : ObservableCollection {
}

En fin de compte, je veux être capable de modifier les objets Entry (qui sont en réalité plus complexes) via l'interface utilisateur, donc la liaison de données bidirectionnelle est une nécessité absolue.

Effort Jusqu'à Maintenant

En utilisant l'excellent WPF Grid Extension de @Rachel, j'ai implémenté un ItemsControl qui peuple une Grid comme désiré :

      <Setter Property="Grid.Row" Value="{Binding Coordinates.X}"/>
      <Setter Property="Grid.Column" Value="{Binding Coordinates.Y}"/>

Le GridCellThumb est un contrôle personnalisé qui permet le glisser-déposer (omis ici pour des raisons de clarté) et expose des propriétés de dépendance de coordonnées :

public class GridCellThumb : Thumb {
  public static readonly DependencyProperty XCoordinateProperty = DependencyProperty.Register("XCoordinate", typeof(int), typeof(GridCellThumb));
  public int XCoordinate {
    get { return (int)GetValue(XCoordinateProperty); }
    set { SetValue(XCoordinateProperty, value); }
 }

 public static readonly DependencyProperty YCoordinateProperty = DependencyProperty.Register("YCoordinate", typeof(int), typeof(GridCellThumb));
 public int YCoordinate {
    get { return (int)GetValue(YCoordinateProperty); }
    set { SetValue(YCoordinateProperty, value); }
 }
}

Le TileControl est un contrôle utilisateur qui affiche le nom d'un Entry :

Je suis bloqué pour trouver comment envelopper l'ItemsControl original dans un modèle de TabControl afin d'atteindre l'objectif d'afficher une entrée, potentiellement plusieurs fois, correctement. Par exemple, la liaison au chemin Coordinates.Z fonctionne, mais crée autant de TabItems qu'il y a d'entrées :

      <Setter Property="Header"
              Value="{Binding Coordinates.Z}" />
      <Setter Property="TabIndex"
              Value="{Binding Coordinates.Z}" />

J'ai essayé les solutions proposées par @greg40 (ItemsControl imbriqué), @d.moncada (un autre ItemsControl imbriqué), et @Sheridan (remontant dans l'arborescence visuelle), mais j'échoue toujours de manière magistrale lorsqu'il s'agit de relier une Entry à une EntryLayer donnée.

Est-ce que quelqu'un a d'autres idées à explorer ? Comme je l'ai dit, je suis également ouvert à restructurer mon modèle de données, si cela conduit à une solution plus facile.

Mise à Jour 2018-01-08

J'ai exploré la possibilité d'utiliser des Buttons au lieu d'un TabControl dans le sens de lier leur état cliqué à l'affichage d'informations variables sur la grille. Cependant, cela n'a fait que déplacer le problème et en créer un nouveau : les données ne sont plus pré-chargées, ce qui est crucial pour les exigences du client.

Je considère désormais fortement la possibilité de suggérer un changement des exigences au client et concevoir un tout autre modèle de données. Afin d'éviter de prendre à nouveau une mauvaise direction, j'apprécierais beaucoup s'il y avait quelqu'un dans la communauté avec une opinion forte sur ce problème qu'il serait prêt à partager avec moi.

0voto

Informagic Points 434

Après quelques conseils offline très précieux d'un expert pair, j'ai décidé de ne pas suivre le modèle de données suggéré par le client et de leur fournir une solution alternative. L'approche consiste maintenant à remplir la Vue de manière descendante.

En essence, cela signifie que ce qui était précédemment ma coordonnée Z est maintenant l'index d'un objet EntryLayer définissant les TabItems dédiés. Chaque EntryLayer a une ObservableCollection qui se réfère aux Entrys qui font partie de ce EntryLayer.

Cela contraste avec le modèle initial dans le sens où maintenant il n'est plus possible qu'un Entry ait plusieurs coordonnées; au contraire, le Entry lui-même pourrait exister plusieurs fois avec des coordonnées différentes.

Comment ai-je vendu cela au client? Je leur ai dit qu'un Entry pouvait désormais avoir des options de personnalisation qui peuvent différer entre ses représentations sur le même ou d'autres EntryLayers, par exemple en utilisant des couleurs et des polices spécifiées par l'utilisateur, tout en pointant toujours vers les mêmes informations de base. Cela me donne un peu plus de travail à faire en termes d'implémentation, mais cela résout élégamment l'impasse en le présentant sous la forme d'une nouvelle fonctionnalité.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X