WPF TreeView ...



  • Folgender Text funktioniert nicht.

    <TreeView ...
    <TreeView.ItemTemplate>
    <HierarchicalDataTemplate>
    <StackPanel>
    <Image Source="...Bild.bmp"/>
    <TextBlock Text="{ Binding Header }" />
    </StackPanel>
    </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
    <TreeViewItem Header="Eintrag 1">
    <TreeViewItem Header="Eintrag 1.1/>
    <TreeViewItem Header="Eintrag 1.2/>
    <TreeViewItem Header="Eintrag 1.3/>
    </TreeViewItem>
    <TreeViewItem Header="Eintrag 2">
    <TreeViewItem Header="Eintrag 2.1/>
    <TreeViewItem Header="Eintrag 2.2/>
    <TreeViewItem Header="Eintrag 2.3/>
    </TreeViewItem>
    </TreeView>

    Später sollte noch ein Trigger eingestzt werden, der beim Selektieren
    die Bitmap verändert. Dann bekomme ich es nicht hin, dass in der TreeView
    Linien angezeigt werden.



  • Und zwar sollte der XAML Code, eine TreeView mit kleinen Bildchen versehen.



  • ich vermute das ItemTemplate wird gar nicht benutzt da es nicht als binding gesetzt wird
    was du brauchst ist ein template fuer die TreeViewItems selber
    also erstell ein style was auf die items wirkt, und dort kannst du es im template machen

    oder du packst die items in einem richtigen baum - und setzt es per binding auf die liste

    zb:

    <TreeView x:Name="Tree" ItemsSource="{Binding}">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Path=Childs}">
                <StackPanel Orientation="Horizontal">
                    <Image Source="Bla" Margin="1" />
                    <TextBlock Text="{Binding Name}" VerticalAlignment="Center" Margin="3,0" />
                </StackPanel>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
    

    der baum ist einfach

    ObservableCollection<Item> _items = new ObservableCollection<Item>()
    Tree.DataContest = _items;
    class Item : INotifyPropertyChanged
    {
        public string Name {...
        public ObservableCollection<Item> Childs {...
    }
    

    im trigger kannst du das image dann auch umstellen

    evtl bietet es sich an den typen aus "Item" zu holen - bei einem datei baum koennte man so zwischen dateien und verzeichnissen unterschiedliche bilder anzeigen



  • Bitte entschuldige .. Da ich noch WPF-Anfänger bin, frage ich Dich, wie ich
    ein Template für die TreeViewItems selber bekomme. Könntest Du mir den
    entsprechenden XAML Code hinschreiben ?



  • kann ja mal eins aus dem kopf zurecht basteln #gg

    <Window.Resources>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TreeViewItem}">
                        <StackPanel>
                            <Image Source="XXXXXXX" Margin="1" x:Name="IconImage" />
                            <TextBlock Text="{Binding Header}" Margin="3,0" VerticalAlignment="Center" />
                        </StackPanel>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsExpanded" Value="False">
                                <Setter TargetName="IconImage" Property="Source" Value="XXXXXXX"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    <Window.Resources>
    
    <TreeView>
        <TreeViewItem Header="Bla" />
        <TreeViewItem Header="Fasel" />
        <TreeViewItem Header="Bla">
            <TreeViewItem Header="Fasel" />
        </TreeViewItem>
    </TreeView>
    

    meine bevorzugte methode waere aber ohne nen style:

    <TreeView x:Name="Tree" ItemsSource="{Binding}">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Path=Childs}">
                <StackPanel Orientation="Horizontal">
                    <Image Source="XXXXXXX" Margin="1" />
                    <TextBlock Text="{Binding Name}" VerticalAlignment="Center" Margin="3,0" />
                </StackPanel>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
    
    private ObservableCollection<Item> _items = new ObservableCollection<Item>();
    ...
    Tree.DataContext = _items;
    ...
    _items.Add(new Item("Bla"));
    _items.Add(new Item("Fasel"));
    Item blaItem = new Item("Bla");
    blaItem.Childs.Add(new Item("Fasel"));
    _items.Add(blaItem);
    
    internal class Item :INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public ObservableCollection<Item> Childs { get; set; }
        private string _name;
        public Item(string name)
        {
            Name = name;
            Childs = new ObservableCollection<Item>();
        }
    
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value:
                Changed("Name");
            }
        }
    
        public override string ToString()
        {
            return Name;
        }
    
        private void Changed(string property)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }
    

    //dazuedit: wollte nur noch anmerken, alles rein aus dem kopf ohne irgendwas zu testen - muss also nicht funktionieren



  • ist mir grad noch was eingefallen als ich grad eigene styles fuer n projekt gemacht hab
    was ich noch erwaehnen sollte
    wenn du das style so wie in meinem beispiel so schreibst das du das template definierst - dann ueberschreibst du damit auch das standardverhalten von markierungen usw
    um das zu umgehen musst du mit TemplateBinding es wiederherstellen

    zb bei einem ListBoxItem:

    <Style x:Key="ListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
    	<Setter Property="Background" Value="Transparent"/>
    	<Setter Property="HorizontalContentAlignment" Value="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
    	<Setter Property="VerticalContentAlignment" Value="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
    	<Setter Property="Padding" Value="2,0,0,0"/>
    	<Setter Property="Template">
    		<Setter.Value>
    			<ControlTemplate TargetType="{x:Type ListBoxItem}">
    				<Border SnapsToDevicePixels="true" x:Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
    					<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
    				</Border>
    				<ControlTemplate.Triggers>
    					<Trigger Property="IsSelected" Value="true">
    						<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
    						<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
    					</Trigger>
    					<MultiTrigger>
    						<MultiTrigger.Conditions>
    							<Condition Property="IsSelected" Value="true"/>
    							<Condition Property="Selector.IsSelectionActive" Value="false"/>
    						</MultiTrigger.Conditions>
    						<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
    						<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
    					</MultiTrigger>
    					<Trigger Property="IsEnabled" Value="false">
    						<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
    					</Trigger>
    				</ControlTemplate.Triggers>
    			</ControlTemplate>
    		</Setter.Value>
    	</Setter>
    </Style>
    

    // ist kopiert {o;

    wobei das dann wieder unterschiede zwischen vista und xp usw geben kann
    das ist wieder ein grund warum ich die methode ohne style bevorzuge {o;



  • Vielen, vielen Dank für Dich !!!



  • Entschuldige, dass ich nochmals nerve ...
    Das Beispiel mit dem TreeVieItem-Style ging nicht.
    Es erschien kein Baum, sondern nur zwei Bildchen.
    Zu Deiner bevorzugten Variante, ein paar Fragen:
    Du definierst eine Klasse namens Item. Wo bringst Du diese unter ?
    Bei denem XAML-Code verwendest Du mehrere Bindigs, die offensichtlich
    auf die Item-Klasse Bezug nehmen. Meine Frage ist die: woher weiss WPF,
    dass die Bindings-Quellen bei Item zu suchen sind ? Man könnte ja auch
    noch eine andere Klasse definieren, die ebenfalls eine Property "Name"
    hätte.



  • GeorgC++ schrieb:

    Entschuldige, dass ich nochmals nerve ...
    Das Beispiel mit dem TreeVieItem-Style ging nicht.
    Es erschien kein Baum, sondern nur zwei Bildchen.

    muesste man in dem fall dann noch etwas anpassen - evtl mit nem HierarchicalDataTemplate - aber egal

    GeorgC++ schrieb:

    Zu Deiner bevorzugten Variante, ein paar Fragen:
    Du definierst eine Klasse namens Item. Wo bringst Du diese unter ?

    wo es sinnvoll ist - es ist eine modelview klasse (daher das INotifyPropertyChanged) - im beispiel ist es nur "Item" - man sollte es schon ein sinnvollen namen geben und in einen sinnvollen namespace packen zur weiterverwendung in anderen klassen

    wenn du zb ein baum mit verzeichnissen und dateien machen moechtest waere es sinnvoll direkt das verzeichnis oder die datei in solch ein item zu verpacken - dann hast du dein baum womit du normal arbeiten kannst, und zudem kannst du den baum ohne etwas per hand zu machen direkt anzeigen lassen

    zb

    public class FileFolderItem : INotifyPropertyChanged ...
    public class FileItem : FileFolderItem, INotifyPropertyChanged ...
    public class FolderItem : FileFolderItem, INotifyPropertyChanged ...
    /* ... */
    private ObservableCollection<FileFolderItem> _items = new ObservableCollection<FileFolderItem>();
    Tree.DataContext = _items;
    /*
    hier kannst du mit deinem baum arbeiten und das tree bleibt immer aktuell
    der vorteil ist auch das das selected item im tree auch gleich eine datei oder ein verzeichnis ist
    womit du arbeiten kannst ohne das du manuell ein mapping her stellen musst
    */
    

    GeorgC++ schrieb:

    Bei denem XAML-Code verwendest Du mehrere Bindigs, die offensichtlich
    auf die Item-Klasse Bezug nehmen. Meine Frage ist die: woher weiss WPF,
    dass die Bindings-Quellen bei Item zu suchen sind ? Man könnte ja auch
    noch eine andere Klasse definieren, die ebenfalls eine Property "Name"
    hätte.

    ja koennte man
    die treeview weiss gar nichts - die weiss nur "zeige 'Name' und als child nimmst du 'Childs'"
    das das richtige genommen wird liegt daran da man das DataContext der liste auf die richtigen items zeigt
    ItemSource steht ja auf "{Binding}" das bedeutet er sucht ab sich selber oder bei nem parent bis zum root nach einer anzeigbaren liste welche auf ein DataContext eingestellt ist

    wenn man die selben items in verschiedenen listen oder sonstwas anzeigen moechte kann man auch das DataContext des fensters nehmen - das geht nur nicht wenn man verschiedene liste verwalten moechte (darum binde ich persoenlich immer direkt an die liste die anzeigen soll)
    DataContext ist also sozusagen die "Binding Quelle"



  • Das mit der ObservableCollection ging gut, die Bildchen werden angezeigt.
    Jetzt geht es noch darum, selektierte Eintrage mit einem anderen Bild zu ver-
    sehen. ( Der Georg ist schon lästig !!!? )
    Noch ein Paar abschliessende Fragen: Kann man dem Aussehen der TreeView Linien
    hinzufügen ? Und noch: Kann man auf die Elemente eines StackPanel zugreifen ?
    Wenn ja dann wie.



  • Noch was, Wie kann man den Text eines selektierten Eintrages berechnen ?



  • och passt schon - wenns laestig wird schliess ich einfach firefox #gg

    was das stackpanel angeht - da du kommst an die elemente - die stecken in der "Children" collection
    StackPanel.Chilren.Add("childitem");
    StackPanel.Chilren.Remove("childitem");
    StackPanel.Chilren.Clear();
    alles da

    die linien hinzufuegen wird schon n bissl komplizierter
    diese musst du manuell setzen
    dh im template die linien erstellen
    dazu musst du immer nur wissen ob es das erste oder das letzte element in der liste ist
    das geht evtl durch einen converter - muesste ich selber probieren - ich kann auf diese linien gern verzichten #gg

    was meinst du mit "Text berechnen"?



  • Die TreeView enthält Knoten mit einer Beschriftung, z.B. Eintrag1.
    Wenn ich einen Knoten auswähle, dann möchte ich diesen Text zurückgeben.
    z.B. ...OnSelectded(...) // oder so ...
    MessageBox.Show(Tree.Items.CurrentItem.ToString()); // oder so ...
    Das ging nicht !
    jetzt geht es noch um die Bildchen die erscheinen sollen wenn ein
    Knoten expandiert wird. Nachdem ich die Variante, die Du bevorzugst
    eingebaut habe, ging folgender XAML Code nicht :
    ...
    <Trigger Property="IsExpanded" Value="True">
    <Setter .../>
    </Trigger>
    ...
    Folgendes ging:
    ...
    <Trigger Property="Control.IsMouseOver" Value="True">
    <Setter .../>
    </Trigger>
    ...
    Dann erschien ein neues Bildchen ( das bei Selektierung ) sobald sich
    die Maus über dem Knoten befand. Und nicht bei einem expandiertem Knoten.



  • was bedeuted "das ging nicht" - was ist passiert ?

    hast du ein objekt gebunden wie in meinem beispiel oder nur n string?
    Tree.SelectedItem beinhaltet das selektierte item
    aber nicht den text oder das bild sondern das objekt an sich
    du hast ja das gebundene objekt - daran kommst du indem du sagst

    MessageBox.Show((Tree.SelectedItem as Item).Name);
    bzw
    MessageBox.Show((Tree.SelectedItem as Item).ToString());

    ----

    was ist mit:
    <Trigger Property="TreeViewItem.IsExpanded" Value="True">
    <Setter .../>
    </Trigger>

    geht das?
    "Control" hat kein "IsExpanded" property - aber das TreeViewItem



  • <Trigger Property="TreeViewItem.IsExpanded" Value="True">
    <Setter .../>
    </Trigger>

    tat nichts.

    <EventTrigger RoutedEvent="Expanded">
    <Setter .../>
    </EventTrigger>

    Hier kam ein Laufzeit-Fehler. Irgendwas konnte nicht konvertiert werden.



  • hmm - grad mal selber wirklich probiert
    anscheind kennt das itemtemplate gar kein treeviewitem an der stelle und kanns entsprechend nicht antriggern

    das einzigste was mir nu einfaellt waere doch ein eigenes style wo du dann aber leider auch alles selber anzeigen lassen musst
    zb den expander selber zeichnen

    hier mal ein kleines beispiel - da wird das text auf "Is Expanded" geaendert sofern das eintritt - du erkennst das dann schon

    <Window.Resources>
    		<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="+"/>
    								<TextBlock Text="{Binding Name}" x:Name="textfield" />
    							</WrapPanel>
    							<Border>
    								<ItemsPresenter Name="children" Visibility="Collapsed" />
    							</Border>
    						</StackPanel>
    						<ControlTemplate.Triggers>
    							<Trigger Property="IsSelected" Value="True">
    								<Setter TargetName="item" Property="Background" Value="LightBlue"/>
    							</Trigger>
    							<Trigger Property="IsExpanded" Value="True">
    								<Setter TargetName="children" Property="Visibility" Value="Visible"/>
    								<Setter TargetName="textfield" Property="Text" Value="Is Expanded" />
    							</Trigger>
    							<Trigger Property="HasItems" Value="False">
    								<Setter TargetName="expander" Property="Visibility" Value="Collapsed"/>
    							</Trigger>
    						</ControlTemplate.Triggers>
    					</ControlTemplate>
    				</Setter.Value>
    			</Setter>
    		</Style>
    	</Window.Resources>
    	<TreeView ItemsSource="{Binding}">
    		<TreeView.ItemTemplate>
    			<HierarchicalDataTemplate ItemsSource="{Binding Path=Childs}" />
    		</TreeView.ItemTemplate>
    	</TreeView>
    

    hab hier noch die styles fuer des toggle button fuer xp und vista rumliegen:

    <!-- XP -->
    	<Style x:Key="ExpandCollapseToggleStyle" TargetType="{x:Type ToggleButton}">
    		<Setter Property="Focusable" Value="False"/>
    		<Setter Property="Width" Value="19"/>
    		<Setter Property="Height" Value="13"/>
    		<Setter Property="Template">
    			<Setter.Value>
    				<ControlTemplate TargetType="{x:Type ToggleButton}">
    					<Border Width="19" Height="13" Background="Transparent">
    						<Border Width="9" Height="9" BorderThickness="1" BorderBrush="#FF7898B5" CornerRadius="1" SnapsToDevicePixels="true">
    							<Border.Background>
    								<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
    									<LinearGradientBrush.GradientStops>
    										<GradientStop Color="White" Offset=".2"/>
    										<GradientStop Color="#FFC0B7A6" Offset="1"/>
    									</LinearGradientBrush.GradientStops>
    								</LinearGradientBrush>
    							</Border.Background>
    							<Path x:Name="ExpandPath" Margin="1,1,1,1" Fill="Black" Data="M 0 2 L 0 3 L 2 3 L 2 5 L 3 5 L 3 3 L 5 3 L 5 2 L 3 2 L 3 0 L 2 0 L 2 2 Z"/>
    						</Border>
    					</Border>
    					<ControlTemplate.Triggers>
    						<Trigger Property="IsChecked" Value="True">
    							<Setter Property="Data" TargetName="ExpandPath" Value="M 0 2 L 0 3 L 5 3 L 5 2 Z"/>
    						</Trigger>
    					</ControlTemplate.Triggers>
    				</ControlTemplate>
    			</Setter.Value>
    		</Setter>
    	</Style>
    
    <!-- Vista -->
    	<Style x:Key="ExpandCollapseToggleStyle" TargetType="{x:Type ToggleButton}">
    		<Setter Property="Focusable" Value="False"/>
    		<Setter Property="Width" Value="16"/>
    		<Setter Property="Height" Value="16"/>
    		<Setter Property="Template">
    			<Setter.Value>
    				<ControlTemplate TargetType="{x:Type ToggleButton}">
    					<Border Width="16" Height="16" Background="Transparent" Padding="5,5,5,5">
    						<Path x:Name="ExpandPath" Fill="Transparent" Stroke="#FF989898" Data="{StaticResource TreeArrow}">
    							<Path.RenderTransform>
    								<RotateTransform Angle="135" CenterX="3" CenterY="3"/>
    							</Path.RenderTransform>
    						</Path>
    					</Border>
    					<ControlTemplate.Triggers>
    						<Trigger Property="IsMouseOver" Value="True">
    							<Setter Property="Stroke" TargetName="ExpandPath" Value="#FF1BBBFA"/>
    							<Setter Property="Fill" TargetName="ExpandPath" Value="Transparent"/>
    						</Trigger>
    						<Trigger Property="IsChecked" Value="True">
    							<Setter Property="RenderTransform" TargetName="ExpandPath">
    								<Setter.Value>
    									<RotateTransform Angle="180" CenterX="3" CenterY="3"/>
    								</Setter.Value>
    							</Setter>
    							<Setter Property="Fill" TargetName="ExpandPath" Value="#FF595959"/>
    							<Setter Property="Stroke" TargetName="ExpandPath" Value="#FF262626"/>
    						</Trigger>
    					</ControlTemplate.Triggers>
    				</ControlTemplate>
    			</Setter.Value>
    		</Setter>
    	</Style>
    

    kannst dir das komplette style auch von blend automatisch erstellen lassen


Anmelden zum Antworten