Laden...

Darstellung von mehren Tabellen in TreeView

Erstellt von mvollmer vor 12 Jahren Letzter Beitrag vor 12 Jahren 5.133 Views
M
mvollmer Themenstarter:in
61 Beiträge seit 2011
vor 12 Jahren
Darstellung von mehren Tabellen in TreeView

verwendetes Datenbanksystem: Microsoft SQL

Guten Tag,

ich bräuchte etwas Hilfe. Ich bekomm es nicht hin, das 2 Tabellen in einem TreeView angezeigt werden.

Es sind folgende 3 Tabellen vorhanden:

Versions
---------
VersionID
VersionNumber
Programs
-----------
ProgramID
Name

Die Zwischentabelle:

Program_has_Versions
--------------------------
ProgramVersionID
Program_ID
Version_ID

Ich möchte nun einen TreeView erstellen indem die Programme aufgelistet sind und die Versionen untergeordnet.

Etwa so

Name1
- Version 1
- Version 2
Name 2
- Version X
- Version Y

Folgendermaßen sieht der XAML Code aus:

     <Grid x:Name="LayoutRoot">
        <Grid.Resources>
            <DataTemplate x:Key="VersionTemplate">
                <TextBlock Text="{Binding VersionNumber}" />
            </DataTemplate>

            <sdk:HierarchicalDataTemplate x:Key="ProgramTemplate" ItemsSource="{Binding Versions}" ItemTemplate="{StaticResource VersionTemplate}" >
                <TextBlock Text="{Binding Name}" />
            </sdk:HierarchicalDataTemplate>
            
        </Grid.Resources>
        <StackPanel DataContext="{Binding Data,ElementName=ProgramsDS}">
            <riaControls:DomainDataSource Name="ProgramsDS" LoadSize="20" AutoLoad="True" QueryName="GetProgramVersions">
                <riaControls:DomainDataSource.DomainContext>
                    <ds:ProgramContext/>
                </riaControls:DomainDataSource.DomainContext>
            </riaControls:DomainDataSource>
            <sdk:DataGrid ItemsSource="{Binding Data,ElementName=ProgramsDS}"/>
            <TextBlock Text="Programm Übersicht" Style="{StaticResource HeaderTextStyle}" />
            <sdk:TreeView ItemsSource="{Binding}" ItemTemplate="{StaticResource ProgramTemplate}" />
        </StackPanel>
    </Grid>

Ich habe den Service um folgende Methode erweitert :

public IQueryable<Programs> GetProgramVersions()
        {
            return this.ObjectContext.Programs.Include("Versions");
        }

Aber der Include kann ja nicht funktionieren, da sie keine Relation zueinander haben. Ich habe nun einige Wege versucht, aber leider find ich nicht den richtigen. Wie nutz ich genau die Zwischentabelle?

Mit freundlichen Grüßen

G
538 Beiträge seit 2008
vor 12 Jahren

Ich vermute mal stark, dass du EF benutzt, da kannst du ja durchaus die Navigationseigenschaften benutzen.
Wie dem auch sei - du brauchst in der Zwischentabelle eigentlich nur zwei ID_s (Programm und Version) dann haste auch deine direkte Abhängigkeit in EF

Der Vorteil der Klugheit liegt darin, dass man sich dumm stellen kann - umgekehrt ist das schon schwieriger (K. Tucholsky)
Das Problem mit Internet-Zitaten ist, dass sie oftmals zu unrecht als authentisch angenommen werden. (K. Adenauer)

M
mvollmer Themenstarter:in
61 Beiträge seit 2011
vor 12 Jahren

Ja ich nutze das EF.

Hier wurde mir empfohlen jeder Tabelle einen PK zu geben, deswegen hab ich es so gemacht.

G
538 Beiträge seit 2008
vor 12 Jahren

Im Endeffekt musst du selbst wissen, wie du es machst, aber wie der letzte Beitrag im anderen Thread sagt, ist das mit dem künstlichen PK gar nicht notwendig ...

Du kannst vielleicht mal versuchen das Include auf deiner Zwischentabelle auszuführen statt auf der Program-Tabelle.

Aber was gehe sollte:


this.ObjectContext.Programs.SelectMany(p => p.Program_has_Versions.Version);

