Using Styles in Windows Presentation Foundation - wpf

Imagine I have a data bound ListView and in the <ControlTemplate.Triggers>
I have the following
<DataTrigger Binding="{Binding Path=Status}" Value="Completed">
<Setter Property="Background" Value="{StaticResource CompletedBackground}" />
<Setter Property="Foreground" Value="Black" />
</DataTrigger>
I want that to be bound to a Style i have in my Grid.Resources which looks like the following:
<Style x:Key="CompletedBackground" TargetType="ListViewItem">
<Setter>
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFBCFAA6" Offset="0"/>
<GradientStop Color="#FFA3E88B" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
However, as you might imagine this doesn't work, suprise suprise, you can't bind "Setter" to "Background", so my question is, how do I actually solve the problem?
I've looked through the following a lot of times, cant find any information here.

What you're trying to do is fundamentally flawed. For starters, your style's setter doesn't specify a target property. Presumably, the target property should be Background:
<Style x:Key="CompletedBackground" TargetType="ListViewItem">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFBCFAA6" Offset="0"/>
<GradientStop Color="#FFA3E88B" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
Secondly, you're then trying to assign a Style instance to the Background property, which is of type Brush, not Style.
Depending on exactly what you're trying to achieve, you should be able to just change the Style to a Brush resource:
<LinearGradientBrush x:Key="CompletedBackground" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFBCFAA6" Offset="0"/>
<GradientStop Color="#FFA3E88B" Offset="1"/>
</LinearGradientBrush>
Then use it from your trigger in the same fashion you already are.

Related

Applying same style to multiple elements

I am new to using WPF and I was trying to apply Style (e.g. Background for TextBox, Button and MenuItem should be Orange). To achieve this I did something like:
<Style TargetType="TextBox" x:Key="sampleTextBox">
<Setter Property="Margin" Value="2"/>
<Setter Property="FontFamily" Value="Verdana"/>
<Setter Property="FontSize" Value="11px"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
<GradientStop Color="#FFFFD190" Offset="0.2"/>
<GradientStop Color="Orange" Offset="0.85"/>
<GradientStop Color="#FFFFD190" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
and repeated the same piece of code for targettype Button and for target menu.
This is working absolutely fine. But I would like to minimize the amount of repeated code by probably having multiple targettype values.
Please let me know if it is possible.
Thanks.
<Window.Resources>
<Style x:Key="sampleTextBox">
<Setter Property="Control.FontFamily" Value="Verdana"/>
<Setter Property="Control.FontSize" Value="11px"/>
<Setter Property="Control.FontWeight" Value="Bold"/>
<Setter Property="Control.Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
<GradientStop Color="#FFFFD190" Offset="0.2"/>
<GradientStop Color="Orange" Offset="0.85"/>
<GradientStop Color="#FFFFD190" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel>
<TextBlock Text="This is a string and it should be wrapped." Style="{StaticResource sampleTextBox}"/>
<TextBox Text="This is a string and it should be wrapped." Style="{StaticResource sampleTextBox}"/>
</StackPanel>
Style has an attribute BasedOn. http://msdn.microsoft.com/en-us/library/system.windows.style.basedon.aspx With this you can use Style inheritance. Define a base style with common attributes and derive your child styles with specific attributes.
You could use FrameworkElement as a TargetType:
<Style TargetType="FrameworkElement" x:Key="CommonStyle">
<Setter Property="Control.Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
<GradientStop Color="#FFFFD190" Offset="0.2"/>
<GradientStop Color="Orange" Offset="0.85"/>
<GradientStop Color="#FFFFD190" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
And then use specific styles for each element by inheriting (BasedOn) CommonStyle:
<Style TergetType="TextBox" BasedOn="{StaticResource CommonStyle}" x:Key="TextBoxStyle">
<Setter Property="Margin" Value="2"/>
<Setter Property="FontFamily" Value="Verdana"/>
<Setter Property="FontSize" Value="11px"/>
<Setter Property="FontWeight" Value="Bold"/>
</Style>

Why could not I call this brush here?

I created a linearGradientBrush inside "Application.Resources" and tried to call it with one of the style setters. The compiler says the name could not be resolved. Could not see why....
<Style x:Key="GridTextBoxStyle" TargetType="TextBox">
<Setter Property="Background" Value="{StaticResource TextBlinearBrush}"/>
</Style>
<LinearGradientBrush x:Key="TextBlinearBrush" EndPoint="1,0.5" StartPoint="0,0.5">
<GradientStop Color="Black" Offset="0" />
<GradientStop Color="Gray" Offset="1" />
<GradientStop Color="DarkGray" Offset="0.541" />
</LinearGradientBrush>
You'll need to define the brush before it is used. Just move the LinearGradientBrush above the Style where it is referenced and that should do the trick.

WPF Gradient in 2 directions

