Hallo!
Ich fülle ein GridView mit Daten aus einer Benutzer-SQL Abfrage. Die Anzahl der Grid-Spalten sind daher dynamisch.
Dies realisiere ich über einen GridView-Converter entsprechend dem Vorschlag von hier den ich etwas modifiziert habe:
<ListView ItemsSource="{Binding DatenListe}" View="{Binding SpaltenKonfiguration, Converter={StaticResource ConfigToDynamicGridViewConverter}}"/>
Das Grundprinzip: Der Converter erstellt aus einer Liste<>, in der die GridViewColumn-Eigenschaften definiert werden, ein (dynamisches) GridView.
Da ich für einzelne Spalten eine Eingabemöglichkeit realisieren möchte, benutze ich ein GridViewCell-Template.
Der GridView Converter bindet im Standard den DatenSpalten-Name an die DisplayMemberBinding - Eigenschaft der Grid-Spalte.
Deshalb habe ich den GridView-Converter so geändert, dass er bei Angabe eines Cell-Templates das DisplayMemberBinding übergeht und damit die Datenbindung im Cell-Template realisiert wird. (Anmerkung: Bei DisplayMemberBinding wird das Cell-Template nicht wirksam!)
[ValueConversion(typeof(ColumnConfig), typeof(GridView))]
/// <summary>
/// Value Converter zum dynamischen Binden einer Spalten-Liste an eine Grid-View.
/// </summary>
public class ConfigToDynamicGridViewConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is ColumnConfig config) // Ist der Konverter an eine ColumnConfig-Instanz gebunden?
{
GridView gridView = new(); // Grid-View initalisieren
foreach (Column column in config.Columns) // Spalten-Liste iterieren
{
GridViewColumn gvc = new() // GridView-Spalte initalisieren
{
Header = column.SpaltenBezeichnung,
HeaderContainerStyle = column.HeaderContainerStyle,
CellTemplate = column.CellTemplate
};
if(column.CellTemplate == null) // Wenn kein Cell-Template angegeben wurde -> DisplayBinding implementieren, ansonsten muss das DataBinding im CellTemplate definiert werden!
{
Binding binding = new(column.NameDatenSpalte); // (DisplayMember) Binding initalisieren
gvc.DisplayMemberBinding = binding; // DisplayMemberBinding das Daten-Binding übergeben
}
gridView.Columns.Add(gvc); // GriedView-Spalte dem GridView hinzufügen
}
return gridView; // Grid-View adäquat der übergebenen ColumnConfig-SpaltenListe zurückgeben.
}
return Binding.DoNothing; // Im Fehlerfall "Leeres"-Binding als Grid-View zurückgeben
}
Beispiel für eine GridView-Spalten Generierung:
SpaltenKonfiguration = new arWPF.WPF_Objekte.ColumnConfig // Spalten-Konfiguration initalisieren
{
Columns = new List<arWPF.WPF_Objekte.Column>
{
new arWPF.WPF_Objekte.Column("Spalte1 Produkt", "Bezeichnung"), // Spalte 1
new arWPF.WPF_Objekte.Column("Spalte2 Bewertung", "Bewertung", null, GVCellTemplate) // Spalte 2
}
};
Alles schön, funktioniert (siehe Bild).
Jetzt habe ich allerdings das Problem, dass die Datenbindung im Cell-Template fest angegeben ist:
<DataTemplate x:Key="GridViewCellTemplate">
<Border BorderBrush="Green" BorderThickness="1" Padding="2">
<TextBox MinWidth="100" Text="{Binding Path=Bewertung, UpdateSourceTrigger=PropertyChanged}" />
</Border>
</DataTemplate>
(Wie) kann ich das Binding des übergebenen DataTemplates dynamisch gestalten?
Genauer gesagt, geht das bereits in XAML?
Hallo!
Da ich einen Weg gefunden habe das DataTemplate aus einem XAML-Text zu erstellen, kann ich auch bei komplexen CellTemplates spezifische DataTemplates erstellen und diese der Auflistung des GridView Converters hinzufügen.
Als erstes definiere ich eine DataTemplate-Vorlage als XAML-Text, wobei die spezifische Data-Bindung als Platzhalter angegeben wird (Path=) und erstelle zweitens über eine Methode daraus das spezifische DataTemplate:
// 1. DataTemplate-Vorlage
string DataTemplateVorlage = @"<Border BorderBrush = ""Green"" BorderThickness = ""1"" Padding = ""2"" > ";
DataTemplateVorlage += @"<TextBox MinWidth = ""100"" Text = ""{Binding Path=, UpdateSourceTrigger=PropertyChanged}"" /> ";
DataTemplateVorlage += @"</Border >";
// 2. Spezifisches DataTemplate
DataTemplate dataTemplate = GetDynamicDataTemplate(DataTemplateVorlage, "Bewertung");
/// <summary>
/// Konvertiert einer XAML-DataTemplate Vorlage ein DataTemplate.
/// </summary>
/// <param name="DataTemplateVorlage">XAML-DataTemplate (Vorlage) das in ein DataTemplate konvertiert werden soll.</param>
/// <param name="DatenSpalte">Bezeichnung der Datenspalte die im DataTemplate verwendet werden soll.</param>
/// <param name="BindingIndikator">[Optional] Platzhalter für die Binding-Angabe des DataTemplates. Standard: Path=</param>
/// <returns></returns>
public static DataTemplate GetDynamicDataTemplate(string DataTemplateVorlage, string DatenSpalte, string BindingIndikator = "Path=")
{
DataTemplateVorlage = DataTemplateVorlage.Replace(BindingIndikator, BindingIndikator + DatenSpalte);
string zDataTemplate = @"<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""> ";
zDataTemplate += DataTemplateVorlage + "</DataTemplate>";
StringReader stringReader = new(zDataTemplate);
XmlReader xmlReader = XmlReader.Create(stringReader);
return System.Windows.Markup.XamlReader.Load(xmlReader) as DataTemplate;
}
Dieses spezifische DataTemplate übergebe ich der GridView SpaltenConfigurations-Liste welche der GridView-Converter zur dynamischen Erstellung der GridView benutzt.
SpaltenKonfiguration = new arWPF.WPF_Objekte.ColumnConfig // Spalten-Konfiguration initalisieren
{
Columns = new List<arWPF.WPF_Objekte.Column>
{
new arWPF.WPF_Objekte.Column("Spalte1 Produkt", "Bezeichnung"), // Spalte 1
new arWPF.WPF_Objekte.Column("Spalte2 Bewertung", "Bewertung", null, dataTemplate) // Spalte 2
}
};
Ziel erreicht. Dynamisches ListView mit Eingabemöglichkeit (durch CellTemplate).
Nur damit hier keiner auf dumme Ideen kommt: So wie hier sollte man auf keinen Fall mit XAML und C# Benutzeroberflächen generieren:
string DataTemplateVorlage = @"<Border BorderBrush = ""Green"" BorderThickness = ""1"" Padding = ""2"" > ";
Wie man DataTemplates korrekt erstellt und verwendet, findet man u.a. in diesem Artikel oder in den dort verlinkten Seiten der Dokumentation.
Weeks of programming can save you hours of planning