Change OnClick label color WPF - c#

Im coming from a C# winforms background and I would normally do all this in code.
I have several Labels that I'm using as a menu.
When the mouse hovers over them the text changes color by:
<Page.Resources>
<SolidColorBrush x:Key="normalColor" Color="White" />
<SolidColorBrush x:Key="mouseOverColor" Color="Gold" />
<Style TargetType="Label" x:Key="menuItemStyle">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Foreground" Value="{StaticResource normalColor}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="{StaticResource mouseOverColor}"/>
</Trigger>
</Style.Triggers>
</Style>
</Page.Resources>
<Label x:Name="Label_Video1" Style="{StaticResource menuItemStyle}" Content="1.Video 1." FontSize="16" HorizontalAlignment="Left" Margin="25,74,0,0" VerticalAlignment="Top" MouseLeftButtonDown="Label_Video1_MouseLeftButtonDown" />
<Label x:Name="Label_Video2" Style="{StaticResource menuItemStyle}" Content="2. Video 2." FontSize="16" HorizontalAlignment="Left" Margin="25,105,0,0" VerticalAlignment="Top" MouseDown="Label_Video2_MouseDown"/>
When the user clicks a label I want it to stay a certin color (In this case Gold) and all the others to stay their normal colour. So if a label has been previously clicked and I click another label it will go from gold to white etc

When using WPF, you have to think slightly differently. We know that the Label control doesn't know when it has been clicked and it especially doesn't know when another Label element has been clicked... but some controls do. Thinking about it... a RadioButton has exactly this behaviour. Now this is where WPF really shines.
We can use RadioButtons and by providing them with a new ControlTemplate, we can make them look like plain text:
<Grid Background="Black">
<Grid.Resources>
<Style TargetType="{x:Type RadioButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<TextBlock Text="{TemplateBinding Content}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource AncestorType={
x:Type RadioButton}}}" Value="True">
<Setter Property="Foreground" Value="Gold" />
</DataTrigger>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource
AncestorType={x:Type RadioButton}}}" Value="True">
<Setter Property="Foreground" Value="Gold" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<StackPanel Margin="5" Background="{x:Null}">
<RadioButton Content="1.Video 1." />
<RadioButton Content="2.Video 2." />
<RadioButton Content="3.Video 3." />
</StackPanel>
</Grid>
If you don't want the RadioButtons to be all together in a StackPanel, then you can use give them the same GroupName property value to ensure that they still operate as one group.

Related

How to trigger parent element using child?

What I am trying to do is when my mouse got over a button inside a border that border background color must change.
<Border Name="HistoryBorder"
Grid.Column="0"
Height="30"
VerticalAlignment="Top"
Margin="0,0,326.8,0"
Background="#FF323030"
CornerRadius="0,0,15,0"
MouseDown="draging_bar">
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="75"
Name="HistoryButton"
Height="30"
Margin="0"
FontFamily="/Fonts/#Minecraft"
Foreground="#FFB6B002" >
<Button.Content>
History
</Button.Content>
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=HistoryButton, Path=IsMouseOver}" Value="True">
<DataTrigger.Setters>
<Setter Property="Background" Value="Green" />
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Border>
As you can see border is parent and button is a child in it. When I try to use targetname it is not working. Is something like that even possible ?
As far as I understand, setting Style for Border to be bind with it's child's IsMouseOver can be one of the ways.
<Border Name="HistoryBorder"
Grid.Column="0"
Height="30"
VerticalAlignment="Top"
Margin="0,0,326.8,0"
CornerRadius="0,0,15,0"
>
<!-- You have to put Background color to Style. Or priority makes it cannot be overrided from the Setter of Style -->
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Background" Value="#FF323030"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=HistoryButton, Path=IsMouseOver}" Value="True">
<DataTrigger.Setters>
<Setter Property="Background" Value="Black"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
...

Listbox Item selection

