WPF: TreeView Control XAML HierachicalDataTemplate!



  • Hallo ich versuche gerade via XAML und meinen eigen "MyTreeNode" Knoten einen TreeView anzuzeigen! Es kommen keine fehler und nix alles ok, aber es wird nix angezeigt:

    Hier mein TreeView UserControl:

    WFP:

    <UserControl x:Class="Model3DUtil.SceneTreeViewer"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:module3D="clr-namespace:Model3DUtil.Model"
        Height="300" Width="300">
        <Grid>
            <Border x:Name="Secnne" Grid.Column="2" Background="CadetBlue">
                <TreeView 
                    Name="Scene3DViwer" 
                    ItemsSource="{Binding Root}">
    
                    <TreeView.Resources>
    
                        <HierarchicalDataTemplate
                            DataType="{x:Type module3D:MyTreeNode}" 
                            ItemsSource="{Binding Children}">
                            <TextBlock Text="{Binding Name}" Margin="5" />
                        </HierarchicalDataTemplate>
    
                    </TreeView.Resources>
    
                </TreeView>
            </Border>
    
        </Grid>   
    </UserControl>
    

    Code Behind:

    /// <summary>
        /// Interaktionslogik für SceneTreeViewer.xaml
        /// </summary>
        public partial class SceneTreeViewer : UserControl
        {
            public SceneTreeViewer()
            {
                InitializeComponent();
    
                Root = new MyTreeNode("root", new[] 
                {
                    new MyTreeNode("Child1"),
                    new MyTreeNode("Child2",new[]
                    {
                          new MyTreeNode("SubChild1")
                    })
                });
    
            }
    
            public MyTreeNode Root { get; private set; }
    
        }
    

    Hier meine "MyTreeNode " klasse!!

    public class MyTreeNode
        {
            public MyTreeNode(string name,IEnumerable<MyTreeNode> child)
                :this(name)
            {
                _children = new List<MyTreeNode>(child);
            }
            public MyTreeNode(string name) 
            {
                _children = new List<MyTreeNode>();
                Name = name;
            }
            public void Add(MyTreeNode node) 
            {
                _children.Add(node);
            }
    
            public bool IsLeaf { get { return !_children.Any(); } }
            public string Name { get; private set; }
            private List<MyTreeNode> _children = default(List<MyTreeNode>);
            public IEnumerable<MyTreeNode> Children { get { return _children; } }
        }
    

    Und so rufe ich es auf in meinem fenster!!!

    ......
    <Border x:Name="Secnne" Grid.Column="2" Background="CadetBlue">
                <modeltree:SceneTreeViewer x:Name="TEST"></modeltree:SceneTreeViewer>
    

    Der hintergrund des Borders hat die CadetBlue farbe, aber derTree vie darin wird nur weis angezeigt!!

    Hoffe ihr könnt mir helfen, weis nich mehr was ich noch probieren könnte!!



  • probier mal:

    binding in der xaml von
    ItemsSource="{Binding Root}"
    zu
    ItemsSource="{Binding}"

    und im code
    this.DataContext = Root;
    hinzu zu fuegen (nachdem Root gefuellt ist)

    geht es dann ?

    (davon das ich es fuer schlechten code halte sage ich derzeit mal nichts (keine observablecollection, keine propertychanges usw)[von mvvm ganz zu schweigen])



  • Hallo Evil,

    Ne geht dann immer noch nich:( Das kann doch net sein

    Ja der code ist sicher nich gut, ist mein erster versuch mal nen Treeveiew via xaml zu füllen etc. du meisnt ich sollte IEnumerable mit OberserCollection tauschen, und INodifyChange für die TreeItems bzw. MyTreeNodes implementieren?



  • das waere schonmal ein guter ansatz ja (das ding heisst INotifyPropertyChanged)
    wird aber an dein problem nichts aendern
    denn du deklarierst den tree falsch

    du hast das HierarchicalDataTemplate in den resourcen, das ist quatsch, das gehoert ins itemtemplate

    dein:

    <TreeView Name="Scene3DViwer" ItemsSource="{Binding Root}">
        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type module3D:MyTreeNode}" ItemsSource="{Binding Children}">
                <TextBlock Text="{Binding Name}" Margin="5" />
            </HierarchicalDataTemplate>
        </TreeView.Resources>
    </TreeView>
    

    wird zu:

    <TreeView Name="Scene3DViwer" ItemsSource="{Binding Root}">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Children}"> <!-- DataType muesste egal sein -->
                <TextBlock Text="{Binding Name}" Margin="5" />
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
    


  • Ohje, es klappt immer noch nich.. glaub du bekomsmt auch gleich die Kriese Mr.Evil mit mir;)



  • was heisst "klappt nicht"
    wenn du mitn debugger startest (F5) kannst du das output einblenden (view->output)(ist in visual studio per default ausgeblendet) und da siehst du meldungen wenn ein binding fehl schlug
    hast du das neue Tree auf mit dem DataContext vorschlag probiert ? maybe wird "Root" nicht gefunden (aber auch das siehst du dann im output)
    kannst auch mal dein neuen code zeigen nach der aenderung



  • Das Ausgabe Fenster hab ich drin.. ich sehe kein exception oder sonstige hinweise das ein Binding fehlschlug... ja ich hab dein Vorschlag mit dem "DateContext" auch schon proviert.. hab acuh das DataType Attribute mal gesetzt etc. aber nix .. einfach ein weise Fenster!

    Wenn ich

    this.Scene3Dviwer.DataContext= Root;
    

    mache und mir das Scene3Dviewr object anschau, sind 0 Items vorhanden.. oh man

    Das XAML ist jaschon ganz gut, aber zum debuggen ists zum kotzen



  • bedenke - wenn du
    this.Scene3Dviwer.DataContext= Root;
    machst - muss das ItemsSource nur auf binding stehen
    ItemsSource="{Binding}"

    am sonsten wie gesagt, zeig doch nochmal dein geaenderten code insgesammt (wie beim init post)



  • btw:

    setz mal breakpoints in die ctors - nicht das der zweite ctor erst spaeter aufgerufen wird und dadurch deine children ueberschrieben werden

    public MyTreeNode(string name,IEnumerable<MyTreeNode> child)
        :this(name)
    {
        _children = new List<MyTreeNode>(child);
    }
    public MyTreeNode(string name)
    {
        _children = new List<MyTreeNode>();
        Name = name;
    }
    

    was mir noch einfallen wuerde - pack mal das root element in eine liste
    also
    public ObservableCollection<MyTreeNode> Root { get; set; }
    Root.Add(new ...);



  • Das wars Problem:

    public ObservableCollection<MyTreeNode> Root { get; set; }
    Root.Add(new ...);

    ist aber blöd weil Root nun mal nur ein object ist.. aber der TreeView kann wohl mehrer Ursprünge haben;)

    Danke Mr. Evil;)



  • Da hab ich aber grad noch ne frage:)

    ich will nun in jedes TreeView item, da wo der Textblock is noch einen button rein machen.. der soll aber NUR dann angezeigt werden wenn der Knoten Selektiert ist.. d.h. es soll dann nur der button von einem Knoten im Tree angezeigt werden!



  • NullBockException schrieb:

    ist aber blöd weil Root nun mal nur ein object ist.. aber der TreeView kann wohl mehrer Ursprünge haben

    genau
    denn die ersten elemente im tree haben ja keinen parent mehr - entsprechend kommen die aus einer liste

    was selektionen im tree an geht - ich hatte da man irgendwo was gelesen wo ich auch experimente gestartet hatte - ich such mal



  • schau mal hier:
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-238330-and-postdays-is-0-and-postorder-is-asc-and-highlight-is-treeview-and-start-is-10.html

    das waere zb ne moeglichkeit:

    <Style TargetType="{x:Type TreeViewItem}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="TreeViewItem">
                    <StackPanel Margin="10,2,0,0">
                        <WrapPanel Name="item">
                            <ToggleButton Name="expander" IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsExpanded}" Content="+"/>
                            <Button Content="..." x:Name="button" Visibility="Collapsed" />
                            <TextBlock Text="{Binding Name}" />
                        </WrapPanel>
                        <Border>
                            <ItemsPresenter Name="children" Visibility="Collapsed" />
                        </Border>
                    </StackPanel>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter TargetName="button" Property="Visibility" Value="Visible"/>
                        </Trigger>
                        <Trigger Property="IsExpanded" Value="True">
                            <Setter TargetName="children" Property="Visibility" Value="Visible"/>
                        </Trigger>
                        <Trigger Property="HasItems" Value="False">
                            <Setter TargetName="expander" Property="Visibility" Value="Collapsed"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    den expander musst du dann leider wieder selber zeichnen - steht auch in dem thread



  • Ohhh.. das sieht ja höllisch komplizier aus, auch nach mehrmalligen anschaun hab ich da bischen verständis probleme!

    Wo muss ich den diesen xaml code jinkopieren? direkt in das TReeviewitem ? oder extra?



  • So ich habdas man nach reiner Intension eignebaut. und es funktioniert auch;) Aber ich würdes auch bischen verstehen wollen..

    <Style TargetType="{x:Type TreeViewItem}">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="TreeViewItem">
    ....
    
                  </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
    

    Dieser Teil hab ich so einigermaßen verstanden.. staddest da ich den inhalt u. ausehen einer Baumknotens direkt im "HierachicalDataTemplate" definiere, wird über den Style die inhalteigenschaft "Template" des TreeViewItems defniert bzw. überschrieben?? Aber den ganzen rest mit Wrappanel und so versteh ich net ganz!!



  • ueberschrieben ja
    normalerweise ist es so das lokale template definitionen gewichtiger sind als styles - nur durch das HierachicalDataTemplate hat man keine TreeViewItems wo man das template definieren koennte - drum ist der normale weg das man ein style definiert um die TreeViewItems zu manipulieren

    erst zur laufzeit wird das HierachicalDataTemplate aufgeloest zu richtigen TreeViewItems

    und durch ein globalen style fuer TreeViewItem beeinflusst man alle diese elemente in dem sichtbarkeits bereich des styles

    wenn man dann das Template definiert nimmt man dem TreeViewItem jegliches aussehen sodass man alles selber machen kann bzw muss

    wenn du zb im style nur ein textblock machst wirst du feststellen das es sich noch genauso verhaellt - zb bei doppelklick auf und zu klappen - aber man sieht keine expander mehr (die pfeile in vista oder die +/- in xp) wo man nodes auf und zu klappt

    da kommt dann das wrappanel ins spiel
    dem gibt man dann ein toggle button was diesen expander darstellt (man muss den toggle butten nur wieder komplett selber stylen)
    dann dein gewuenschten button daneben und dann ein textblock mit einem binding zu Name
    (man koennte auch ein horizontalen stackpanel nehmen, spielt keine rolle)

    im trigger werden diese elemente dann entsprechend beeinflusst



  • Wie kann man denn auf das WrapPanel vom C# Code aus Zugreifen?



  • gar nicht - sowas macht man nicht
    wozu auch, man hat doch schon alle items im code - die treeview zeigt nur an - darauf zu greifen ist doch schon sehr forms / mfc altbacken

    //typo



  • Ok aber wie kann man dann in einer treeview einen explorer realisieren, bei dem die folders ein image und die dateien (blätter) dann anstatt eines images ein eigenes steuerelement bekommen?



  • man erstellt sich ein
    Folder bzw File objekt (oder gemeinsam eines)
    diese haben die entsprechenden properties

    public class FileFolderItem
    {
        public BitmapImage Icon { get; set; }
        public string Name { get; set; }
        /* ... */
        public Collection<FileFolderItem> Childs { get; set; }
    }
    

    im code hatt man dann den Tree

    public class Bla
    {
        public Collection<FileFolderItem> Items { get; set; }
    }
    

    und in der xaml bindet man entsprechend

    <Style TargetType="{x:Type TreeViewItem}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="TreeViewItem">
                    <StackPanel Margin="10,2,0,0">
                        <WrapPanel Name="item">
                            <ToggleButton Name="expander" IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsExpanded}" Content="+"/>
                            <Image Source="{Binding Icon}" />
                            <TextBlock Text="{Binding Name}" />
                        </WrapPanel>
                        <Border>
                            <ItemsPresenter Name="children" Visibility="Collapsed" />
                        </Border>
                    </StackPanel>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsExpanded" Value="True">
                            <Setter TargetName="children" Property="Visibility" Value="Visible"/>
                        </Trigger>
                        <Trigger Property="HasItems" Value="False">
                            <Setter TargetName="expander" Property="Visibility" Value="Collapsed"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
    <TreeView ItemsSource="{Binding Items}">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Childs}" />
        </TreeView.ItemTemplate>
    </TreeView>
    

    // der code ist nur pseudo


Anmelden zum Antworten