3

Ich möchte in einem Datagrid-Daten zeigen, wo die Daten sind eine Sammlung vonDatagrid: dynamische Datatemplate für dynamische Datagridtemplatecolumn

public class Thing 
{ 
    public string Foo { get; set; } 
    public string Bar { get; set; } 
    public List<Candidate> Candidates { get; set; } 
} 

public class Candidate 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    ... 
} 

, wo die Zahl der Bewerber in Kandidatenliste zur Laufzeit variiert.

Wunsch Raster-Layout wie folgt aussieht

Foo | Bar | Candidate 1 | Candidate 2 | ... | Candidate N 

Ich möchte eine DataTemplate für jeden Candidate haben wie ich es während der Laufzeit planen zu ändern - Benutzer wählen können, welche Informationen über Kandidaten in verschiedenen Spalten angezeigt (Kandidat ist nur ein Beispiel, ich habe ein anderes Objekt). Das bedeutet, dass ich auch die Spaltenvorlagen in der Laufzeit ändern möchte, obwohl dies durch eine große Vorlage und das Zusammenlegen der Teile erreicht werden kann.

Ich kenne zwei Möglichkeiten, wie meine Ziele zu erreichen (beide ziemlich ähnlich):

  1. Verwenden AutoGeneratingColumn Ereignis und erstellen Kandidaten Spalten
  2. Spalten hinzufügen manuell

In beiden Fällen Ich muss die DataTemplate von String mit XamlReader laden. Zuvor muss ich die Zeichenfolge bearbeiten, um die Bindung zu ändern, die gewünschte Candidate.

Gibt es eine bessere Möglichkeit, ein DataGrid mit unbekannter Nummer von DataGridTemplateColumn zu erstellen?

Hinweis: Diese Frage auf dynamic datatemplate with valueconverter

bearbeiten basiert: Da ich sowohl WPF und Silverlight unterstützen müssen, ich habe meine eigene DataGrid Komponente geschaffen, die DependencyProperty für Bindig eine Sammlung von Spalten . Wenn sich die Sammlung ändert, aktualisiere ich die Spalten.

+0

Haben Sie jemals eine Lösung oder Antwort für dieses Problem bekommen? –

+0

Nicht wirklich. Wie ich geschrieben habe, bin ich mit dem benutzerdefinierten 'DataGrid'-Steuerelement gelandet. Aber ich denke darüber nach, ein benutzerdefiniertes Steuerelement zu entwickeln, das nur auf einem 'Grid' basiert, da' DataGrid' ziemlich schwer für meine Aufgabe ist. –

Antwort

2

Zum Beispiel erstellen wir zwei Datatemplates und eine Content:

<DataTemplate DataType="{x:Type viewModel:VariantA}"> <dataGrid...> </DataTemplate> 
<DataTemplate DataType="{x:Type viewModel:VariantB}"> <dataGrid...> </DataTemplate> 

<ContentControl Content="{Binding Path=GridModel}" /> 

Nun, wenn Sie Ihre GridModel Eigenschaft (zum Beispiel Typ-Objekt) zu Varianta oder VariantB gesetzt, wird die Datatemplate wechseln.

Varianta & B Beispiel Umsetzung:

public class VariantA 
{ 
    public ObservableCollection<ViewModel1> DataList { get; set; } 
} 

public class VariantB 
{ 
    public ObservableCollection<ViewModel2> DataList { get; set; } 
} 

Hoffnung, das hilft.

+0

Ich sehe deinen Punkt leider brauche ich Spalten Templating - nicht das ganze Raster Templating. –

0

Ich weiß nicht, ob dies eine „bessere“ Art und Weise ist, da diese ziemlich hässlich bleibt, aber ich habe personnaly wie folgt aus:

  • die Vorlage in XAML
  • eine multiBIND nutzen machen, das dauert die aktuelle Bindung + eine Bindung an die Spalte, um den "korrekten" dataContext (d. h: die Zelle anstelle der Zeile)
  • Verwenden Sie einen Konverter für diese Bindung, um den Wert der gewünschten Eigenschaft abzurufen, und fügen Sie optional einen Parameter hinzu, wenn viele Eigenschaften abgerufen werden sollen.

zB: (sorry, ich habe passen nicht mein Code Projekt passen, aber man sollte es sich von dort tun können)

hier ist meine Datatemplate:

<DataTemplate x:Key="TreeCellTemplate"> 
    <Grid> 
     <TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Margin="5,0,0,0"> 
      <TextBlock.Text> 
       <MultiBinding Converter="{StaticResource RowColumnToCellConverter}" ConverterParameter="Text"> 
        <Binding /> 
        <Binding RelativeSource="{RelativeSource AncestorType=DataGridCell}" Path="Column" /> 
       </MultiBinding> 
      </TextBlock.Text> 
     </TextBlock> 
    </Grid> 
</DataTemplate> 

und hier ist mein Konverter:

public class RowColumnToCellConverter : MarkupExtension, IMultiValueConverter 
    { 
     public RowColumnToCellConverter() { } 

     public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
     { 
     XwpfRow row = values[0] as XwpfRow; 
     XwpfTreeColumn column = values[1] as XwpfTreeColumn; 

     if (row == null || column == null) return DependencyProperty.UnsetValue; 

     TreeCell treeCell = (TreeCell)row[column.DataGrid.Columns.IndexOf(column)]; 
     switch ((string)parameter) 
     { 
      case "Text": return treeCell.Text; 
      case "Expanded": return treeCell.Expanded; 
      case "ShowExpandSymbol": return treeCell.ShowExpandSymbol; 
      case "CurrentLevel": return new GridLength(treeCell.CurrentLevel * 14); 

      default: 
       throw new MissingMemberException("the property " + parameter.ToString() + " is not defined for the TreeCell object"); 
     } 
     } 

     public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
     { 
     throw new NotSupportedException(); 
     } 

     public override object ProvideValue(IServiceProvider serviceProvider) 
     { 
     return new RowColumnToCellConverter(); 
     } 
    } 