I have a WPF project (C#, Visual Studio 2010, MVVM).
In this project I have a ListBox. When I select the items I am getting a blue box around my objects and I'm not sure where said box comes from. Obviously it's something to do with selection, but I'm not sure what to alter to get it to disappear.
The Listbox is here:
<ListBox ItemsSource="{Binding ChatNodeListViewModel.ChatNodeVMs, Source={StaticResource Locator}}" Background="Transparent" Name="LbNodes" SelectedItem="{Binding ChatNodeListViewModel.SelectedNode, Source={StaticResource Locator}}" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas HorizontalAlignment="Left" VerticalAlignment="Top" Width="2000" Height="1600"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Canvas.Left" Value="{Binding XCoord}"/>
<Setter Property="Canvas.Top" Value="{Binding YCoord}"/>
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="lb_PreviewMouseLeftButtonDown" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Thumb Name="myThumb" Template="{StaticResource NodeVisualTemplate}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="DragDelta">
<cmd:EventToCommand Command="{Binding ChatNodeListViewModel.DragDeltaCommand, Source={StaticResource Locator}}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Thumb>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding NodeVisualMode}" Value="0">
<Setter TargetName="myThumb" Property="Template" Value="{StaticResource NodeVisualTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding NodeVisualMode}" Value="1">
<Setter TargetName="myThumb" Property="Template" Value="{StaticResource NodeTemplateTest}" />
</DataTrigger>
<DataTrigger Binding="{Binding NodeVisualMode}" Value="2">
<Setter TargetName="myThumb" Property="Template" Value="{StaticResource NodeTemplateTest2}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
There is a main ControlTemplate which can be switched out for others (the main one being NodeVisualTemplate). But since this issue seems to happen on all of them, I'm inclined to believe it's further up the tree.
Still, I did do some testing for it, and will show it here:
<ControlTemplate x:Key="NodeVisualTemplate">
<Grid>
<Border x:Name="_Border" Padding="1" SnapsToDevicePixels="true" BorderThickness="3" Margin="2" CornerRadius="5,5,5,5" BorderBrush="SteelBlue" Visibility="Visible">
</Border>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}" Value="true">
<Setter TargetName="_Border" Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="0" Color="Black" Opacity="1" BlurRadius="20" />
</Setter.Value>
</Setter>
<Setter TargetName="_Border" Property="Background" Value="Transparent"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Within the border at the top of this template are some text boxes and stuff, but I don't suspect them as being part of the affair. The point is that I've changed the background of this control template. It is 'transparent' in the XAML above, but I've changed it to red and other colours to test. It doesn't get rid of the blue square that surrounds the ListBoxItem.
Here is an image of the horrible blue background I'm getting:
Can I get rid of it? Honestly I'm really at a loss.
Thanks.
Edit: I should mention something else I tried that is rather important here. I went up a layer into the ListBox.ItemContainerStyle part above and just under this line:
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="lb_PreviewMouseLeftButtonDown" />
I added this:
<Style.Triggers>
<Trigger Property="IsSelected" Value="True" >
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="Black" />
</Trigger>
</Style.Triggers>
Did it work? No, it didn't. It didn't get rid of the big blue box. It did make my fonts bold though, so I know it at least had that effect.
Your Thumb is still generated inside of a ListBoxItem, Which has ControlTemplate of It's own.
You need to put in your ListBoxItem Style, a Setter for Template, and put in there only:
<ControlTemplate TargetType="ListBoxItem">
<ContentPresenter ContentSource="Content"/>
</ControlTemplate>
I think this is a "standard" thing, wpf does for you in ListBoxes. It highlights the currently selected Item for you. I found a similar thread here: WPF ListBox, how to hide border and change selected item background color?
They fixed it by adding this code to the listbox and setting the HighlightBrushKey to Transparent for this ListBox, did you try that in your case?
<ListBox BorderThickness="0" HorizontalContentAlignment="Stretch" >
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
</Style.Resources>
</Style>
</ListBox.Resources>
</ListBox>

Change the color of elements inside a Grid once a mouse hovers the Grid

