Setting borderbrush to LinearGradientBrush in WPF - wpf

I'm new to WPF and still having some basic problems.
I have a control from devcomponents that defaults to a blue border. My textboxes etc. have a more grey colour. I want the devcomponents control to have the same border.
I look in the properties of a TextBox and see that BorderBrush is set to "System.Windows.Media.LinearGradientBrush" yet I can't put -
<WpfEditors:IntegerInput BorderBrush="System.Windows.Media.LinearGradientBrush"...
In fact, I can't put -
<TextBox BorderBrush="System.Windows.Media.LinearGradientBrush" ...
What magic am I missing?
Thanks.

To the property BorderBrush you have to assign a Brush (as you could guess by its name).
One kind of Brush is a LinearGradientBrush (the thing which makes a gradient between colors)
SolidColorBrush is another kind of Brush which could also get assigned.
As it looks as this kind of control you use has already assigned a LinearGradientBrush.
Now you can assign a Brush of your choice and override the already set Brush.
Example for a LinearGradientBrush:
<TextBox>
<TextBox.BorderBrush>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="Black" Offset="0.0" />
<GradientStop Color="White" Offset="1" />
</LinearGradientBrush>
</TextBox.BorderBrush>
</TextBox>
If you want your border just in a a solid color you can also use a SolidColorBrush.
<TextBox.BorderBrush>
<SolidColorBrush Color="Red" />
</TextBox.BorderBrush>
or just use the existing Converter Color --> SolidColorBrush
<TextBox BorderBrush="Red" Text="bla bla" />
EDIT:
And if you want that all your controls have the same Border you can add a Brush to the ResourceDictionary of a container object and reuse it for all the controls...
<!-- Add the Brush as resource to the surrounding window -->
<Window.Resources>
<SolidColorBrush x:Key="controlBorderBrush" Color="Gray" />
</Window.Resources>
<!-- -->
<TextBlock BorderBrush="{StaticResource controlBorderBrush}" Text="huhuuu" />
<otherlib:SpecialTextBlockWithOverriddenProps BorderBrush="{StaticResource controlBorderBrush}" Text="hahaaaaaaa" />

Related

Referencing a StaticResource in another DynamicResource

