Laden...

Combobox mit Treeview zeigt nur Klassenname an

Letzter Beitrag vor einem Jahr 10 Posts 714 Views
Combobox mit Treeview zeigt nur Klassenname an

Hallo zusammen,

wiedermal ein kleines Problem:

Ich hab mir eine Combobox gebaut mit einem TreeView drin. In der TreeView hab ich einige Bindings an den Textblöcken.  Die Combobox weiß nun vermutlich nicht, welches Binding als SelectedItem angezeigt werden soll und zeigt mir nur den Klassennamen an. Hier meine Combobox:

<ComboBox HorizontalContentAlignment="Stretch" ItemsSource="{Binding GlobalInstance.DropdownDamageArea}" SelectedValuePath="Name" ItemContainerStyle="{StaticResource treeCombobox}" ScrollViewer.CanContentScroll="False">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding Name}"/>
                <TreeView x:Name="tree" Margin="5" ItemsSource="{Binding CodeGroups}" Style="{StaticResource comboTreeView}">
                    <TreeView.Template>
                        <ControlTemplate>
                            <ItemsPresenter/>
                        </ControlTemplate>
                    </TreeView.Template>
                    <TreeView.ItemTemplate>
                        <HierarchicalDataTemplate ItemsSource="{Binding DamageCodes}">
                            <TextBlock Text="{Binding Name}"/>
                            <HierarchicalDataTemplate.ItemTemplate>
                                <DataTemplate>
                                    <TextBlock x:Name="text">
                                        <Run Text="{Binding Id}"/>
                                        <Run Text=" - "/>
                                        <Run Text="{Binding CodeDescription}"/>
                                    </TextBlock>
                                </DataTemplate>
                            </HierarchicalDataTemplate.ItemTemplate>
                        </HierarchicalDataTemplate>
                    </TreeView.ItemTemplate>
                </TreeView>
            </StackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

Dazu gehört folgender Code:

private ObservableCollection<CodeArea> dropdownDamageArea;
public ObservableCollection<CodeArea> DropdownDamageArea
{
	get
	{
		return dropdownDamageArea;
	}
	set { dropdownDamageArea = value; OnPropertyChanged(); }
}
public partial class CodeArea
{
    [Key]
    public int Id { get; set; }

    [StringLength(50)]
    public string? Name { get; set; }

    [InverseProperty("GroupArea")]
    public virtual ICollection<CodeGroup> CodeGroups { get; set; } = new List<CodeGroup>();
}
public partial class CodeGroup
{
    [Key]
    public int Id { get; set; }

    [StringLength(50)]
    public string? Name { get; set; }

    public int? GroupAreaId { get; set; }

    [InverseProperty("CodeGroup")]
    public virtual ICollection<DamageCode> DamageCodes { get; set; } = new List<DamageCode>();

    [ForeignKey("GroupAreaId")]
    [InverseProperty("CodeGroups")]
    public virtual CodeArea? GroupArea { get; set; }
}
public partial class DamageCode
{
    [Key]
    [StringLength(4)]
    [Unicode(false)]
    public string Id { get; set; } = null!;

    public int CodeGroupId { get; set; }

    [StringLength(50)]
    public string? CodeDescription { get; set; }

    [ForeignKey("CodeGroupId")]
    [InverseProperty("DamageCodes")]
    public virtual CodeGroup CodeGroup { get; set; } = null!;

    [InverseProperty("DamageCode")]
    public virtual ICollection<WorkActionsHardware> WorkActionsHardwareDamageCodes { get; set; } = new List<WorkActionsHardware>();

    [InverseProperty("ProcessCode")]
    public virtual ICollection<WorkActionsHardware> WorkActionsHardwareProcessCodes { get; set; } = new List<WorkActionsHardware>();
}

Ich würde gerne diesen Textblock als SelectedItem in der Combobox anzeigen:

<TextBlock x:Name="text">
	<Run Text="{Binding Id}"/>
	<Run Text=" - "/>
	<Run Text="{Binding CodeDescription}"/>