I'm working on a WPF application. I have a button, which contains a Grid with a TextBlock and a Path element, as following:
<Button x:Name="btn" Margin="1">
<Button.Content>
<Grid Style="{DynamicResource button_special_hover}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*"/>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="15*" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="tx" TextWrapping="Wrap" Text="{DynamicResource MenuString}" Grid.Column="2" />
<Path x:Name="path" Stretch="Fill" Fill="{DynamicResource MarkerBrush}" Data="M 65.3334,41.3334C 65.3334,45.0133 62.3467,48 58.6667" Grid.Column="0" />
</Grid>
</Button.Content>
What I want to achieve is that once I hover the grid inside the button with my mouse, I want the foreground of the text inside the textblock to turn into white and the fill color for the path element to turn into white too.
What I've tried to do is declare a style for the grid as following: (button_special_hover)
<Style x:Key="button_special_hover" TargetType="{x:Type Grid}">
<Setter Property="Background" Value="Transparent"/>
<Style.Resources>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Grid},
Path=IsMouseOver}" Value="True">
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Path}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Grid},
Path=IsMouseOver}" Value="True">
<Setter Property="Fill" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Style.Resources>
</Style>
I don't get what I want. Once I hover over the grid, nothing happens. Only when I hover the Textblock inside the grid, the text turns into White. Once I hover the Path, nothing happens.
How can I achieve that once I hover the grid both the textblock foreground and the Path Fill turn into White? Thanks!
Your problem is that you specified the fill on the path element.
NOTE: I 'fixed' the Data by added ",41.3334" to it.
<Path x:Name="path" Stretch="Fill" Fill="{DynamicResource MarkerBrush}" Data="M 65.3334,41.3334C 65.3334,45.0133 62.3467,48 58.6667,41.3334" Grid.Column="0" />
When you do that, you can't override it. If you specified it in the style instead then you'd get the results you want.
Remove Fill from Path:
<Path x:Name="path" Stretch="Fill" Data="M 65.3334,41.3334C 65.3334,45.0133 62.3467,48 58.6667,41.3334" Grid.Column="0" />
and add Fill to the style:
<Style x:Key="button_special_hover" TargetType="{x:Type Grid}">
<Setter Property="Background" Value="Transparent"/>
<Style.Resources>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Grid},
Path=IsMouseOver}" Value="True">
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Path}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Grid},
Path=IsMouseOver}" Value="True">
<Setter Property="Fill" Value="White"/>
</DataTrigger>
</Style.Triggers>
<!-- "All I did was add this line below" -->
<Setter Property="Fill" Value="{DynamicResource MarkerBrush}"/>
</Style>
</Style.Resources>
</Style>

How to properly apply a DataTrigger binding inside a WPF style for a different component type?

I'm still a little green when it comes to WPF. I'm currently working on a WPF form that has several text boxes on it. Each of those TextBoxes are paired with a TextBlock sitting in the same x,y coord, acting as GhostText. Once you click inside the TextBox the GhostText disappears.
Below is an example of how the binding was originally setup in the form's XAML (this same code is duplicated for all text boxes thus the reasoning behind using a style) :
<TextBox Grid.Column="0" Width="40" Height="25" VerticalAlignment="Top" HorizontalAlignment="Left" x:Name= "RecordMinutesTextBox" Padding="12,5,5,0" Text ="{Binding RecordMinute}" Margin="0,25,5,1" PreviewTextInput="CheckNumberValidation" Background="{Binding ElementName=FireWashingtonResponseTimeReport,Path=DataContext.RequiredFieldColor}"/>
<TextBlock Grid.Column="0" Width="40" IsHitTestVisible="False" Text="MIN" VerticalAlignment="Center" HorizontalAlignment="Left" Foreground="DarkGray" Margin="8,25,0,1" >
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=RecordMinutesTextBox}" Value="">
<Setter Property="Visibility" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
*Note the name of one of the many text boxes,"RecordMinutesTextBox", used as the ElementName for the DataTrigger Binding.
Here is the code from inside my WPF Style template:
<Style x:Key="MinuteAndSecondsGhostText" TargetType="TextBlock">
<Setter Property="Width" Value="40"/>
<Setter Property="IsHitTestVisible" Value="False"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Foreground" Value="DarkGray"/>
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=??WhatDoIPutHere??}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>enter code here
So my question really comes down to this. What should I use in the DataTrigger Binding as the ElementName for this style? Considering that I have multiple TextBoxes with different names on my form. Thanks in advance.
I give you an idea... and you can change your codes base on it.
In my following sample you see the TextBlock shows state of IsFocused property of the TextBox. So you can put the pair of your elements in a parent like StackPanel and access to properties of one child in another child by RelativeSource instead of ElementName...
Just put this codes in your window and focus in TextBox, What you see inside the TextBlock?
<StackPanel Orientation="Horizontal" Background="White" Margin="20">
<TextBox Text="" Name="TextBox" Background="DarkSalmon" Width="100" Height="30"/>
<TextBlock Text="{Binding Path=Children[0].IsFocused,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type StackPanel}}}"
Margin="20,0"/>
</StackPanel>
Edit:
Base on my above idea you can use RelativeSource instead of ElementName to resolve your problem like the following sample:
<Style x:Key="MinuteAndSecondsGhostText" TargetType="TextBlock">
<Setter Property="Width" Value="40"/>
<Setter Property="IsHitTestVisible" Value="False"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Foreground" Value="DarkGray"/>
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Children[0].Text,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type StackPanel}}}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
And in your Window body:
<StackPanel x:Name="Pair1" Orientation="Horizontal" Background="White" Margin="20">
<!--TextBox should be first child of StackPanel-->
<TextBox Text="" Name="TextBox1" Background="DarkSalmon" Width="100" Height="30"/>
<TextBlock Text="Sample Text" Style="{StaticResource MinuteAndSecondsGhostText}" Margin="20,0"/>
</StackPanel>
<StackPanel x:Name="Pair2" Orientation="Horizontal" Background="White" Margin="20">
<!--TextBox should be first child of StackPanel-->
<TextBox Text="" Name="TextBox2" Background="DarkSalmon" Width="100" Height="30"/>
<TextBlock Text="Sample Text" Style="{StaticResource MinuteAndSecondsGhostText}" Margin="20,0"/>
</StackPanel>

