I have the following styles in WPF to draw and color a box, which is a custom control with various PART_Name items defined in a ResourceDictionary:
<ResourceDictionary>
.
.
.
<Brush x:Key="BoxStroke">#FFD69436</Brush>
<LinearGradientBrush x:Key="BoxBrush" StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#FAFBE9" Offset="0" />
<GradientStop Color="Green" Offset="1" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<Style x:Key="BoxStyle" TargetType="Path">
<Setter Property="Fill" Value="{DynamicResource BoxBrush}"/>
<Setter Property="Stroke" Value="{DynamicResource BoxStroke}"/>
</Style>
<Style x:Key="Box" TargetType="Path" BasedOn="{StaticResource BoxStyle}">
<Setter Property="Data" Value="M 0,0 H 60 V40 H 0 Z"/>
</Style>
.
.
.
</ResourceDictionary>
My question is how can I access the GradientStop color property for the brush?
For instance if the user clicks on the box turn it from "Green" to "Blue".
I've got all of the appropriate code to handle user interaction, I am just stumped on how to change the color of the brush.
The easiest way to do this would be to use databinding instead. Bind the view to an object which has a property that contains the value of the colour you want to change. Then bind that property value to the gradient. When the button is clicked, modify that property and the databinding mechanism will update the colour on screen for you. Just make sure you either implement INotifyPropertyChanged or make the property a dependency property.
Good luck!
Once you can access the brush in code, you will just need to assign it a Color value. For example the System.Windows.Media.ColorConverter class will translate hex/web colors into System.Windows.Media.Color values.
Here's a sample, hopefully this is the general idea of what you're asking about:
System.Windows.Media.LinearGradientBrush gb = new System.Windows.Media.LinearGradientBrush();
gb.GradientStops[0].Color = (Color)ColorConverter.ConvertFromString("#FF00FF00");
Related
Code:
<telerik:RadMenuItem Header="Home" x:Name="radMenuHome" />
How to apply a color for a radmenuitem if it is selected? I want to have the selected menu item to be in gray color. Thanks.
I have no idea about the RadMenuItem class, but I can only assume that it extends the MenuItem class. If this is the case, then this answer should still make sense. The WPF MenuItem has what I would call a bug in it... this short example demonstrates this:
<Menu>
<Menu.Resources>
<Style TargetType="{x:Type MenuItem}">
<Style.Triggers>
<Trigger Property="MenuItem.IsHighlighted" Value="True">
<Setter Property="MenuItem.Foreground" Value="Red" />
<Setter Property="MenuItem.Background" Value="Black" />
</Trigger>
</Style.Triggers>
</Style>
</Menu.Resources>
<MenuItem Header="One" />
<MenuItem Header="Two">
<MenuItem Header="Three" />
</MenuItem>
</Menu>
As you can see, the MenuItem.Foreground correctly changes to Red as you mouse over the MenuItems, but the Background does not. This is because of the way that the default MenuItem Controltemplate was defined. You can find this default template in the Menu Styles and Templates page on MSDN.
So the correct way to achieve what you want is to define a new ControlTemplate for the MenuItem that is based on the default one from the linked page. To make it more tricky for you, you'll actually find four ControlTemplates in the default one... these are for the TopLevelHeader, TopLevelItem and SubmenuHeader and SubmenuItem so you can actually style top level and child MenuItems differently.
Anyway, looking at these ControlTemplates, you should see the following Trigger (from the linked page):
<Trigger Property="IsHighlighted"
Value="true">
<Setter Property="Background"
TargetName="Border">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{StaticResource ControlLightColor}" />
<GradientStop Color="{StaticResource ControlMouseOverColor}"
Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
Changing the Setter.Value here should have the effect that you want, but you'd need to do it all the ControlTemplates that you find it in. My last tip for you is that when defining new ControlTemplates, you should start with the default XAML and then edit and make your changes only once you have seen it working and the control(s) look as they should do by default. Also, keep running the project to see if it still works for every few changes that you make. Good luck.
UPDATE >>>
I just found the WPF XAML MenuItem Styles page of Jim Nuzzi's blog, which has further explanations of customising the WPF Menu and MenuItem classes for you.
Try this (only for web/.NET scenario):
protected void RadMenu1_ItemDataBound(object sender, Telerik.Web.UI.RadMenuEventArgs e)
{
if (e.Item.NavigateUrl == Request.Url.LocalPath)
{
e.Item.ForeColor = System.Drawing.Color.White;
e.Item.BackColor = System.Drawing.Color.Gray;
}
}
Note: add OnItemDataBound="RadMenu1_ItemDataBound" to your Telerik:RadMenu tag
I have a ComboBox with a custom theme I wrote, and I get the error message "Cannot animate (0).(1) on an immutable object." This specifically happens when I set its selectedindex after the user selects one of the options in the combobox.
Doing some research online, I found that this is a common issue with databound items or dynamic resources. I'm not using any databound resources, but what I think is happening is since the combobox is collapsed, it tries to set the state of a button that doesn't exist. I narrowed it down to this code:
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="Background" Value="{DynamicResource spPressedStateBrush}" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="Background" Value="{DynamicResource spOverStateBrush}" />
</Trigger>
</ControlTemplate.Triggers>
Which depends on these Dynamic Resources:
<LinearGradientBrush x:Key="spOverStateBrush" StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#2C8CBF" Offset="0" />
<GradientStop Color="#2793BF" Offset="0.5" />
<GradientStop Color="#2483BF" Offset="0.5001" />
<GradientStop Color="#2C8CBF" Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="spPressedStateBrush" StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#0C6C9F" Offset="0" />
<GradientStop Color="#07739F" Offset="0.5" />
<GradientStop Color="#04639F" Offset="0.5001" />
<GradientStop Color="#0C6C9F" Offset="1" />
</LinearGradientBrush>
So I'm pretty sure those dynamic resources are the culprit, but how would I solve this problem?
After painstakingly trying to debug it by comparing the code to the original Control Template, I figured out that transporting all of the resources that my control uses into the file itself, and replacing all DynamicResources with StaticResources and that fixed the bug I was having.
Be sure that both this two (spOverStateBrush or spPressedStateBrush) Brushes are not used in different place in your code.
In order to be animatable, the Background Brush (spOverStateBrush or spPressedStateBrush) of the Border must be mutable, which the default value isn't.
If you use one of these two Brushes in another place, You should assign a new LinearGradientBrush before animating something like this code:
LinearGradientBrush gradient = new LinearGradientBrush();
gradient.StartPoint = new Point( 0.5, 0 );
gradient.EndPoint = new Point( 0.5, 1 );
gradient.GradientStops.Add(new GradientStop(Colors.Black, 0));
gradient.GradientStops.Add(new GradientStop(Color.FromArgb(100,69,87,186), 1));
Border.Background = gradient;
I have a simple style that I'm trying to apply to all of the buttons in my app:
<LinearGradientBrush x:Key="ButtonBackgroundBrush">
<GradientStop Color="Yellow" Offset="0" />
<GradientStop Color="Red" Offset="1" />
</LinearGradientBrush>
<SolidColorBrush Color="Purple" x:Key="ButtonForegroundBrush" />
<SolidColorBrush Color="LimeGreen" x:Key="ButtonBorderBrush" />
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="{StaticResource ButtonBackgroundBrush}" />
<Setter Property="Foreground" Value="{StaticResource ButtonForegroundBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource ButtonBorderBrush}" />
</Style>
I have this in my app.xaml file, so it is available to everything in my app. If I put a button on a page, the styles does not apply. If I add an "x:key" to the style and then add that style name to the button, the style is applied. Therefore, I know the style is written correctly and is within scope of the button. Does anyone have any ideas as to why the style does not automatically apply to the buttons as it should if I leave off the x:key from the style?
Must have been a VS2010 glitch. I added a new page, put styles in the app.xaml file and they applied to the controls on the new page. Opened up the old page and, as if by Microsoft magic, the styles started applying. Truly strange.
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.
I working on a XAML style for my controls.
The code below is for setting the color of a stackpanel. It works fine but there is something else I want to do. If the trigger is activated I want to set the font color for all child items inside the stackpanel.
At the moment I only have textblocks inside the stackpanel and I know I could easily create a separate style for a textbock. But if this style is triggered it will only affect ONE and not ALL textblocks.
But I want to change all items inside the stackpanel as soon as I got a mouseover trigger for the panel.
Is this even possible in XAML or do I have to code a regular event?
<Style x:Key="XStack" TargetType="StackPanel">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="SkyBlue" Offset="6"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<!-- Trigger-->
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True" >
<Setter Property="Background" Value="SkyBlue"/>
</Trigger>
</Style.Triggers>
</Style>
Add this to your trigger:
<Setter Property="TextElement.Foreground" Value="Blue"></Setter>
Like Olle said, you can set the attached property TextElement.Foreground to a custom value. For a child control/UI node in the visual tree, if the property is not set, WPF will walk up the UI hierarchy till it finds a value and use it. This means that all child controls can share a property value defined at the parent level.
This should work for all TextBlocks... however if your StackPanel contained a TextBox, its text color wouldn't be affected. It uses the Foreground property from the Control base class... So be sure to test it out with all possible child element types.