Laden...

Dynamische Matrix virtualisieren

Letzter Beitrag vor einem Monat 3 Posts 277 Views
Dynamische Matrix virtualisieren

Hallo!

Ich möchte eine Matrix (Zeilen und Spalten) welche in einem Objekt-Typ (AblageTyp) definiert sind grafisch darstellen.

Der AblageTyp hat die (integer) Eigenschaften MatrixZeilen und MatrixSpalten.

public partial class AblageTyp
{
    /// <summary>
    /// ID des Ablage-Typ's.
    /// </summary>
    public long ID { get; set; } = DateTime.Now.Ticks;
    ...
    /// <summary>
    /// Anzahl der Spalten des Ablage-Typ's
    /// </summary>
    public int MatrixSpalten { get; set; } = 2;

    /// <summary>
    /// Anzahl der Zeilen des Ablage-Typ's
    /// </summary>
    public int MatrixZeilen { get; set; } = 3;
    ...    
}

Im ViewModel habe ich eine View einer AblageTyp'en-Liste.

class VMAblageTypenVerwaltung : MVVM_Base
{
    internal CollectionViewSource CVSAblageTypen { get; set; } = new CollectionViewSource();    // CVS deklarieren

    /// <summary>
    /// View der Ablage-Typen Liste.
    /// </summary>
    public ICollectionView AblageTypenListeView { get => CVSAblageTypen.View; }
    ...

Diese Daten-View zeige ich in einem ListView an.

<ListView ItemsSource="{Binding AblageTypenListeView}" IsSynchronizedWithCurrentItem="True">
    <i:Interaction.Behaviors>
        <local:AblageTypChangeBehavior/>
    </i:Interaction.Behaviors>
    <ListView.View>
        <GridView>
            <GridViewColumn DisplayMemberBinding="{Binding Path=Bezeichnung}" Header="Bezeichnung" />
            <GridViewColumn DisplayMemberBinding="{Binding Path=Kapazität}" Header="Kapazität" />
            <GridViewColumn DisplayMemberBinding="{Binding Path=MatrixZeilen}" Header="Matrix-Zeilen" />
            <GridViewColumn DisplayMemberBinding="{Binding Path=MatrixSpalten}" Header="Matrix-Spalten" />
        </GridView>
    </ListView.View>
</ListView>

Beim navigieren des Benutzers durch die AblageTyp'en wird über einen Behavior eine Methode (RefreshAblageTypMatrixListe) aufgerufen die im ViewModel  eine zentrale Matrix-Liste (AblageTypMatrixListe) in welcher Border entsprechend der Summe der aktuellen Matrix dynamisch gefüllt werden, aktualisiert.

public void RefreshAblageTypMatrixListe(AblageTyp aktAblageTyp)
{
    AblageTypMatrixListe.Clear();                                                       // Matrix-Liste leeren

    for (int i = 0; i < aktAblageTyp.MatrixZeilen * aktAblageTyp.MatrixSpalten; i++)    // Matrix iterieren
    {
        Border border = new();
        border.Margin = new(5); border.Background = Brushes.Blue;
        border.MinHeight = 20; border.MinWidth = 20;
        AblageTypMatrixListe.Add(border);
    }
    OnChanged(nameof(AblageTypMatrixListe));    // View der Matrix-Liste aktualisieren
}

Entsprechend dem Ablage-Typ besteht die Matrix-Liste aus einer unterschiedlichen Anzahl an Bordern.

Diese (in der passenden Matrix) möchte ich grafisch darstellen. Dazu verwende ich ein ItemControl dem ich als ItemsSource die dynamisch gefüllte Matrix-Liste zuordne.

<ItemsControl ItemsSource="{Binding AblageTypMatrixListe, UpdateSourceTrigger=PropertyChanged}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Height="200" Width="200" Rows="3" Columns="3" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

Beim ersten Aufruf wird die Anzahl an Border auch angezeigt (siehe Bild). Bei einem Wechsel des Ablage-Art Eintrages wird zwar die ViewModel Matrix-Liste korrekt aktualisiert aber die grafische Anordnung ändert sich nicht.

Es ergeben sich für mich 2 Fragen:

  1. Wie aktualisiere ich das ItemsControl?
  2. Wie binde ich meine Matrix an die Rows und Columns des UniformGrid?

Hallo!

Ein Teil der Fragen hat sich geklärt.

Die Aktualisierung der Anzeige des ItemsControl wird ausgeführt, wenn ich für die zentrale AblageTypMatrixListe eine OC verwende.

private ObservableCollection<Border> _AblageTypMatrixListe;
/// <summary>
/// Liste der Ablage-Typ Matrix Einträge
/// </summary>
public ObservableCollection<Border> AblageTypMatrixListe { get => _AblageTypMatrixListe; set { _AblageTypMatrixListe = value; OnChanged(nameof(AblageTypMatrixListe)); } }

Bei einer OC wird die View ja automatisch auch beim Verändern des Inhalts (hinzufügen/löschen...) aktualisiert aber ich war davon ausgegangen, dass die manuelle Aktualisierung der View in der Befüllungs-Methode (RefreshAblageTypMatrixListe) reicht, aber ... ?

public void RefreshAblageTypMatrixListe(AblageTyp aktAblageTyp)
{
    .... 
    OnChanged(nameof(AblageTypMatrixListe));    // View der Matrix-Liste aktualisieren
}

Nun muss ich nur noch die Matrix gebunden bekommen. Dann wärs perfekt.

So, geschafft!

Ich habe dem ViewModel noch zwei Eigenschaften (MatrixZeilen und MatrixSpalten) hinzugefügt, dem (beim Wechsel des Ablage-Typs) die Matrix-Werte des aktuellen Ablage-Typs zugeordnet werden:

public void RefreshAblageTypMatrixListe(AblageTyp aktAblageTyp)
{
    MatrixZeilen = aktAblageTyp.MatrixZeilen;
    MatrixSpalten = aktAblageTyp.MatrixSpalten;
    ...
}

und diese Werte im ItemsPanelTemplate an die zugeordneten Eigenschaften (Rows und Columns) des UniformGrid's gebunden.

<ItemsControl ItemsSource="{Binding AblageTypMatrixListe, UpdateSourceTrigger=PropertyChanged}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
                <UniformGrid Height="200" Width="200" 
                             Rows="{Binding MatrixZeilen, UpdateSourceTrigger=PropertyChanged}"
                             Columns="{Binding MatrixSpalten, UpdateSourceTrigger=PropertyChanged}"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

Jetzt wird die Matrix beim Ändern des Ablage-Typ's korrekt aktualisiert.