oder so ähnlich

Der Vorteil der Klugheit liegt darin, dass man sich dumm stellen kann - umgekehrt ist das schon schwieriger (K. Tucholsky)
Das Problem mit Internet-Zitaten ist, dass sie oftmals zu unrecht als authentisch angenommen werden. (K. Adenauer)

M
mvollmer Themenstarter:in
61 Beiträge seit 2011
vor 12 Jahren

Aber was gehe sollte:

  
this.ObjectContext.Programs.SelectMany(p => p.Program_has_Versions.Version);  
  

Das funktioniert nicht bei Program_has_Versions kann ich dann wieder Select etc machen aber kein Version

Du kannst vielleicht mal versuchen das Include auf deiner Zwischentabelle auszuführen statt auf der Program-Tabelle.

Klar -.-, gute Idee das Hilft mir schonmal etwas weiter 😃

Jetzt hab ich mal dann Programs und Versions Included

Und folgenden XAML Code

<Grid.Resources>
            <DataTemplate x:Key="VersionTemplate">
                <TextBlock Text="{Binding VersionNumber}" />
            </DataTemplate>

            <sdk:HierarchicalDataTemplate x:Key="ProgramTemplate" ItemsSource="{Binding Versions}" ItemTemplate="{StaticResource VersionTemplate}" >
                <TextBlock Text="{Binding Programs.Name}" />
            </sdk:HierarchicalDataTemplate>
            
        </Grid.Resources>
        <StackPanel>
            <riaControls:DomainDataSource Name="Program_has_VersionDS" LoadSize="20" AutoLoad="True" QueryName="GetPrograms_has_Versions">
                <riaControls:DomainDataSource.DomainContext>
                    <ds:ProgramContext/>
                </riaControls:DomainDataSource.DomainContext>
            </riaControls:DomainDataSource>

            <sdk:DataGrid ItemsSource="{Binding Data,ElementName=Program_has_VersionDS}"/>
            <TextBlock Text="Programm Übersicht" Style="{StaticResource HeaderTextStyle}" />
            <sdk:TreeView ItemsSource="{Binding Data,ElementName=Program_has_VersionDS}" ItemTemplate="{StaticResource ProgramTemplate}" />

Wie müssen die Bindings nun aussehen? Da ich ja nun noch explizit an Programs.Name bzw Versions.VersionNumber binden möchte.

G
538 Beiträge seit 2008
vor 12 Jahren

Warum kannst du kein Version nutzen bei den Program_has_Versions?
Da sollte es doch eine Navigationseigenschaft geben, oder?

Der Vorteil der Klugheit liegt darin, dass man sich dumm stellen kann - umgekehrt ist das schon schwieriger (K. Tucholsky)
Das Problem mit Internet-Zitaten ist, dass sie oftmals zu unrecht als authentisch angenommen werden. (K. Adenauer)

M
mvollmer Themenstarter:in
61 Beiträge seit 2011
vor 12 Jahren

Ja Tabelle Programs, Versions haben jeweils die Navigationseigenschaft Programs_has_Versions. Die Tabelle Programs_has_Versions hat die Navigationseigenschaften Programs und Versions.

Aber es klappt nicht, wie oben erwähnt.

G
538 Beiträge seit 2008
vor 12 Jahren

Warum klappt es denn nicht?
Denn SelectMany erfüllt ja ziemlich genau den Zweck, den ich aus deiner Methode erkennen konnte
Du könntest auch über das Programm gruppieren und dir damit die Daten holen

Abgesehen davon hast du immer noch die Möglichkeit den künstlichen PK zu entfernen ...

Der Vorteil der Klugheit liegt darin, dass man sich dumm stellen kann - umgekehrt ist das schon schwieriger (K. Tucholsky)
Das Problem mit Internet-Zitaten ist, dass sie oftmals zu unrecht als authentisch angenommen werden. (K. Adenauer)

M
mvollmer Themenstarter:in
61 Beiträge seit 2011
vor 12 Jahren
public IQueryable<Programs> GetProgramVersions()
{
            return this.ObjectContext.Programs.SelectMany(e => e.Programs_has_Versions.Version);
}