dies das MVVM Modell speichert, und ich ziehe diese Art und Weise, Dinge zu tun, weil ich wirklich mit am Wenigsten xAML-Parser mache "dynamische" Datatemplates, aber aus meiner Sicht ist es immer noch ein hässlicher Hack.

Ich wünsche den Jungs bei MS uns geben würde, eine Art und Weise Zellen anstelle von Zeilen zu erhalten, wie Datacontexts Templat-Spalten on the fly zu erzeugen in der Lage sein ...

Hoffnung diese

EDIT hilft: In Ihrem Fall sollte der Konverter eigentlich viel einfacher sein (Sie können die Instanz der Zelle direkt zurückgeben, wenn ich mich nicht täusche, und Sie brauchen keinen Parameter), aber ich habe die komplexere Version dennoch verlassen, einfach rein Falls jemand anderes ein ähnliches Problem hat

+0

Interessante Idee, obwohl ich es nicht als Silverlight verwenden kann, unterstützt 'MultiBinding' nicht. –

+0

ARF, tut mir leid, ich bin nicht vertraut mit Silverlight, also wusste ich nicht. Ich kann bestätigen, dass dies in WPF ziemlich gut funktioniert, ich habe es heute wieder verwendet und bin ziemlich glücklich mit der Methode, obwohl es hässlich ist. – David

0

Ich habe ein Simila betrachtet r Problem und habe nur eine Handvoll nützlicher Muster gefunden. Das ganze "dynamische Spalten" -Problem ist in Silverlight ein interessantes Problem.

Gestern fand ich diese Seite Silverlight DataGrid with Dynamic Columns auf Travis Pettijohns Website während meiner Suchen.

Zuvor hatte ich das von Colin Eberhardt umrissene "Indexkonverter" -Muster verwendet, das fantastisch funktioniert ... solange Sie DataGridTextColumn verwenden. Alles kann in Code hinterher getan werden, und ich hatte keine Probleme, Stile zur Laufzeit anzuwenden. Allerdings ist es jetzt meine Anforderung, einige Formatierungen auf Zellebene anzuwenden - den Hintergrund für die Zelle usw. zu ändern - was bedeutet, dass ein DataGridTemplateColumn erforderlich ist.

Das große Problem mit einem DataGridTemplateColumn für mich war, dass ich die Bindung im Code nicht festlegen kann. Ich weiß, wir können es bauen, indem wir XAML analysieren, aber wie jeder andere auch, der wie ein massiver Hack aussieht und für den Nintendo unerreichbar ist.

Das von Travis skizzierte Muster (der erste Link oben) ist völlig anders. Erstellen Sie zu Ausführungszeit (d. H. Ladezeit der Seite) die Spalten, die Sie in Ihrem Raster benötigen. Das bedeutet, dass Sie durch Ihre Sammlung iterieren und eine Spalte für jedes Element mit der entsprechenden Überschrift usw. hinzufügen. Implementieren Sie dann einen Handler für das Ereignis RowLoaded. Wenn jede Zeile geladen ist, setzen Sie einfach den DataContext für jede Zelle auf die entsprechende Eigenschaft/Eigenschaft Index des Elternteils.

private void MyGrid_RowLoaded(object sender, EventArgs e) 
{ 
    var grid = sender as DataGrid; 
    var myItem = grid.SelectedItem as MyClass; 
    foreach (int i = 0; i < myItem.ColumnObjects.Count; i++) 
    { 
     var column = grid.Columns[i]; 
     var cell = column.GetCellContent(e.Row) 
     cell.DataContext = myItem.ColumnObjects[i]; 
    } 
} 

Dies hat die Notwendigkeit für mich, den Index-Konverter zu verwenden, entfernt. Sie können wahrscheinlich eine Binding verwenden, wenn Sie die cell.DataContext setzen, aber für mich ist es einfacher, die Vorlage einfach direkt an das zugrunde liegende Objekt zu binden.

Ich plane jetzt mehrere Vorlagen (wo jeder an die gleichen Eigenschaften auf meinem Zellobjekt binden kann) und zwischen ihnen beim Laden der Seite wechseln.Sehr saubere Lösung.

+0

Diese 'RowLoaded' sieht wie eine wirklich gute Lösung aus. Obwohl ich es nicht als Lösung sehe, wenn Sie Spalten dynamisch hinzufügen und entfernen müssen. Dann ist es wahrscheinlich besser, die Vorlage aus dem String zu laden und dort die Bindung "myItem.ColumnObjects [i]" durch den tatsächlichen Index zu ersetzen. –

+0

@LukasCenovsky Ich habe den dynamischen Spalten Code nicht gepostet, aber Sie können es in den Links anzeigen. Wie bereits erwähnt, ist dies der einfache Teil - 'foreach (var item in columnsToAdd) {grid.Columns.Add (new DataGridTemplateColumn() {Header = item.HeaderProperty};}' –

+0

@LukasCenovsky Fügen Sie die 'DataTemplate' hinzu, die Sie möchten Ich werde versuchen, meine Antwort später zu bearbeiten ... –