If I change some resource used as StaticResource then all the controls which referring to is affected.
And it does not in case the resource is referred as DynamicResource.
But how about some resource referred as StaticResource in a DynamicResource?
<Color x:Key="Color">#000000</Color>
<LinearGradientBrush x:Key="GradientBrush" EndPoint="0,0" StartPoint="0,1" >
<GradientStop Color="{StaticResource Color}" Offset="0" />
<GradientStop Color="{StaticResource Color}" Offset="1" />
</LinearGradientBrush>
<DrawingBrush x:Key="TabItemBrush" >
<DrawingBrush.Drawing>
<GeometryDrawing Brush="{StaticResource GradientBrush}">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0 100 100 100"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid x:Name="Root">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected" >
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Panel.Background).(DrawingBrush.Drawing).(GeometryDrawing.Brush).(GradientBrush.GradientStops)[0].(GradientStop.Color)">
<EasingColorKeyFrame KeyTime="0" Value="Red" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Selected">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Panel.Background).(DrawingBrush.Drawing).(GeometryDrawing.Brush).(GradientBrush.GradientStops)[0].(GradientStop.Color)">
<EasingColorKeyFrame KeyTime="0" Value="Blue" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Border" Background="{DynamicResource TabItemBrush}">
<ContentPresenter x:Name="ContentSite" VerticalAlignment="Center"
HorizontalAlignment="Center" ContentSource="Header"
Margin="12,2,12,2" RecognizesAccessKey="True" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Border in TabItem refers TabItemBrush using DynamicResource.
TabItemBrush refers GradientBrush using StaticResource.
GradientBrush refers Color using StaticResource.
<StackPanel>
<TabControl>
<TabItem Header="AAA" >AAA</TabItem>
<TabItem Header="BBB" >BBB</TabItem>
<TabItem Header="CCC" >BBB</TabItem>
</TabControl>
<Border Background="{DynamicResource GradientBrush}" Height="1000" Width="1000"/>
</StackPanel>
I thought that all of TabItem change to same color when I change a selected item in TabControl because all of resources is referred as StaticResource except TabItemBrush.
But only the color of selected item is blue and the others red.
Moreover if i change all the StaticResource to DynamicResource, it works incorrectly ( all red or all blue).
Why StaticResource works as if it is not shared and DynamicResource as shared?
I found your question very difficult to understand. However, I think you are confused because you are not understanding the difference between StaticResource and DynamicResource.
As the names suggest, StaticResource references will/can never change (they are static).
Resources referenced using StaticResource are resolved only once at compile-time.
As the names suggest, DynamicResource references can change (they are dynamic).
Resources referenced using DynamicResource are resolved at runtime.
Since StaticResource references are resolved at compile-time, the XAML parser can avoid the overhead of creating an intermediate lookup expression (which is then executed at runtime).
This is why you should always avoid DynamicResource with the goal to improve the application's performance.
StaticResource does not allow forward references, whereas DynamicResource does - but the forward reference aspect doesn't matter in your context.
"If I change some resource used as StaticResource then all the
controls which referring to is affected."
This is definitely not correct. You can't change resources referenced as StaticResource and then observe that those changes update the referencing objects - this can't have never happened (see explanation of the fundamental characteristics above).
You probably meant something different here.
"I thought that all of TabItem change to same color when I change a selected >item in TabControl because all of resources is referred as StaticResource >except TabItemBrush.
But only the color of selected item is blue and the others red."
This is the correct behavior. You have defined the VisualState objects for "Selected" and "Unselected" with the VisualStateManager. According to these states clicking a TabItem will modify the Background of the selected item to blue and all other unselected items to red.
"Moreover if i change all the StaticResource to DynamicResource, it works >incorrectly ( all red or all blue)."
Here comes in the special behavior of the XAML engine in context of animations: it will freeze the Storyboard.
This means, all participating child instances like AnimationTimeline (e.g., ColorAnimation), or Animatable types in general, are frozen and therefore not changeable (they all inherit Freezable).
As a consequence, all referenced resources like Brush must be static and known at compile-time: you can't reference resources using DynamicResource if the referencing instance needs to be frozen.
Resources used in a Storyboard must be referenced as StaticResource.
Now, given the case that an element, for example a Border, uses DynamicResource to reference a resource that itself contains references to other resources:
If the element uses DynamicResource to reference a pure static resource (a resource that doesn't itself references other resources using DynamicReference), then the XAML engine will optimize the reference by treating it as a StaticResource, to avoid the overhead, and store it in the internal lookup table for the static resources (remember, StaticResource is much cheaper in terms of performance costs than looking up a DynamicResource). This resource can be targeted by a Storyboard animation as it is now static:
<ResourceDictionary>
<Color x:Key="Color">#000000</Color>
<!-- Resource is treated as a static resource -->
<LinearGradientBrush x:Key="GradientBrush" EndPoint="0,0" StartPoint="0,1" >
<GradientStop Color="{StaticResource Color}" Offset="0" />
<GradientStop Color="{StaticResource Color}" Offset="1" />
</LinearGradientBrush>
</ResourceDictionary>
<!-- The lookup behavior is optimized to StaticResource.
A Storyboard therefore will be able to animate the Background resource.
-->
<Border Background="{DynamicResource GradientBrush}" />
If the resource that an element references is itself referencing at least one resource using DynamicResource, then the reference will remain dynamic and will prevent e.g., the Storyboard from being frozen and the animation won't work:
<ResourceDictionary>
<Color x:Key="Color">#000000</Color>
<!-- Resource is treated as a dynamic resource,
because it contains at least one DynamicResource reference.
-->
<LinearGradientBrush x:Key="GradientBrush" EndPoint="0,0" StartPoint="0,1" >
<GradientStop Color="{DynamicResource Color}" Offset="0" />
<GradientStop Color="{StaticResource Color}" Offset="1" />
</LinearGradientBrush>
</ResourceDictionary>
<!-- The lookup behavior is now DynamicResource and not optimized.
A Storyboard won't be able to animate the properties of the resource as it can't be frozen.
-->
<Border Background="{DynamicResource GradientBrush}" />
Another effect you ecounter is that all items have the same Background when you select a TabItem.
This is related to the way the XAML engine handles resources. Referenceing a resource using StaticResource, results in the resource being shared by default.
You have two solutions to fix this issue:
Declare each participating resource of the static reference as non-shared by setting the x:Shared attribute to false:
<ResourceDictionary>
<Color x:Key="Color"
x:Shared="False">#000000</Color>
<LinearGradientBrush x:Key="GradientBrush"
x:Shared="False"
EndPoint="0,0"
StartPoint="0,1" >
<GradientStop Color="{StaticResource Color}" Offset="0" />
<GradientStop Color="{StaticResource Color}" Offset="1" />
</LinearGradientBrush>
</ResourceDictionary>
<Border Background="{StaticResource GradientBrush}" />
Alternatively, define the animated resource as inline. This way each TabItem border will have its own/non-shared Brush resource:
<ResourceDictionary>
<Color x:Key="Color">#000000</Color>
</ResourceDictionary>
<Border>
<Border.Background>
<LinearGradientBrush EndPoint="0,0" StartPoint="0,1" >
<GradientStop Color="{StaticResource Color}" Offset="0" />
<GradientStop Color="{StaticResource Color}" Offset="1" />
</LinearGradientBrush>
</Border.Background>
</Border>

Changing the border colors of a WPF combobox

So I am trying to change the style of my combobox in Expression blend.
What I did was create a combobox, and went RightClick > Edit Template > Edit a Copy
And I can change the colors of the combobox, except there is a white border in between the background of the combobox, and the border of the combobox. Here is a screen so you can see:
As you can see, there is a while border between the blue and red. As far as I can tell, the code to change the color of combobox is the following:
<ToggleButton Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource
ComboBoxReadonlyToggleButton}" BorderBrush="Red" Background="Blue"/>
But no matter what, there is always a white border. How do i get rid of it?
I know this is an old question, and it's specific to blend but when googling for this problem, this is one of the first things I found.
A really simple example of a way to fix this, that is a little less complex than the first answer mentioned is to set "Style" Properties. (Not sure this applies to blend since I don't use blend, but for simple wpf in visual studio, this works)
For example, this code below creates a window just like the one mentioned in the question, but with the white lines (in the drop down items) editable.
<ComboBox Background="Blue" BorderBrush="Red">
<ComboBox.ItemContainerStyle>
<!-- Without this style section, there are white lines around the borders. Even if you set these properties in the comboBoxItem areas -->
<Style TargetType="ComboBoxItem">
<Setter Property="Background" Value="Green"/>
<Setter Property="BorderBrush" Value="Purple"></Setter>
</Style>
</ComboBox.ItemContainerStyle>
<ComboBoxItem MouseMove="schedule" Name="cbi1">schedule</ComboBoxItem>
</ComboBox>
The problem is when you Edit a Copy, you're editing a copy with Microsoft's built-in chrome components. In order to change that outside border, you'll need to replace those bits with normal WPF controls so that you can change the values. For a combo box, you would want use the code here: http://msdn.microsoft.com/en-us/library/ms752094
e: This is the part you want to edit
<Border x:Name="Border"
Grid.ColumnSpan="2"
CornerRadius="2"
BorderThickness="1">
<Border.BorderBrush>
<LinearGradientBrush EndPoint="0,1"
StartPoint="0,0">
<GradientStop Color="{DynamicResource BorderLightColor}"
Offset="0" />
<GradientStop Color="{DynamicResource BorderDarkColor}"
Offset="1" />
</LinearGradientBrush>
</Border.BorderBrush>

WPF change dynamicresource in codebehind

The standard RadioButton does not support setting the color of the ellipse. So, I took a radiobutton template from this location as a basis for a custom RadioButton:
RadioButton Styles and Templates
<Ellipse.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{DynamicResource ControlLightColor}" />
<GradientStop Color="{DynamicResource ControlMediumColor}" Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
ControlLightColor and ControlMediumColor are defined as:
<Color x:Key="ControlLightColor">#ffff9a</Color>
<Color x:Key="ControlMediumColor">#ffff9a</Color>
Which gives us a yellow-ish ellipse.
How can I alter this color in the codebehind?
Regards,
Michel
Create a style by following this:
Creating a Style in code behind
then assign it to your element.Style
You can also access resources by
Resources["mykey"]
Solution:
<Ellipse x:Name="Border" StrokeThickness="1" Fill="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.RadioButtonColor}">
Public ReadOnly Property RadioButtonColor() As SolidColorBrush
Get
Dim solidColorBrush As SolidColorBrush
If MyBusinessLogic Then
solidColorBrush = _radioButtonNotRequiredBrush
Else
solidColorBrush = _radioButtonRequiredBrush
End If
Return solidColorBrush
End Get
End Property
Thumbs up for JRB for thinking along.

WPF: Dealing with images

I want to create the following functionality: I want to be able to show an image on a window, if it is provided, or a linear gradient brush if the image is not present. I came up with two approaches:
To create a Border and to apply ImageBrush to the border Background property if
the image Uri is provided, or LinearGradientBrush if not. This is easy to
implement: The view model would supply the border background property either with image
brush or linear gradient brush. But there's one big problem: if the image dimensions
don't fit the border size, the image is deformed which is something I wish to avoid. Is
there a way to set ImageBrush and to preserve the image dimensions ratio, i.e. to
apply something like Stretch = Stretch.Uniform?
To create a Border and an Image inside of it. Then, to create a data trigger for
the border, and if the image Uri (a property from the view model) is null, to set
the Background of the border to LinearGradientBrush and to leave it blank if
otherwise. I tried creating this, but the data trigger never understood the null
case. There is also a problem with the Image because if null is provided for
ImageSource property, an exception is thrown. The code looks like this:
<Border Width="130" Height="170">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding Image}" Value="{x:Null}">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#696969" Offset="0.0" />
<GradientStop Color="#2E2E2E" Offset="1.0" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Image Name="image" Stretch="Uniform">
<Image.Source>
<BitmapImage
DecodePixelWidth="{Binding ElementName=image, Path=Width}"
UriSource="{Binding Path=Image}" />
</Image.Source>
</Image>
</Border>
What is the best and the easiest way to implement such functionality? Thanks.
ImageBrush is derived from the TileBrush class, which has a Stretch property. So you can use not something like Stretch = Stretch.Uniform, but exactly that.