WPF Edit Resource Dictionary dynamically passing parameters

I have a style defined in my resource dictionary so the style can be used in all over my application.
<Style x:Key="HyperlinkStyle" TargetType="{x:Type Hyperlink}">
<Setter Property="Foreground" Value="{StaticResource Color3}" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="TextBlock.TextDecorations" Value="{x:Null}" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="{StaticResource Color3Pressed}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{StaticResource Color2NotEnabled}" />
</Trigger>
</Style.Triggers>
</Style>
I use the style in Datagrid like this :
<DataGridTemplateColumn Width="140*" CanUserReorder="False" CanUserResize="True" Header="">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate />
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource DatagridCellHyperlinkStyle}">
<Setter Property="IsEnabled" Value="{Binding Path=MyObject, Converter={StaticResource ConverterMyObject}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Padding="{TemplateBinding Padding}" VerticalAlignment="Center">
<TextBlock Width="Auto" Height="Auto" TextTrimming="CharacterEllipsis">
<Hyperlink IsEnabled="{TemplateBinding IsEnabled}">
<InlineUIContainer TextDecorations="{Binding Path=TextDecorations, RelativeSource={RelativeSource AncestorType=TextBlock}}" Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType=TextBlock}}">
<ContentPresenter Width="Auto" Height="Auto" Content="{Binding DataContext.MyObject.Name, RelativeSource={RelativeSource AncestorType=DataGridRow}}"/>
</InlineUIContainer>
<Hyperlink.Style>
<Style TargetType="Hyperlink" BasedOn="{StaticResource HyperlinkStyle}">
<EventSetter Event="Hyperlink.Click" Handler="Clic" />
</Style>
</Hyperlink.Style>
</Hyperlink>
</TextBlock>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
This works great but I would like to be able to have 2 colors in my style depending on a parameter.
For example, my datagrid has 1500 rows, I want to highlight 100 rows from the collection by changing their hyperlink color.
So I thought the best was to set the Tag property of the hyperlink to a custom value and use it in the style. Following this answer : https://social.msdn.microsoft.com/Forums/vstudio/en-US/d3424267-ed1f-4b30-90a1-5cca9843bd22/problem-making-a-trigger-on-the-tag-property?forum=wpf, I made the changes below :
xmlns:sys="clr-namespace:System;assembly=mscorlib"
First I've set the tag manually for each row to see if the style changes :
<Hyperlink IsEnabled="{TemplateBinding IsEnabled}" Tag="10">
and added this trigger to my hyperlink's style:
<Trigger Property="Tag">
<Trigger.Value>
<sys:Byte>10</sys:Byte> <!-- My highlighted object -->
</Trigger.Value>
<Setter Property="Foreground" Value="{StaticResource Color1}" />
</Trigger>
But it doesn't work when I launch my application (I still have "Color3" whereas I want "Color1" when Tag is equals to 10.
I've checked the output there isn't any error.
I've tried to change "sys:Byte" by "sys:String" but the result was the same.
Do I need to change my approach for this problem ? I've read maybe we can't add trigger on Tag Property.
Thank you
I suggest if you set the tag like this
<Hyperlink IsEnabled="{TemplateBinding IsEnabled}" Tag="10">
Then write the trigger in the same way:
<Trigger Property="Tag" Value="10">
...setter
</Trigger>
Or if in hyperlink you use Byte type as Tag:
<Hyperlink IsEnabled="{TemplateBinding IsEnabled}">
<Hyperlink.Tag>
<sys:Byte>10</sys:Byte>
</Hyperlink.Tag>
</Hyperlink>
Then same goes in the trigger:
<Trigger Property="Tag">
<Trigger.Value>
<sys:Byte>10</sys:Byte>
</Trigger.Value>
...setter
</Trigger>

Categories

Resources