Styles, datatemplates and triggers - c#

I'm pretty new to WPF and needed a little help. I like to use custom UserControls as Templates instead of defining them in the Style or as ControlTemplates mainly because I can see the outcome as I'm writing the code. This approach was working great when I started but I'm not sure how to check for triggers on my control and change stuff in the UserControl template. Hopefully someone understands what I'm trying to say here. Anyways here's some code. I want to know if an item from ListView[MenuTray] is selected inside the DataTemplate of the ListViewItem control...
MainWindow.xaml
<Window x:Class="Cellestus.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="1000" Height="650" Loaded="Window_Loaded">
<Window.Resources>
<ResourceDictionary Source="/Resources/GlobalResources.xaml" />
</Window.Resources>
<Viewbox x:Name="WindowView" Stretch="Fill">
<Canvas x:Name="WindowCanvas" Width="1000" Height="650" Background="{StaticResource MainWindow_Bg}">
<!-- other stuf... -->
<ListView x:Name="MenuTray" Width="1000" Height="60" Canvas.Bottom="0" Style="{StaticResource MenuTray_Style}" />
</Canvas>
</Viewbox>
</Window>
MainWindow.xaml.cs, Method:Window_Loded
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// MenuTrayItems.List: ObservableCollection<MenuTrayItem>
// MenuTrayItem: Class with a couple of string variables, Title, image, page and etc..
MenuTray.ItemsSource = MenuTrayItems.List;
}
/Resources/GlobalResources.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:class="clr-namespace:Cellestus.Classes"
xmlns:view="clr-namespace:Cellestus.Views">
<!-- Brushes -->
<ImageBrush x:Key="MainWindow_Bg" ImageSource="/Images/Backgrounds/MainWindow.png" Stretch="Fill" />
<LinearGradientBrush x:Key="Gradient_Black" StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#2c2c2c" Offset="0" />
<GradientStop Color="#151515" Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="Gradient_LightGray" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#6B6B6B" Offset="1" />
<GradientStop Color="#D6D7D8" Offset="0" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="Gradient_Gray" StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#515151" Offset="0" />
<GradientStop Color="#282828" Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="Gradient_Green" StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#77ba53" Offset="0" />
<GradientStop Color="#5a9c37" Offset="1" />
</LinearGradientBrush>
<!-- Templates -->
<ItemsPanelTemplate x:Key="HorizontalListView">
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
<DataTemplate x:Key="MenuTrayItem_Template" DataType="{x:Type class:MenuTrayItem}">
<view:MenuTrayItemView />
</DataTemplate>
<!-- Styles -->
<Style x:Key="MenuTray_Style" TargetType="{x:Type ListView}">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="{StaticResource Gradient_Black}" />
<Setter Property="ItemsPanel" Value="{StaticResource HorizontalListView}" />
<Setter Property="ItemTemplate" Value="{StaticResource MenuTrayItem_Template}" />
</Style>
</ResourceDictionary>
/Views/MenuTrayItemView.xaml
<UserControl x:Class="Cellestus.Views.MenuTrayItemView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary Source="/Resources/GlobalResources.xaml" />
</UserControl.Resources>
<Border x:Name="ItemContainer" Width="150" Height="60" CornerRadius="10" Background="{StaticResource Gradient_Gray}" MouseEnter="ItemContainer_MouseEnter" MouseLeave="ItemContainer_MouseLeave">
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Image Margin="0,3,0,0" Width="60" Height="30" Source="{Binding Image, FallbackValue='/Images/Icons/MenuTray/default.png'}" />
<TextBlock Margin="3,0,0,0" Text="{Binding Title, FallbackValue='title'}" FontSize="10" Foreground="White" />
</StackPanel>
</Border>
</UserControl>
/Views/MenuTrayItemView.xaml.cs
private void ItemContainer_MouseEnter(object sender, RoutedEventArgs e)
{
ItemContainer.Background = FindResource("Gradient_LightGray") as Brush;
}
private void ItemContainer_MouseLeave(object sender, RoutedEventArgs e)
{
ItemContainer.Background = FindResource("Gradient_Gray") as Brush;
}
Here's an image for the visual people:
Well if you made it this far. What I want to know is how to catch the IsSelected value on the ListViewItem control in my DataTemplate / UserControl(MenuTrayView.xaml) and change the background to the static resource "Gradient_Green". I know I can just handle the MouseDown event in my view, but I don't want to do that. I want to know if the item is actually selected.

