Laden...

TreeView - SelectedItem designen

Erstellt von DiscMaster vor 14 Jahren Letzter Beitrag vor 13 Jahren 9.275 Views
DiscMaster Themenstarter:in
316 Beiträge seit 2006
vor 14 Jahren
TreeView - SelectedItem designen

Sers zusammen!

Ich bin auf der Suche nach einer Möglichkeit die SelectedItems eines TreeViews zu bearbeiten. Was ich mein ist, wenn ich ein TreeViewItem anklicke, wird das ganz billig blau hinterlegt. Sieht einfach nicht besonders toll aus (zumindest unter WinXP). Mich würde es jetzt interessieren ob und wie man das Aussehen der Selection bearbeiten kann.

Google hat mir nicht viel weiter geholfen. Allerdings habe ich ein Stückchen Markup gefunden, das vieleicht der richtige Ansatz ist, aber ich weiß damit nicht recht umzugehen...


<VisualStateManager.VisualStateGroups>
<VisualState x:Name="Selected"/>
</VisualStateManager.VisualStateGroups>

Ich weiß allerdings nich wie ich damit weitermachen muss (wenn überhaupt...)

Danke, DiscMaster.

"Flache Hierarchien schaffen! Das muss konkret nicht unbedingt etwas bedeuten, kommt aber immer sehr gut an."
Bernd Stromberg

E
92 Beiträge seit 2008
vor 14 Jahren

Hallo discmaster!

Wo kriegst du denn die Items Deiner treeView her?

Mir schwebt da für die Lösung Deines Problems sowas vor: (Die Items kämen in diesem Fall aus der Bindung an die "eineCollection" aus dem datacontext, daher die Frage oben)


 <TreeView  ItemsSource="{Binding EineCollection}" >
        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <Setter Property="FontWeight" Value="Normal" />
                <Style.Triggers>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter Property="FontWeight" Value="Bold" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </TreeView.ItemContainerStyle>
    </TreeView>

Zur demo wird hier einfach der FontWeight geändert wenn IsSelected sich ändert.. Und dafür sollten die elemente der Collection in etwa folgendes können:



        public bool IsSelected
        {
            get { return _isSelected; }
            set
            {
                if (value != _isSelected)
                {
                    _isSelected = value;
                    this.OnPropertyChanged("IsSelected");
                }
            }
        }

DiscMaster Themenstarter:in
316 Beiträge seit 2006
vor 14 Jahren

Die Items kommen aus einer ObservableCollection.
Jetz hab ich allerdings nich nur die TreeViewItems ansich, sondern ich hab das bisschen Layoutet mit dem HierarchicalDataTemplate. Wenn ich das was du mir eben geschrieben hast und beim Setter noch den TargetName angebe, bekomme ich ne Exception, das das Style-Setter kein TargetName haben dürfen....

Hier mal mein TreeView:

<TreeView Margin="4,4,2,4" Name="trvPlaylistConfig" >
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
                    <StackPanel Margin="2,1,2,2" Orientation="Horizontal">
                        <CheckBox Margin="0,0,2,0" IsChecked="{Binding IsChecked}" VerticalAlignment="Center"/>
                        <Image Source="{Binding Type, Converter={StaticResource NodeTypeConverter}}" VerticalAlignment="Center"/>
                        <TextBlock Margin="3,0,0,0" Text="{Binding Text}" VerticalAlignment="Center"/>
                        <Rectangle x:Name="itemBackground" Fill="Red" Margin="1,1,1,1"></Rectangle>
                    </StackPanel>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>

"Flache Hierarchien schaffen! Das muss konkret nicht unbedingt etwas bedeuten, kommt aber immer sehr gut an."
Bernd Stromberg

E
92 Beiträge seit 2008
vor 14 Jahren

Versteh nicht was du meinst..
Was hast du mit TargetName vor? Brauchst Du nicht soweit ich das sehe...

DiscMaster Themenstarter:in
316 Beiträge seit 2006
vor 14 Jahren