Fehler:
"System.Data.Objects.DataClasses.EntityCollection<.Web.Programs_has_Versions>" enthält keine Definition für "Version", und es konnte keine Erweiterungsmethode "Version" gefunden werden, die ein erstes Argument vom Typ "System.Data.Objects.DataClasses.EntityCollection<.Web.Programs_has_Versions>" akzeptiert. (Fehlt eine Using-Direktive oder ein Assemblyverweis?)

Aber indem ich das über die Zwischentabelle abgefragt habe, ist es doch schon fast fertig? Der Datensatz ist ja da, ich muss dann jetzt nur noch das richtige Binding machen, oder?

M
mvollmer Themenstarter:in
61 Beiträge seit 2011
vor 12 Jahren

*Push*

Habe nun feststellen müssen, das wenn ich diese Abfrage gefiniere Versions und Programs = null ist

public IQueryable<Programs_has_Versions> GetPrograms_has_Versions()
{
      return this.ObjectContext.Programs_has_Versions.Include("Versions").Include("Programs");
}

Warum sind die null?

G
538 Beiträge seit 2008
vor 12 Jahren

Schmeiß doch einfach mal diesen künstlichen PK über Bord, lass das Entitymodell neu generieren und du hast schon mal nur noch eine Beziehung über die du dir gedanken machen musst ...

Außerdem:
du schreibst "this.ObjectContext" - ich benutze immer entities .. kanns damit was zu tun haben?

Der Vorteil der Klugheit liegt darin, dass man sich dumm stellen kann - umgekehrt ist das schon schwieriger (K. Tucholsky)
Das Problem mit Internet-Zitaten ist, dass sie oftmals zu unrecht als authentisch angenommen werden. (K. Adenauer)

M
mvollmer Themenstarter:in
61 Beiträge seit 2011
vor 12 Jahren

Der PK wird aber noch von einer anderen Tabelle benötigt (Die ich hier jetzt nicht aufgezählt habe).

Außerdem:
du schreibst "this.ObjectContext" - ich benutze immer entities .. kanns damit was zu tun haben?

Ne denke ich nicht, da die Domänendienstklasse das so generiert hat und die anderen Abfragen alle wunderbar funktionieren.

Da es ja mit Include nicht funktioniert könnte es evnt mit nem Join funktionieren?

G
538 Beiträge seit 2008
vor 12 Jahren

Kann schon - aber das gibt dann neue Klassen, die nicht mehr denen aus dem Entity-Container entsprechen ...

Grade mal ne andere (vielleicht doofe) Idee: sind überhaupt Daten vorhanden in der Datenbank?

Der Vorteil der Klugheit liegt darin, dass man sich dumm stellen kann - umgekehrt ist das schon schwieriger (K. Tucholsky)
Das Problem mit Internet-Zitaten ist, dass sie oftmals zu unrecht als authentisch angenommen werden. (K. Adenauer)

M
mvollmer Themenstarter:in
61 Beiträge seit 2011
vor 12 Jahren

Ja Datensätzte sind vorhanden. Zum Testen hab ich auch ein DataGrid erstellt das dann wie folgt aussieht:

Eine weitere möglichkeit wäre doch in der DB eine View zu erstellen?

G
538 Beiträge seit 2008
vor 12 Jahren

Okay mal folgendes:

Wenn du von Program_has_Versions (PHV) kommst solltest du zu einer Entity immer nur einen Wert haben - soll heißen


PHV.Versions; //Exisitert nicht, denn du hast ausgehend von PHV nur 1:1 zuordnungen
PHV.Version; //Existiert, sollte _eine_ Version sein - kein Enumerable!
//gleiches gilt für
PHV.Program;
PHV.Programs;

!Es kann natürlich sein, dass es bei dir trotzdem Versions heißt, wenn du "Pluralize or Singularize" nicht aktiviert hattest.
Wie dem auch sei - es ist in den Daten dennoch kein IEnumerable

Ich bin mir jetzt grade nicht ganz sicher, aber genau damit hatte ich mal ein Problem im Zusammenhang mit einer Treeview. Weil die - glaube ich -IEnumerable in der DataSource erwartet.

//edit: Deutsch und so ...

Der Vorteil der Klugheit liegt darin, dass man sich dumm stellen kann - umgekehrt ist das schon schwieriger (K. Tucholsky)
Das Problem mit Internet-Zitaten ist, dass sie oftmals zu unrecht als authentisch angenommen werden. (K. Adenauer)