</TextBlock>

Es wird mir aber nur folgendes angezeigt (Siehe Anhang)

Zur Info: Wir arbeiten mit dem EntityFramework.

DisplayMemberPathgeht ja leider nicht, da das ItemTemplate gesetzt wird in der Combobox. Und die ToString()- Methode überschreiben geht glaub ich auch nicht. Dies müsste ja im Model gemacht werden. Aber bei DB Änderungen wird ja jedesmal vom EF das Model überschrieben. Deshalb ist das glaub ich auch keine Lösung.

Ich hoffe Ihr habt noch Ideen

ComboBox mit TreeView-Elementen? Das habe ich bisher noch nicht gesehen.

Zitat von theSoulT:

Die Combobox weiß nun vermutlich nicht, welches Binding als SelectedItem angezeigt werden soll und zeigt mir nur den Klassennamen an.

Das ist die Voreinstellung wenn weder DisplayMemberPath gesetzt ist noch die ToString-Methode überschrieben wurde, s.a. Understanding SelectedValue, SelectedValuePath, SelectedItem & DisplayMemberPath + Demo (im Abschnitt "Extra Reading →Verbal Explanation" bei DisplayMemberPath).

Und die ToString()- Methode überschreiben geht glaub ich auch nicht. ...

Doch das wäre dann eine der beiden Möglichkeiten (es werden ja nur die Daten im Model neu zugewiesen, der Code bleibt ja derselbe).

Ich würde gerne diesen Textblock als SelectedItem in der Combobox anzeigen:

<TextBlock x:Name="text">
  ...
</TextBlock>

Das versteh ich nicht, denn dieser TextBlock ist doch in jedem TreeView-Item enthalten, d.h. du hast eine (hierarchische) Liste davon. Willst du dann alle anzeigen oder nur den ersten Eintrag davon?


PS: Hattest du meine letzte Antwort aus deinem Beitrag ListView zeigt nur Objektnamen an, Problem mit ContentPresenter und DataTemplates noch gelesen? Oder hast du inzwischen eine andere Lösung (außer der Codeduplikation) gefunden?

Das ist die Voreinstellung wenn weder DisplayMemberPath gesetzt ist noch die ToString-Methode überschrieben wurde

Aber wenn man das ItemTemplate setzt mit einem Textblock zb. und dann die Text Eigenschaft bindet, wird doch dieses Binding übernommen für den angezeigten Wert? 

es werden ja nur die Daten im Model neu zugewiesen, der Code bleibt ja derselbe)

Das muss ich mal probieren. Ich war der Meinung die komplette Model-Klasse wird erneuert

Das versteh ich nicht, denn dieser TextBlock ist doch in jedem TreeView-Item enthalten, d.h. du hast eine (hierarchische) Liste davon. Willst du dann alle anzeigen oder nur den ersten Eintrag davon?

Ich meine das selektierte Element soll in der Struktur angezeigt werden: Id - CodeDescription

ComboBox mit TreeView-Elementen? Das habe ich bisher noch nicht gesehen

War für meinen Fall ganz praktisch. Ich nehme aber gerne auch andere Ideen auf. Im Anhang mal ein Bild wie es aussieht 😃 In diesem Fall sollte in der Combobox oben "1001 - Umwelteinflüsse" stehen.

PS: Hattest du meine letzte Antwort aus deinem Beitrag ListView zeigt nur Objektnamen an, Problem mit ContentPresenter und DataTemplates noch gelesen? Oder hast du inzwischen eine andere Lösung (außer der Codeduplikation) gefunden?

Ne hatte ich noch nicht gesehen. Hab es bisher bis Codeduplikation gelöst. Muss ich mir dann nochmal anschauen 😃 Danke dir für dne Hinweis.