You need to create a style to your user control, and do something like
<Style TargetType="{x:Type local:MenuTrayItemView}">
<Trigger Property="IsSelected" Value="true">
<Setter Property="background" Value="Static Resource Gradient_Green"/>
</Trigger>
</Style.Triggers>
</Style>
if you ot problems go here WPF Trigger for IsSelected in a DataTemplate for ListBox items

Related

Styling a Selected ListBoxItem

EDIT:
<Window x:Class="test_project.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:test_project"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox>
<ListBox.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red" />
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="White" />
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="Yellow" Opacity="0.6" />
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="White" />
</ListBox.Resources>
<ListBox.Items>
<ListBoxItem Content="Hello"/>
<ListBoxItem Content="Hello"/>
</ListBox.Items>
</ListBox>
</Grid>
</Window>
I want to Style a ListBoxItem so that it has a blue background with white text when selected. I've looked at many answers online and they all seem to give contradicting opinions, none of which have worked for me so far. Here is my ListBox:
<ListBox Grid.Row="1" Margin="5" ItemContainerStyle="{StaticResource BlueItemStyle}"
BorderBrush="#06658D"
ItemsSource="{Binding UsersView}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is the Style that I have used so far and it achieves nothing:
<!--ListBoxItem-->
<Style TargetType="{x:Type ListBoxItem}" x:Key="BlueItemStyle">
<Setter Property="Height" Value="40"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Background" Value="#06658D"/>
</Trigger>
</Style.Triggers>
</Style>
However this does not Style the ListBoxItem in any way. Simply put, my question would be the simplest way to Style a selected ListBoxItem so the ListBox looks like this:
This solution does not work on Windows 10, which means it's no longer suitable at all, going forward. Thanks, Microsoft.
To the best of my knowledge, that's going to have to mean replacing the ListBoxItem control template. Two questions addressing that case:
WPF. ListBox item style
https://stackoverflow.com/a/35810145/424129
You can't change the selected item background color that way; the Background property of the ListBoxItem doesn't change for the selection state. Instead, ListBoxItem's default control template uses Background for unselected, and has a trigger which replaces the actual background brush of some template child with {DynamicResource {x:Static SystemColors.HighlightBrushKey}} when IsSelected is true.
You could do the same with a child of your DataTemplate, or you could replace the Template but it's easier just to override the resource instead.
You can override the same resource globally as well, for a consistent look.
<ListBox
Grid.Row="1"
Margin="5"
ItemContainerStyle="{StaticResource BlueItemStyle}"
BorderBrush="#06658D"
ItemsSource="{Binding UsersView}"
>
<ListBox.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#06658D" />
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="White" />
<!--
The default inactive selection color in Win7 is much too pale for my taste;
our older users are unable to distinguish it from white on most monitors.
-->
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="#06658D" Opacity="0.6" />
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="White" />
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Get value for Style from property of the instance to be styled

I would like to create elements during runtime and add a style to them.
Given the following Xaml, I would like to bind the value for the property Center to the actual values of a styled object (see main method). I tried different Binding notations but without success (propably because I am still new to xaml). I also tried to just change the Center on the instance but the instance is frozen and cannot be changed.
<Window x:Name="window" x:Class="CirclePing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="AlertBubble" TargetType="{x:Type Path}">
<Setter Property="StrokeThickness" Value="0"/>
<Setter Property="Data">
<Setter.Value>
<!-- how can I bind Center to the Tag property of a 'styled' instance? -->
<EllipseGeometry x:Name="circle"
Center="200,200"
RadiusX="100"
RadiusY="100">
</EllipseGeometry>
</Setter.Value>
</Setter>
<Setter Property="Fill">
<Setter.Value>
<RadialGradientBrush>
<GradientStop Color="Black"/>
<GradientStop Color="#2F5CB2"/>
</RadialGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="OpacityMask">
<Setter.Value>
<RadialGradientBrush>
<GradientStop Color="#00000000" Offset="0.5"/>
<GradientStop Color="#FFFFFFFF" Offset="1"/>
</RadialGradientBrush>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
</Window>
My Main method:
InitializeComponent();
Random r = new Random();
for (int i = 0; i < 500; i++)
{
var path = new Path();
int positionX = r.Next(1400);
int positionY = r.Next(800);
path.Tag = new Point(positionX, positionY);
path.Style = (Style)this.Resources["AlertBubble"];
}
Now I would like the following to be true: (EllipseGeometry)path.Data).Center.Y == positionY. What binding expression should I use?
You can bind Center using RelativeSource markup extension and finding parent Path instance object like this:
<EllipseGeometry x:Name="circle"
Center="{Binding Tag, RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=Path}}"
RadiusX="100"
RadiusY="100"/>