M
mvollmer Themenstarter:in
61 Beiträge seit 2011
vor 12 Jahren

Den Haken für "Pluralize or Singularize" habe ich nicht aktiviert.

Hab die Abfrage jetzt nochmal geprüft.

 public IQueryable<Programs_has_Versions> GetPrograms_has_Versions()
{
      var query = this.ObjectContext.Programs_has_Versions.Include("Versions").Include("Programs");
      return query;
}

Wenn ich mir dann query anschaue, ist dort alles vorhanden. Also die Abfrage funktioniert. Alle Einträge(Array of Programs_has_Versions) und Programs/Versions sind auch enthalten und mit den richtigen Daten gefüllt.

Die Frage die jetzt nur noch offen ist, wie stell ich das nun richtig dar?
Von mir aus kann das ganze auch in 2 Listsboxen dargestellt werden, es muss nicht unbedingt ein TreeView sein.

Habe es folgendermaßen probiert:

<ListBox x:Name="lbVer" Grid.Column="1" Grid.Row="2" DisplayMemberPath="Versions" ItemsSource="{Binding Data, ElementName=Program_has_VersionDS}" />

In der Listbox sind jetzt auch 3 Einträge, nur extrem klein.

T
146 Beiträge seit 2004
vor 12 Jahren

Hallo!

Als ich vor einiger meinen ersten Baum mit wpf Darstellen wollte hat mir folgender Artikel sehr weitergeholfen:

http://www.codeproject.com/KB/WPF/TreeViewWithViewModel.aspx

Ich kann dir nur empfehlen, das mal zu lesen und zu verstehen.

G
538 Beiträge seit 2008
vor 12 Jahren

Versions ist ja wenn du EF benutzt Partiell,

implementier mal ToString() oder nutze nicht Versions als Path sondern Versions.DisplayName ...

Der Vorteil der Klugheit liegt darin, dass man sich dumm stellen kann - umgekehrt ist das schon schwieriger (K. Tucholsky)
Das Problem mit Internet-Zitaten ist, dass sie oftmals zu unrecht als authentisch angenommen werden. (K. Adenauer)

M
mvollmer Themenstarter:in
61 Beiträge seit 2011
vor 12 Jahren

DisplayMemberPath="Versions.DisplayName" -> Kein unterschied

DisplayMemberPath="Versions.toString()" ->

Fehlermeldung:
Code: 4004
Category: ParserError
Message: Das Festlegen von Eigenschaft 'System.Windows.Data.Binding.Path' hat eine Ausnahme ausgelöst.
File:
Line: 1
Position: 120

G
538 Beiträge seit 2008
vor 12 Jahren

Gibts denn "DisplayName" bei dir? Du musst schon was auswählen, was existiert ...

Und ToString() sollst du in public partial class Versions { } überladen

Der Vorteil der Klugheit liegt darin, dass man sich dumm stellen kann - umgekehrt ist das schon schwieriger (K. Tucholsky)
Das Problem mit Internet-Zitaten ist, dass sie oftmals zu unrecht als authentisch angenommen werden. (K. Adenauer)

M
mvollmer Themenstarter:in
61 Beiträge seit 2011
vor 12 Jahren

Gibts denn "DisplayName" bei dir? Du musst schon was auswählen, was existiert ...

Ne, gibt nur VersionNumber. Die soll auch nachher angezeigt werden. Ist es Problematisch wenn VersionNumber ein float Wert ist?

DisplayMemberPath="Versions.VersionNumber" klappt ebenfalls nicht.

Ist ToString so richtig implementiert?

    [MetadataTypeAttribute(typeof(Versions.VersionsMetadata))]
    public partial class Versions
    {

        // Mit dieser Klasse können Sie den Eigenschaften der "Versions"-Klasse
        // benutzerdefinierte Attribute hinzufügen.
        //
        // Beispielsweise wird im Folgenden die Xyz-Eigenschaft als
        // notwendige Eigenschaft gekennzeichnet und das Format für die gültigen Werte angegeben:
        //    [Required]
        //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
        //    [StringLength(32)]
        //    public string Xyz { get; set; }
        public override string ToString()
        {
            return Convert.ToString(VersionNumber);
        }
        internal sealed class VersionsMetadata
        {

            // Metadatenklassen sind nicht für die Instanziierung vorgesehen.
            private VersionsMetadata()
            {
            }

            public EntityCollection<Programs_has_Versions> Programs_has_Versions { get; set; }

            public int VersionID { get; set; }

            public double VersionNumber { get; set; }
        }
    }