WPF Controls: How to Reference Resources in Animations?

I have written a control and successfully created a storyboard to cause an animation during triggered events. It changes the fill of an ellipse for a duration of time. Instead of writing a new RadialGradientBrush each time I need to change the fill, I provided two of them in the resources.
EDIT:
I have an Ellipse that is the main component to the control and is what is affected by the animation. It's implementation is simple and looks like this:
<Ellipse Name="myEllipse" Style="{StaticResource DimStyle}" />
When I add it to the storyboard (instead of referencing the brush as a resource), my animation works as intended. When I reference the brush as a resource I get this exception:
"Cannot find resource named 'IlluminatedStyle'. Resource names are case sensitive."
Inside of the storyboard, this is where it is currently referenced:
<UserControl.Resources>
<Storyboard x:Key="Foo">
<ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames.KeyFrames>
<DiscreteObjectKeyFrame KeyTime="0:0:0.01" Value="{StaticResource IlluminatedStyle}" />
<DiscreteObjectKeyFrame KeyTime="0:0:0.85" Value="{StaticResource DimStyle}" />
</ObjectAnimationUsingKeyFrames.KeyFrames>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
The styles are closely identical and only the GradientStop color properties differ so I'll provide only one style for an example.
The Style Referenced
<UserControl.Resources>
<Style x:Key="IlluminatedStyle" TargetType="Ellipse">
<Setter Property="Fill">
<Setter.Value>
<RadialGradientBrush>
<GradientStop Color="#FF215416" Offset="1"/>
<GradientStop Color="#FE38DA2E" Offset="0"/>
<GradientStop Color="#FE81FF79" Offset="0.688"/>
</RadialGradientBrush>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
So how do I correctly reference a style such as this in my Storyboard?
Note: The Storyboard and Style are both contained within the same UserControl.Resources tag but broken out for this example.
EDIT
I put the Style before the Storyboard in UserControl.Resources and now I get an exception stating:
"This Freezable cannot be frozen.
at System.Windows.Freezable.Freeze()
at System.Windows.Freezable.GetCurrentValueAsFrozen()
at System.Windows.Media.Animation.TimelineCollection.GetCurrentValueAsFrozenCore(Freezable source)
at System.Windows.Freezable.CloneCoreCommon(Freezable sourceFreezable, Boolean useCurrentValue, Boolean cloneFrozenValues)
at System.Windows.Media.Animation.Timeline.GetCurrentValueAsFrozenCore(Freezable sourceFreezable)
at System.Windows.Freezable.GetCurrentValueAsFrozen()
at System.Windows.Media.Animation.Clock..ctor(Timeline timeline)
at System.Windows.Media.Animation.TimelineGroup.AllocateClock()
at System.Windows.Media.Animation.Clock.AllocateClock(Timeline timeline, Boolean hasControllableRoot)"
There are three reasons why a Freezable cannot be frozen:
It has animated or data bound properties.
It has properties that are set by a dynamic resource.
It contains Freezable sub-objects that cannot be frozen.
So, first find out which Freezable is causing trouble and then check the above.
Seeing that I am new to WPF and XAML I made the mistake of making my resources a style and did not realize that I could have simply made the brushes a resource and avoid styles altogether.
I kept the reference to the DiscreteObjectKeyFrames' values as static to the new brush resources. I changed the Ellipse to this:
<Ellipse Name="myEllipse" Fill="{StaticResource DimBrush" />
The style property was removed and I assigned the brush to the fill property directly. In the ObjectAnimationUsingKeyFrames tag I added Fill as the Storyboard.TargetProperty since I was no longer using a style to dress up the fill. The DiscreteObjectKeyFrames now look like this:
<DiscreteObjectKeyFrame KeyTime="0:0:0.01" Value="{StaticResource IlluminatedBrush}" />
<DiscreteObjectKeyFrame KeyTime="0:0:0.85" Value="{StaticResource DimBrush}" />
My resources are much simpler without being wrapped in a style and IMO, more elegant. Also the brushes are defined before the animation in my final solution.
<UserControl.Resources>
<RadialGradientBrush x:Key="DimBrush" >
<GradientStop Color="#FF21471A" Offset="1"/>
<GradientStop Color="#FF33802F" Offset="0"/>
<GradientStop Color="#FF35932F" Offset="0.688"/>
</RadialGradientBrush>
<RadialGradientBrush x:Key="IlluminatedBrush">
<GradientStop Color="#FF215416" Offset="1"/>
<GradientStop Color="#FE38DA2E" Offset="0"/>
<GradientStop Color="#FE81FF79" Offset="0.688"/>
</RadialGradientBrush>
<!-- Storyboard code follows... -->
</UserControl.Resources>
Everything is now working as intended. The best assumption I can make is that styles are not freezable since they were the components that I removed and I no longer receive exceptions regarding a freezable that cannot be frozen.

Resources