Dynamically creating Expression Blend controls in WPF C#

I have created a button in Expression Blend 4. I want to dynamically create instances of this button at run time.
The code for the button is below:
<Button Content="Button" HorizontalAlignment="Left" Height="139" Margin="46,107,0,0" VerticalAlignment="Top" Width="412" Grid.ColumnSpan="2">
<Button.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Button.Background>
</Button>
In addition to the supplying the code can you put comments in explaining what you are doing so that I can learn the general principle.
I know this is a simple question so I have been reading up places such as: Expression blend & WPF, Is there a way to "extract" WPF controls of Expression Blend?, and http://social.msdn.microsoft.com/forums/en-US/wpf/thread/ffa981b8-9bba-43a2-ab5e-8e59bc10fc0d/ unfortunately none of these have helped.
In your WPF application you should have a App.xaml file, in there you can add Styles that are to be used thoughout your UI.
Example:
<Application x:Class="WpfApplication8.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<!--The style for all your buttons, setting the background property to your custom brush-->
<Style TargetType="{x:Type Button}"> <!--Indicate that this style should be applied to Button type-->
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>
Or if you dont want to apply to all buttons, you can give your Style a Key so you can apply to certain Buttons in your UI
<Application x:Class="WpfApplication8.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<!--Add a x:Key value so you can use on certain Buttons not all-->
<Style x:Key="MyCustomStyle" TargetType="{x:Type Button}">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>
To use this Style on a Button, just add a binding to the Style property of the Button
<Button Style="{StaticResource MyCustomStyle}" />
this will apply the Style just to this Button
Or if you really want to do it in code behind you can just add the Brush you want to the background
Button b = new Button
{
Background = new LinearGradientBrush(Colors.Black, Colors.White, new Point(0.5, 1), new Point(0.5, 0))
};
its very easy to translate xaml to code because xaml uses the exact same property names, like the code brush I posted above:
new LinearGradientBrush(Colors.Black, Colors.White, new Point(0.5, 1), new Point(0.5, 0))
is ....
Brush(firstColor,secondColor,StartPoint EndPoint)
Xaml just accesses properties in the button, they will all have the same names in C#.

Changing control properties using BasedOn