naja, ich will ja nich irgendwas ändern sondern wenn ein element ausgewählt wird, soll das schön hinterlegt werden, also nich so hässlich blau ohne rahmen sondern halt was weiß ich mit einem abgerundeten rechteck (Rectangle: itemBackground, letzte zeile im StackPanel im Template). Und ich dachte, wenn jetzt eben das item ausgewählt wird, dann soll das rectangle z.B. grün werden

"Flache Hierarchien schaffen! Das muss konkret nicht unbedingt etwas bedeuten, kommt aber immer sehr gut an."
Bernd Stromberg

E
92 Beiträge seit 2008
vor 14 Jahren

Hmm.... soweit hatte ich das schon verstanden 😉

genau für den Zweck gibts m.E. Styles und Trigger...

Du erstellst für deine TrreViewItems einen Style und setzt darin beliebige Properties der betroffenen Items. (Ob es nun runde Ecken, grüne Farbe oder wie im Bsp. der Text ist...)

Da aber eben abhängig von einer anderen eigenschaft (nämlich IsSelcted) gesetzt werden soll, verwendest du einen Trigger der genau diese eine Eigenschaft "Überwacht"

Und jetzt sind wir an dem Punkt an dem mir nicht mehr klar ist wo genau dein Problem liegt. 🤔

Was genau ist jetzt unklar?

DiscMaster Themenstarter:in
316 Beiträge seit 2006
vor 14 Jahren

na jetzt mus ich doch irgendwie klar machen was er da verändern soll? deswegen eben der TargetName, weil das Property="Fill" könnte ja genauso zum TextBlock gehören... oder hab ich da was nicht richtig verstanden?

"Flache Hierarchien schaffen! Das muss konkret nicht unbedingt etwas bedeuten, kommt aber immer sehr gut an."
Bernd Stromberg

E
92 Beiträge seit 2008
vor 14 Jahren

Ahhhhh!

Ok, jetzt weiß ich was du meinst! Klar, hast recht, das hatte ICH nicht verstanden.

Also spontan würde ich vorschlagen die BackGround-Farbe als Property des Items zu implementieren und den Hintergrund deines DataTemplate daran zu binden.

Die Property des Items kannst du dann wie oben beschrieben per Style setzen...

Aber wenn ich zu Hause bin überlege ich gern auch noch mal, muss ja so gehen.

DiscMaster Themenstarter:in
316 Beiträge seit 2006
vor 14 Jahren

Yah, sowas in der richtung hatte ich mir vorhin dann auch überlegt, aber das wäre ein workaround das mit sicherheit auch schlichter zu lösen geht...

"Flache Hierarchien schaffen! Das muss konkret nicht unbedingt etwas bedeuten, kommt aber immer sehr gut an."
Bernd Stromberg

E
92 Beiträge seit 2008
vor 14 Jahren

Guten Abend!
Hab wie versprochen noch ein bisschen nachgedacht und das jetzt einfach mal ausprobiert.
Das Problem ist dass der Setter im STYLE kein TargetName erlaubt. Sobald du den entsprechenden Setter in das Template verschiebst läufts.

Hier mal mein Code:


namespace WpfApplication2
{
    public partial class MainWindow : Window
    {
        public ObservableCollection<TrvDing> SubItems { get; set; }

        public MainWindow()
        {
            SubItems = new ObservableCollection<TrvDing>();
            InitializeComponent();
            trvPlaylistConfig.DataContext = this;

            SubItems.Add(new TrvDing());
            SubItems.Add(new TrvDing());
            SubItems.Add(new TrvDing());
        }
    }

    public class TrvDing :  TreeViewItem
    {
        public string Text
        {
            get { return "Ein TestText"; }
        }
    }
}

Das TrvDing soll (falls es dieses Hinweises noch Bedarf 😁 ) dein Item sein...

Und mit dem XAML ist dann alles wie es soll: (Ich habe beim Template mal eine wunderschöne Hintergrundfarbe gesetzt, damit das selektieren des items "einfacher" ist und man sieht wo welches Item anfängt...)


