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 machenoder 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 wiederherstellenzb 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 istwenn 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 dadie 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 #ggwas 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 sagstMessageBox.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 antriggerndas 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 zeichnenhier 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