I have a UserControl with some buttons. I need to change the background colour of the buttons but retain all other properties and colours such as mouseover events
I've used the following code which I hoped would define UniqueButton1 based on {StaticResource {x:Type Button}}" but change the background property to define my own colours
<UserControl x:Class="Project.Detail"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="800" BorderBrush="#FF5380E7" BorderThickness="0,0,0,1" xmlns:my="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero">
<UserControl.Resources>
<Style x:Key="UniqueButton1" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}" >
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0,1">
<GradientStop Color="#FFF896CE" Offset="0" />
<GradientStop Color="#FFF788C7" Offset="0.5" />
<GradientStop Color="#FFF570BB" Offset="0.5" />
<GradientStop Color="#FFF353AE" Offset="1" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid Height="30" Width="800">
<Button Height="30" HorizontalAlignment="Left" Margin="360,0,0,0" Name="button2" VerticalAlignment="Top" Width="50" Click="b_Click" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="10" FontStretch="SemiCondensed" />
<Button Style="{StaticResource UniqueButton1}" Height="30" HorizontalAlignment="Left" Margin="423,0,0,0" Name="button1" VerticalAlignment="Top" Width="50" Click="b_Click" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="10" FontStretch="SemiCondensed" />
</Grid>
</UserControl>
It kind of works, in that I get the background colours the way I need, but UniqueButton1 which I've defined in <UserControl.Resources> is not based on the other default button2 with no style changes applied. Eg, the background, colours, animation speeds etc are all different
Perhaps it's because I'm also applying a theme?
My question is either
1. How do I define a resource that is based on a default button2 with a theme applied?
or
2. How do I get all of the properties of button2 with the theme applied so I can build my own style?
It's been suggested I may need to build my own style, which is fine. But I need to match the behaviour of the default themed button except for the background property - if I can't see how it's made up it seems a bit of a catch22.
EDIT: To clarify things
These two images show 3 buttons, you can see the mousover for button3 is orange and blue for button2. What you can't see is that the animation speed is also different. Essentially I want my button to look like button1 in the rest state and button3 on mouseover whilst retaining the button3 animation speed and other properties. Button2 is what I'm getting using basedon with no further properties changed.
Button3 is produced with no resources and XAML
<Button Height="30" HorizontalAlignment="Left" Margin="360,0,0,0" VerticalAlignment="Top" Width="50" Click="button_Click" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="10" FontStretch="SemiCondensed"/>
Button2 is produced using
<UserControl.Resources>
<Style x:Key="TEST1" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}" >
</Style>
</UserControl.Resources>
<Button Style="{StaticResource TEST1}" Height="30" HorizontalAlignment="Left" Margin="360,0,0,0" VerticalAlignment="Top" Width="50" Click="button_Click" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="10" FontStretch="SemiCondensed"/>
Button1 is produced using
<UserControl.Resources>
<Style x:Key="UniqueButton1" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}" >
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0,1">
<GradientStop Color="#FFF896CE" Offset="0" />
<GradientStop Color="#FFF788C7" Offset="0.5" />
<GradientStop Color="#FFF570BB" Offset="0.5" />
<GradientStop Color="#FFF353AE" Offset="1" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Button Style="{StaticResource UniqueButton1}" Height="30" HorizontalAlignment="Left" Margin="360,0,0,0" VerticalAlignment="Top" Width="50" Click="button_Click" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="10" FontStretch="SemiCondensed" />
So obviously button 1 and 2 are not based on button3 which is what I want. I want to clone button3 properties and ONLY change the background to pink.
Change your BaseButton from x:Type to a named one and then set it's name on others BasedOn, that should do the trick.

Make Control Invisible on start up (no values) / Access control inside DataTemplate

Currently i have a LinearGradientBrush displaying as a bar chart. The problem is on start up of my program (no values from databinding yet) i am getting white bars all across my screen, since the GradientBrush has no values yet and displays this as default.
How exactly do i make sure nothing shows until it actually get its databound values.
How to make this invisible until it get values?
Code of the DataTemplate and the itemsControl where its being used:
<ItemsControl x:Name="icGrafiek"
Margin="0,0,0,0"
ItemsSource="{Binding Source={StaticResource Grafiek}}"
ItemTemplate="{DynamicResource GrafiekItemTemplate}"
RenderTransformOrigin="1,0.5" Grid.RowSpan="6" Grid.Column="1"
<DataTemplate x:Key="GrafiekItemTemplate">
<Grid>
<Border Height="30" Margin="15" Grid.RowSpan="6">
<Border.Background>
<LinearGradientBrush StartPoint="0.0,0" EndPoint="1.0,0">
<GradientStopCollection>
<GradientStop Offset="0.0" Color="{Binding FillBar, UpdateSourceTrigger=PropertyChanged}" />
<GradientStop Offset="{Binding Value, UpdateSourceTrigger=PropertyChanged}"
Color="{Binding FillBar, UpdateSourceTrigger=PropertyChanged}"/>
<GradientStop Offset="{Binding Value, UpdateSourceTrigger=PropertyChanged}"
Color="Transparent"/>
<GradientStop Offset="1" Color="Transparent" />
</GradientStopCollection>
</LinearGradientBrush>
</Border.Background>
</Border>
</Grid>
</DataTemplate>
One way to hide the bars until there's data bound is to use Triggers to set the Visibility depending on some value.
In your DataTemplate:
<DataTemplate x:Key="GrafiekItemTemplate">
<Grid x:Name="grid">
...
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Value}" Value="{x:Null}">
<Setter TargetName="grid" Property="Visibility" Value="Collapsed" />
</DataTrigger>
</DataTemplate.Triggers>
You may have to use a different value than "Value" for the binding path in the DataTrigger, but this should get you started.

Categories

Resources