<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication2"
        Title="MainWindow" Height="350" Width="525">
    
    <Window.Resources>
        <Style x:Key="myExampleStyle" TargetType="local:TrvDing">
             <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="local:TrvDing">
                        <StackPanel Margin="2,1,2,2" Orientation="Horizontal" Background="Aquamarine">
                            <CheckBox Margin="0,0,2,0" IsChecked="{Binding IsChecked}" VerticalAlignment="Center"/>
                            <TextBlock Margin="3,0,0,0" Text="{Binding Text}" VerticalAlignment="Center"/>
                            <Rectangle x:Name="itemBackground" Fill="Red" Margin="1,1,1,1" Width="80" Height="20"></Rectangle>
                        </StackPanel>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter TargetName="itemBackground" Property="Fill" Value="Green"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    
    
    <Grid>
        <TreeView Margin="4,4,2,4" Name="trvPlaylistConfig" ItemsSource="{Binding Path=SubItems}" ItemContainerStyle="{StaticResource myExampleStyle}" />
    </Grid>
</Window>

Durch das Ableiten vom TreeViewItem kannst Du die Items (insbesondere die Hintergrundfarbe) jetzt natürlich auch ohne das ganze drumherum wie gehabt per Style und Trigger setzen! Ich habe die Ableitung nur gemacht um die Propertie "Template " schnell zur Verfügung zu haben.. das würde man dann im Ernstfall wohl anders machen...

DiscMaster Themenstarter:in
316 Beiträge seit 2006
vor 14 Jahren

Erstmal herzlichen Dank für dein Bemühen!
Wenn ich dein Beispiel übertrage habe ich schonmal einiges erreicht. Allerdings bekomme ich jetzt keine Hierarchie mehr hin... Zweitens: Wenn ich von TreeViewItem ableite hab ich weder Image noch Text im Item - nur noch das Rectangle, ist also auch nicht ganz Ziel der Übung...

Durch das Ableiten vom TreeViewItem kannst Du die Items (insbesondere die Hintergrundfarbe) jetzt natürlich auch ohne das ganze drumherum wie gehabt per Style und Trigger setzen! Ich habe die Ableitung nur gemacht um die Propertie "Template " schnell zur Verfügung zu haben.. das würde man dann im Ernstfall wohl anders machen...

Wie implementiert man denn den Extremfall?

"Flache Hierarchien schaffen! Das muss konkret nicht unbedingt etwas bedeuten, kommt aber immer sehr gut an."
Bernd Stromberg

E
92 Beiträge seit 2008
vor 14 Jahren

