Laden...

Datagrid mit variablen Spalten

Erstellt von Caveman vor 7 Jahren Letzter Beitrag vor 7 Jahren 2.046 Views
Caveman Themenstarter:in
187 Beiträge seit 2009
vor 7 Jahren
Datagrid mit variablen Spalten

Hallo,

ich versuche krampfhaft ein Datagrid zu bauen, bei der die Anzahl der Spalten variabel ist. Ein Beispiel als Bildanhang. Die Spalte "Pose name" ist immer vorhanden. Der Yx Spalten sind variabel und werden in einem anderen Datagrid definiert.
Ich habe größte Probleme, die variablen Spalten darzustellen. Das betrifft sowohl den Spaltenkopf als auch die Zellen.
Deshalb möchte ich hiermit um etwas Hilfe/Unterstützung bitten.

Hier mein derzeitiger Arbeitsstand:
Die Spaltenüberschriften werden jetzt der Einfachheit halber im Konstructor vom ViewModel erzeugt.

Die View:


        <DataGrid Margin="0,30,0,0" Grid.Row="3" AutoGenerateColumns="False" AlternatingRowBackground="Silver" ItemsSource="{Binding Poses}" >
            <DataGrid.CellStyle>
                <Style TargetType="DataGridCell">
                    <Setter Property="VerticalAlignment" Value="Stretch" />
                    <Setter Property="VerticalContentAlignment" Value="Stretch"/>
                </Style>
            </DataGrid.CellStyle>
            <DataGrid.Columns>
                <DataGridTextColumn Header="Pose name" Binding="{Binding Posename}"  />
                <DataGridTemplateColumn Width="auto">
                    <DataGridTemplateColumn.HeaderStyle>
                        <Style TargetType="DataGridColumnHeader">
                            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
                            <Setter Property="VerticalContentAlignment"  Value="Stretch" />
                            <Setter Property="Margin" Value="0" />
                            <Setter Property="ContentTemplate">
                                <Setter.Value>
                                    <DataTemplate>
                                        <ItemsControl ItemsSource="{Binding ValveNames}">
                                            <ItemsControl.ItemsPanel>
                                                <ItemsPanelTemplate>
                                                    <StackPanel Orientation="Horizontal">
                                                    </StackPanel>
                                                </ItemsPanelTemplate>
                                            </ItemsControl.ItemsPanel>
                                            <ItemsControl.ItemTemplate>
                                                <DataTemplate>
                                                    <Border  Width="70" >
                                                        <TextBlock Text="{Binding}" TextAlignment="Center"/>
                                                    </Border>
                                                </DataTemplate>
                                            </ItemsControl.ItemTemplate>
                                        </ItemsControl>
                                    </DataTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </DataGridTemplateColumn.HeaderStyle>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <CheckBox IsChecked="{Binding Valves.Values, Mode=OneWay}"  />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>

Das ViewModel:


    public class RwCreateEpMainWindowViewModel : ViewModelBase
    {
        private ObservableCollection<RwEpPoseCreationData> poses;
        private ObservableCollection<string> valveNames;

        public ObservableCollection<RwEpPoseCreationData> Poses
        {
            get { return poses; }
            set
            {
                if (poses != value)
                {
                    poses = value;
                    OnPropertyChanged();
                }
            }
        }

         public ObservableCollection<string> ValveNames
        {
            get { return valveNames; }
            set
            {
                if (valveNames != value)
                {
                    valveNames = value;
                    OnPropertyChanged();
                }
            }
        }

        public RwCreateEpMainWindowViewModel()
        {
            Poses = new ObservableCollection<RwEpPoseCreationData>();
            ValveNames = new ObservableCollection<string>() { "y1", "y2", "y3", "y4" };
        }
    }

Das Model:

    
public class RwEpPoseCreationData
    {
        public string Posename { get; set; }
        public Dictionary<string, bool> Valves { get; set; }

        public RwEpPoseCreationData()
        {
            Valves = new Dictionary<string, bool>();
         }
    }

2.080 Beiträge seit 2012
vor 7 Jahren

Die Columns-Property musst Du binden.
Hierfür bietet sich die CompositeCollection an, damit kannst Du mehrere Quell-Collections zusammen an ein Ziel binden.
So hast Du dann als erste Quelle die zur Entwicklungszeit bekannten Spalten, die Zweite bindest Du an die Liste der dynamischen Spalten.

Dynamische Spalten habe ich bisher immer im Zusammenhang mit Reflection genutzt.
Du brauchst ein Objekt, was nötige Spalten-Informationen besitzt (Name, Typ, etc.) und weiß, wie es den Wert für eine Zeile bekommt.
Dieses Objekt muss unabhängig von den Zeilen-Objekten sein, als rein Spalten-Information.

Wenn Du eine Liste dieser Spalten-Infos gebunden hast, brauchst Du natürlich noch das WPF-Spalten-Objekt dazu. Das müsst Du via Converter machen. Du brauchst also einen Converter, der anhand der Spalten-Informationen die Spalte dazu baut.

Um die Daten selber in eine Zelle zu bekommen, brauchst Du eine Methode.
Daran zu binden wird wohl nicht ganz so simpel werden, zumindest weiß ich im Moment nicht, wie man mit einem ObjectDataProvider ein Parameter in Form eines Bindings nutzen kann.
Mit einem Converter als Umweg sollte sich das auch lösen lassen. Ich hab mir z.B. mal einen Converter geschrieben, der den Namen der Methode als Property hat und als Converter-Parameter den Parameter für die Methode.

Ein konkretes Beispiel:
Ich hab letztens eine Oberfläche gebaut, die anhand des eingeloggten Users andere Properties anzeigt.
Das Zeilen-Objekt besitzt alle möglichen Properties.
Neben der Liste der Zeilen-Objekte gibt es noch eine Liste von Property-Informationen. Da finden sich Name und Typ, per Reflection gesammelt. Besagte Methode ruft dann den Wert der Property per Reflection ab.
Ich kann dann aus dem verwaltenden ViewModel heraus beliebig Property anzeigen oder verbergen.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.