Laden...

Dynamisches GridView mit Eingabe-Möglichkeit

Letzter Beitrag vor einem Monat 3 Posts 271 Views
Dynamisches GridView mit Eingabe-Möglichkeit

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