oder muss ich "Versions" zurückgeben?

M
mvollmer Themenstarter:in
61 Beiträge seit 2011
vor 12 Jahren

bump...

Das Problem besteht leider immernoch.

EDIT:

Habs jetzt anders gelöst. Doch zufrieden bin ich mit der Lösung nicht wirklich 😦

int SelectedProgramID = ((Programs)lbPro.SelectedItem).ProgramID;
Programs_has_Versions[] PhVAry = _RelationContext.Programs_has_Versions.Where(f => f.Program_ID == SelectedProgramID).ToArray<Programs_has_Versions>();
List<Versions> VerList = new List<Versions>();
foreach (var item in PhVAry)
{
      VerList.Add(_VersionsContext.Versions.Where(g => g.VersionID == item.Version_ID).First());
}

lbVer.ItemsSource = VerList;

Dies steht im SelectionChanged Event der Program Listbox. Beim auswählen werden nun die Versionen zum Program angezeigt.

G
538 Beiträge seit 2008
vor 12 Jahren

Also, nochmal alles zurück auf Anfang ..

Deine Datenquelle für die äußere Liste sind die Programme - die anderen Daten kommen über die Verknüpfungstabelle aus Versions.

In Worten also:
Versionen gruppiert nach Programmen (und das ist der entscheidende Hinweis)


entities.Versions.GroupBy(v => v.Programm_has_Versions.Programs)

Dadurch bekommst du für jedes Element einen SChlüssel (der dann das Programm ist) und alle seine "Kinder".

Es kann sein, dass meine GroupBy Syntax unzureichend ist, aber es soll die Idee zeigen

Der Vorteil der Klugheit liegt darin, dass man sich dumm stellen kann - umgekehrt ist das schon schwieriger (K. Tucholsky)
Das Problem mit Internet-Zitaten ist, dass sie oftmals zu unrecht als authentisch angenommen werden. (K. Adenauer)

M
mvollmer Themenstarter:in
61 Beiträge seit 2011
vor 12 Jahren

Eine kurze Zwischenfrage...

Ich nutzte ja ADO.NET Entity Data Model, das entspricht doch dem Entity Framework?

Nicht das wir aneinander vorbeireden.

entities.Versions.GroupBy(v => v.Programm_has_Versions.Programs)

Ich kann ab Programs_has_Versions auf kein weiteres Entity Zugreifen, nur noch auf SQL Befehle wie .GroupBy etc

G
538 Beiträge seit 2008
vor 12 Jahren

Ich würde

v => v.Prog_has_Ver.Select(phv => phv.Programs).Single()

versuchen

Der Vorteil der Klugheit liegt darin, dass man sich dumm stellen kann - umgekehrt ist das schon schwieriger (K. Tucholsky)
Das Problem mit Internet-Zitaten ist, dass sie oftmals zu unrecht als authentisch angenommen werden. (K. Adenauer)

M
mvollmer Themenstarter:in
61 Beiträge seit 2011
vor 12 Jahren

Ich habe das Zauberwort gefunden!

Es ist "[Include]" und muss in der metadata Klasse vor die Property gesetzt werden, damit sie auch serialisiert werden 😃

Bsp:

internal sealed class Programs_has_VersionsMetadata
        {

            // Metadatenklassen sind nicht für die Instanziierung vorgesehen.
            private Programs_has_VersionsMetadata()
            {
            }

            public int Program_ID { get; set; }

            [Include]
            public Programs Programs { get; set; }

            public int ProgramVersionID { get; set; }

            public EntityCollection<Tickets_has_Prog_has_Ver> Tickets_has_Prog_has_Ver { get; set; }

            public int Version_ID { get; set; }
            [Include]
            public Versions Versions { get; set; }
        }

Jetzt klappt alles wunderbar.

Viel Dank Grumbler85 für deine ausführliche Bemühung!