If i want to make the edges of a selected item in a Listbox look smooth I do this:
<Setter Property="Background" TargetName="Bd">
<Setter.Value>
<LinearGradientBrush EndPoint="0,0" StartPoint="1,0">
<GradientStop Offset="0" Color="Transparent"/>
<GradientStop Offset="0.05" Color="{x:Static SystemColors.HighlightColor}"/>
<GradientStop Offset="0.95" Color="{x:Static SystemColors.HighlightColor}"/>
<GradientStop Offset="1" Color="Transparent"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
However, this only makes the left and right edge smooth, not the top and bottom. If I change StartPoint and EndPoint, I can make the top and bottom smooth, but then I loose the smoothness on the left&right sides. So how can I make all 4 borders smooth using a Gradient brush?
The OpacityMask is one way to do this, as others have already suggested, but it's slightly challenging because you cannot set an OpacityMask on a brush. You can only set it on a visual - OpacityMask is something that is done on a per-visual basis. But the Background of a ListBox isn't a separate element in the visual tree - it's just a property of the ListBox, and it's usually template bound to something like a Border element somewhere in the template.
Same goes for the use of bitmap effects that some people have suggested here - those are also applied to whole visuals, not to individual brushes.
However, you can handle this with a VisualBrush - that lets you define a brush by using a visual tree. So I think this does roughly what you're looking for:
<Setter Property="Background" TargetName="Bd">
<Setter.Value>
<VisualBrush>
<VisualBrush.Visual>
<Rectangle Width="1" Height="1">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="0,0" StartPoint="1,0">
<GradientStop Offset="0" Color="Transparent"/>
<GradientStop Offset="0.05" Color="{x:Static SystemColors.HighlightColor}"/>
<GradientStop Offset="0.95" Color="{x:Static SystemColors.HighlightColor}"/>
<GradientStop Offset="1" Color="Transparent"/>
</LinearGradientBrush>
</Rectangle.Fill>
<Rectangle.OpacityMask>
<LinearGradientBrush EndPoint="0,0" StartPoint="0,1">
<GradientStop Offset="0" Color="Transparent"/>
<GradientStop Offset="0.05" Color="White"/>
<GradientStop Offset="0.95" Color="White"/>
<GradientStop Offset="1" Color="Transparent"/>
</LinearGradientBrush>
</Rectangle.OpacityMask>
</Rectangle>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
The corners might not be quite what you're looking for though - depends on how big they end up being. They don't look especially round when you use this technique. So you could go down the effect route. You might prefer this:
<Setter Property="Background" TargetName="Bd">
<Setter.Value>
<VisualBrush Viewbox="0.1,0.1,0.8,0.8">
<VisualBrush.Visual>
<Border Width="100" Height="100" CornerRadius="10"
Background="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}">
<Border.Effect>
<BlurEffect Radius="20"/>
</Border.Effect>
</Border>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
Note that I've used an Effect rather than a BitmapEffect. You have fewer options with Effect, but they're usually a better option as they're designed to render in hardware.
I don't think it can be done with the built-in gradient brushes. You could implement your own RectangleGradientBrush, but I don't think it's easy... (actually it doesn't seem possible, as Brush implementations depend on low level rendering stuff that's not accessible from user code)

WPF set border background in trigger

I need to create a trigger, that will change Border background property, when MouseEnter occurred. I did the follow:
<Border Width="20" Height="30" Focusable="True">
<Border.Background>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Color="Aquamarine" Offset="0"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.Background>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Color="Aquamarine" Offset="0"/>
<GradientStop Color="Beige" Offset="0.2"/>
<GradientStop Color="Firebrick" Offset="0.5"/>
<GradientStop Color="DarkMagenta" Offset="0.9"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
but it doesn't work. Thanks.
Common mistake. You have set the Border.Background property directly which will always override the value set by your trigger. (Locally set values have a very high precedence, style has a pretty low precedence.)
Instead, you should move your "normal" background into the Style like so:
<Border>
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Color="Aquamarine" Offset="0"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Style.Triggers>
<!-- the trigger you showed -->
</Style.Triggers>
</Style>
</Border.Style>
</Border>

silverlight styles

<navigation:Page.Resources>
<Style x:Key="PageBackground" TargetType="Grid">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
<GradientStop Color="White" Offset="1"/>
<GradientStop Color="Silver"/>
</LinearGradientBrush>
<Path x:Name="shinePath" Data="M0,0 L0,300 C-5.5,306.5 40,68 215,0 z" Stretch="Fill" Opacity="0.1">
<Path.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0" SpreadMethod="Pad">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="#00FFFFFF" Offset="0.871"/>
</LinearGradientBrush>
</Path.Fill>
</Path>
</Setter.Value>
</Setter>
</Style>
</navigation:Page.Resources>
error- property 'Value' is set more then once
You've got a single <Setter.Value> element, it can contain only one child elment. Looks to me as though the Path is intended for a different property. Can't think what though Grid doesn't have a property that can accept a path. Do you intend the path to be the content of the Grid?

Resources