Hinweis: Deine Modelle sind wohl Datenbank Entitäten, weil sie ICollection verwenden, so wäre die korrekte Initialisierung ein HashSet und keine List.
Relationen zwischen Entitäten können nur ein mal existieren, weshalb EFCore unter der Haube auch HashSet verwendet. HashSet ist für solche Use Cases konzipiert und optimiert; List macht kein Sinn.

PS: wenn es Entitäten sind, dann solltest das vielleicht auch so benennen 😃

Ich denke, du hast ein falsches Verständnis von der ComboBox mit TreeView.

Du kannst zwar im TreeView einzelne Items (Zeilen) markieren, für die Anzeige in der ComboBox wird aber als SelectedItem immer nur ein Element der zugewiesenen ItemsSource ausgewählt (bei dir also ein CodeArea- Objekt).

Du müßtest ansonsten noch eine weitere Eigenschaft SelectedEntry(o.ä). erstellen und diese dann per Binding an das TreeView.SelectedItem aktualisieren lassen.

Aber was soll in der ComboBox angezeigt werden, wenn eine der übergeordneten Elemente (wie z.B. "QSVS Zuordnung" oder "Umwelteinflüsse") markiert wird?

Du kannst zwar im TreeView einzelne Items (Zeilen) markieren, für die Anzeige in der ComboBox wird aber als SelectedItem immer nur ein Element der zugewiesenen ItemsSource ausgewählt (bei dir also ein CodeArea- Objekt).

Stimmt, daran hab ich gar nicht gedacht. Das SelectedItem ist ja nur das von der Combobox und nicht vom TreeView. Und da ist es die CodeArea.

Du müßtest ansonsten noch eine weitere Eigenschaft SelectedEntry(o.ä). erstellen und diese dann per Binding an das TreeView.SelectedItem aktualisieren lassen.

Geht das denn überhaupt? Ich hab ja letzendlich für jedes Item der Combobox ein eigenes TreeView. Ich versuch mal mein Glück.

Aber was soll in der ComboBox angezeigt werden, wenn eine der übergeordneten Elemente (wie z.B. "QSVS Zuordnung" oder "Umwelteinflüsse") markiert wird?

Das wäre die nächste Frage. Die sollen eigentlich nicht gewählt werden können 😃

Hinweis: Deine Modelle sind wohl Datenbank Entitäten, weil sie ICollection verwenden, so wäre die korrekte Initialisierung ein HashSet und keine List.
Relationen zwischen Entitäten können nur ein mal existieren, weshalb EFCore unter der Haube auch HashSet verwendet. HashSet ist für solche Use Cases konzipiert und optimiert; List macht kein Sinn.

PS: wenn es Entitäten sind, dann solltest das vielleicht auch so benennen 😃

Hab es an meinen Kollegen weitergegebn 😃 Danke

Was soll denn der ausgewählte Datensatz bei der ComboBox genau sein (außer dem gewählten CodeArea-Objekt)?


Für das Deaktivieren der Selektion bei Nicht-Endknoten probiere mal

<Trigger Property="HasItems" Value="true">
   <Setter Property="Focusable" Value="false" />
</Trigger>

(weitere Möglichkeiten s. z.B. Disable WPF TreeView (or TreeViewItem) selection?)

Zitat von Th69

Was soll denn der ausgewählte Datensatz bei der ComboBox genau sein (außer dem gewählten CodeArea-Objekt)?

Letzendlich soll der DamageCode ausgewählt sein. Den brauch ich dann zur weiteren Verarbeitung.

Dann muß dieser DamageCode als eigenständige Eigenschaft der zur Bindung benutzten Klasse (bisher CodeArea) hinzugefügt werden.

Da sich für mich deine bisherigen Klassen aber eher als Datenbank-Stammdaten lesen, solltest du eine neue Klasse erstellen und davon dann an eine ObservableCollection<...>binden.

Sorry für die späte Antwort. Ich bin grad am grübeln, ob ich das Thema irgendwie ohne dieses Treeview lösen kann. Bin damit nicht wirklich zufrieden 😦 Das scheint sehr komplex zu sein...