hmmm hat noch keiner geantwortet... mist.. Na dann schau ich mir das nochmal an... X(

E
92 Beiträge seit 2008
vor 14 Jahren

Na dann mal auf ein neues, vielleicht gefällt Dir diesmal mein Lösungsversuch....


namespace WpfApplication2
{
    public partial class MainWindow : Window
    {
        public ObservableCollection<TrvDing> SubItems { get; set; }
        Random _rnd = new Random();

        public MainWindow()
        {
            SubItems = new ObservableCollection<TrvDing>();
            InitializeComponent();
            trvPlaylistConfig.DataContext = this.SubItems;
            fillTree(SubItems, _rnd.Next(6) + 1);
        }
      
      
        public void fillTree(ObservableCollection<TrvDing> toFill, int noOfChild)
        {
            for (int i = 0; i < noOfChild; i++)
            {
                TrvDing temp1 = new TrvDing();
                fillTree(temp1.SubItems, _rnd.Next(noOfChild-1));
                toFill.Add(temp1);
            }
        }
    }

  

    public class TrvDing : INotifyPropertyChanged
    {
        public TrvDing()
        {
            SubItems = new ObservableCollection<TrvDing>();
        }

        private bool _isSelected;
        public bool IsChecked { get; set; }
        public string Text
        {
            get { return "Ein TestText"; }
        }

        public ObservableCollection<TrvDing> SubItems { get; set; }
    
       
        public bool IsSelected
        {
            get { return _isSelected; }
            set
            {
                if (value != _isSelected)
                {
                    _isSelected = value;
                    this.OnPropertyChanged("IsSelected");
                }
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion // INotifyPropertyChanged Members
    }
}

und:


<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication2"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>   
            <HierarchicalDataTemplate DataType="{x:Type local:TrvDing}" ItemsSource="{Binding SubItems}">
                <StackPanel Margin="2,1,2,2" Orientation="Horizontal" Background="Aquamarine">
                    <CheckBox Margin="0,0,2,0" IsChecked="{Binding IsChecked}" VerticalAlignment="Center"/>
                    <TextBlock Margin="3,0,0,0" Text="{Binding Text}" VerticalAlignment="Center"/>
                    <Rectangle x:Name="itemBackground" Fill="Red" Margin="1,1,1,1" Width="80" Height="20"></Rectangle>
                </StackPanel>
                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding Path=IsSelected}" Value="True">
                        <Setter TargetName="itemBackground" Property="Fill" Value="Green"/>
                    </DataTrigger>
                </DataTemplate.Triggers>
            </HierarchicalDataTemplate>
    </Window.Resources>


    <Grid>
        <TreeView Name="trvPlaylistConfig" ItemsSource="{Binding}" >
            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}" >
                    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                </Style>
            </TreeView.ItemContainerStyle>
        </TreeView>
    </Grid>
</Window>

Das DataTemplate bzw dessen Setter kann es nämlich auch! (mit dem TargetName..)

Sorry das jetzt wieder alles hin und her gerutscht ist (im Vergleich zu Code vom letzten mal) aber ich musste auch ganzschön probieren bis ichs hatte...

DiscMaster Themenstarter:in
316 Beiträge seit 2006
vor 14 Jahren

Yipee, langsam nimmt das alles Gestalt an, und ich hab gerade einiges dazugelernt... ABER: Die TreeViewItems sind nach wie vor blau hinterlegt wenn ich sie auswähle........ 😦

"Flache Hierarchien schaffen! Das muss konkret nicht unbedingt etwas bedeuten, kommt aber immer sehr gut an."
Bernd Stromberg

U
1.578 Beiträge seit 2009
vor 14 Jahren

Dafür musst im ItemContainerStyle das Template überschreiben.

DiscMaster Themenstarter:in
316 Beiträge seit 2006
vor 14 Jahren

äh.... wieder mit trigger und so? (wenn nich, haste ma n codebeispiel?)

"Flache Hierarchien schaffen! Das muss konkret nicht unbedingt etwas bedeuten, kommt aber immer sehr gut an."
Bernd Stromberg

DiscMaster Themenstarter:in
316 Beiträge seit 2006
vor 13 Jahren

Dafür musst im ItemContainerStyle das Template überschreiben.

Das Template im ItemContainerStyle is Rille.
Also, ich bin in der Zwischenzeit öfters auf das Problem gestoßen, und dachte ich poste einfach mal des Rätsels Lösung:


<TreeView [....]>
  <TreeView.Resources>
    <!-- für den Hintergrund -->
    <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
    <!-- für den Text -->
    <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
  </TreeView.Resources>
</TreeView>

"Flache Hierarchien schaffen! Das muss konkret nicht unbedingt etwas bedeuten, kommt aber immer sehr gut an."
Bernd Stromberg

582 Beiträge seit 2008
vor 13 Jahren

Oje. Ich habe mir jetzt NICHT den ganzen Text durchgelesen, aber beim überfliegen habe ich schon gesehen, das es ein wenig zu viel des guten ist 😉.

Wenn es nur um die "Schattierung" bei der Auswahl geht, hast du selbstverständlich recht.

Aber es geht auch anders. Hier hat man völlige kontrolle über die Darstellung.

Hier eine MIschung aus veröffentlichem MS Code und eigenem


        <Style x:Key="ExpandCollapseToggleStyle" TargetType="ToggleButton">
            <Setter Property="Focusable" Value="False"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ToggleButton">
                        <Grid Width="15" Height="13" Background="Transparent">
                            <Path x:Name="ExpandPath" HorizontalAlignment="Left" VerticalAlignment="Center" 
                                  Margin="1,1,1,1" Fill="{StaticResource GlyphBrush}" Data="M 4 0 L 8 4 L 4 8 Z">
                                
                            </Path>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsChecked" Value="True">
                                <Setter Property="Data" TargetName="ExpandPath" Value="M 0 4 L 8 4 L 4 8 Z"/>
                            </Trigger>
                            <Trigger  Property="ToggleButton.IsMouseOver" Value="True">
                                <Setter TargetName="ExpandPath"  Property="Fill" Value="Gold" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="TreeViewItemFocusVisual">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Border>
                            <Rectangle Margin="0,0,0,0" StrokeThickness="5" Stroke="Black" StrokeDashArray="1 2" Opacity="0"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="{x:Type TreeViewItem}" TargetType="{x:Type TreeViewItem}"> 
            <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="1,0,0,0"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TreeViewItem}">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition MinWidth="19" Width="Auto"/>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>
                            <ToggleButton x:Name="Expander" Style="{StaticResource ExpandCollapseToggleStyle}" 
                                          IsChecked="{Binding Path=IsExpanded,RelativeSource={RelativeSource TemplatedParent}}"
                                          ClickMode="Press"/>
                            <Border Name="Bd" Grid.Column="1" Background="{TemplateBinding Background}" CornerRadius="5"
                                    BorderBrush="{StaticResource SolidColorDarkBrown}" BorderThickness="{TemplateBinding BorderThickness}"
                                    Padding="5,0">
                                <TextBlock x:Name="PART_Header" Text="{Binding Path=Header}" />
                            </Border>
                            <ItemsPresenter x:Name="ItemsHost" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsExpanded"
                                     Value="false">
                                <Setter TargetName="ItemsHost"
                                        Property="Visibility"
                                        Value="Collapsed"/>
                            </Trigger>
                            <Trigger Property="HasItems" Value="false">
                                <Setter TargetName="Expander" Property="Visibility"
                                        Value="Hidden"/>
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="HasHeader"
                                               Value="false"/>
                                    <Condition Property="Width"
                                               Value="Auto"/>
                                </MultiTrigger.Conditions>
                                <Setter TargetName="PART_Header" Property="MinWidth" Value="75"/>
                            </MultiTrigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="HasHeader" Value="false"/>
                                    <Condition Property="Height" Value="Auto"/>
                                </MultiTrigger.Conditions>
                                <Setter TargetName="PART_Header" Property="MinHeight" Value="19"/>
                            </MultiTrigger>
                            <Trigger Property="IsSelected" Value="true">
                                <Setter TargetName="Bd" Property="Background" Value="#8f6b5b"/>
                                <Setter Property="Foreground" Value="Gold"/>
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsSelected" Value="true"/>
                                    <Condition Property="IsSelectionActive" Value="false"/>
                                </MultiTrigger.Conditions>
                                <Setter TargetName="Bd" Property="Background"
                                        Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                                <Setter TargetName="PART_Header" Property="Foreground" Value="Gold"/>
                            </MultiTrigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="true">
                                <Setter TargetName="Bd" Property="Background" Value="#8f6b5b"/>
                                <Setter Property="Foreground" Value="Gold"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

 <HierarchicalDataTemplate DataType="{x:Type instanz:HierarchicalItem}"
                                  ItemsSource="{Binding Path=Children}"
                                  >
                <StackPanel>
                    <TextBlock Name="PART_Text" Foreground="Black" Text="{Binding Path=Header}"  FontFamily="Arial"/>
                </StackPanel>
            <HierarchicalDataTemplate.Triggers>
                <Trigger Property="IsMouseOver" Value="true">
                    <Setter Property="Effect">
                        <Setter.Value>
                            <DropShadowEffect 
                                        ShadowDepth="0" 
                                        Color="Gold" 
                                        BlurRadius="20" 
                                        Opacity=".75"
                                        RenderingBias="Quality" />
                        </Setter.Value>
                    </Setter>
                    <Setter TargetName="PART_Text" Property="Foreground" Value="Black" />
                </Trigger>
               
            </HierarchicalDataTemplate.Triggers>
        </HierarchicalDataTemplate>

Das HierarchicalDataTemplate stellt letztendlich dann auch dein Element dar, welches du gebunden hast.

Ich hoffe das hilft 😃.

Gruß dat Tala