XAML: Convert a Brush to a Color? - wpf

I am creating a custom control with two text colors, ColorA and ColorB. ColorA is wired to the Foreground property of the control, and ColorB is wired to a custom dependency property called ForegroundAlt. Both properties are Brush objects. The control's XAML gets the property values using this markup:
<SolidColorBrush x:Key="BrushA" Color="{Binding Path=Foreground, RelativeSource={RelativeSource TemplatedParent}}" />
<SolidColorBrush x:Key="BrushB" Color="{Binding Path=ForegroundAlt, RelativeSource={RelativeSource TemplatedParent}}" />
I need to animate sme text between the two colors in the control template, and that's where I am running into problems.
Normally, I would simply create a data binding to each Brush.Color property, like this:
To="{Binding Source={StaticResource BrushB}, Path=Color}"
But that won't work here. It turns out that you can't use bindings on an animation inside a control template.
As a workaround, I would like to create a pair of Color resources to go along with the Brush resources:
<Color x:Key="ColorA" ??? />
<Color x:Key="ColorB" ??? />
Each Color resource should have the color of its corresponding brush. I could then reference the colors as static resources, and avoid having to data bind from within the animation.
So, here are my questions:
-- How would I declare the Color resources?
-- Is there a simpler way to get the job done?
Thanks for your help.

If I've understood this correctly, what you are trying will not work. Even if you define the Colors as resources, you will still have to bind them to the brush resources and you are back to square one!
One solution is to do it in code behind rather than in the template. Since its a custom control you are building its should be pretty straightforward to add it in th code behind without screwing up the design.

Related

Issue with WPF styles

I wonder if someone can help, I am designing a custom WPF window for an application i am working on and I have an issue with the Min, Max and Close buttons. I have designed a ControlTemplate for the 3 buttons and they are in a StackPanel with Vertical orientation. In my base style I have the following
<Style x:Key="BaseWindowButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value={Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}, Path=(TextElement.Foreground)}" />
<Setter Property="Background" Value="Transparent" />
...
</Style>
Have also tried setting the Foreground to a specific color such as #FF000000 and nothing displays
I have a style that inherits this style for the buttons but does not change the foreground or background.
My problem is that the button content does not display, the button displays and the IsMouseOver trigger fires which changes the background but the textual content never displays.
The Min button style is
<Button x:Name="PART_Min">
<Path Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
Data="F1M0,6L0,9 9,9 9,6 0,6z"
SnapsToDevicePixels=True" />
</Button>
I am at a loss as to why the content does not display so would appreciate your thoughts.
The Visual Tree is below, I have examined this and identified the Foreground values
Window (Foreground: #FF000000)
Grid
AdornerDecorator
Grid
ContentControl (Foreground: #FF000000)
StackPanel
Button (Foreground: #FF000000)
Grid
ContentControl (Foreground: #FF000000)
But like I said above I have removed the binding and specified a physical value and still do not get the content displaying
Use ContentPresenter instead of ContentControl in your button's template. (You should include the button's control template in a question like this... it's potentially highly relevant.)
As I too was a beginner with WPF, and trying to understand things was a bit of a learning curve, I would like to offer a few previous links posted out here.
First a simple style for creating simple label and having all labels as a default take on this format without explicit style assignments.
Another sample showing creation of a custom button. This is where I went step by step to create a custom class and apply a style to it to show/hide image on a button.
Maybe the button link and style declaration will help you find your button coloring issues too.

Can you apply an opacity to a system-defined brush?

I know the WPF brush class has an Opacity property. We have a need to use a system-defined brush but with half the opacity. We'd like to do something like this (this is obviously fake code)...
<Border Background="{DynamicResource {x:Static SystemColors.HighlightBrushKey}, Opacity=0.5}" />
We of course can't change the opacity on a system-defined brush directly because that would mess up everywhere it's used.
What we'd like to do is to somehow define a converter which we take one brush and returns a copy of it with the opacity changed, but since this isn't a binding, we don't know where/how to apply a converter. Plus, if it's a dynamic resource, we need to make sure it tracks changes.
We also can't simply set the opacity on the Border directly either as then all of its children also have the same reduced opacity.
Our current work-around is instead of putting the content directly in the border, we put the border and its contents as siblings in a grid, then we do set the opacity on the border. Since the content is now on top of, instead of inside the border, it isn't affected by the opacity. It just means we've added extra stuff into the visual tree which is annoying, but it does work. It would be much better if we could simply adjust the opacity of a (copy of a) system brush right in the XAML.
A bit late, but for the sake of others...
You can create derivative solid color brushes with new opacities. To do this you simply borrow the color from the original brush used as the binding source, then set a new opacity.
<SolidColorBrush Color="{Binding Color, Source={StaticResource blue-light}}" Opacity="0.5" />
Maybe you could try creating a new brush based on the system color in stead of using the system brush directly, like this:
<Border>
<Border.Background>
<SolidColorBrush
Color="{DynamicResource {x:Static SystemColors.HighlightColorKey}}"
Opacity="0.5" />
</Border.Background>
</Border>
Well, I think I found it! Thanks to other work I've done, I came up with a DynamicResourceBinding concept (StaticResourceBinding too!) which you can use a converter to transform the brush in any way you want.
Here's a link to that page here on StackOverflow where I do this for both Dynamic and Static resources...
Post 33816511: How to create a DynamicResourceBinding

Static resource array binding only works for the first time

I created a custom control which inherits from toolbar.
I would like that the default control template of the toolbar will contain a couple of default buttons.
In order to achieve this, I created a static array to hold the button list:
<x:Array x:Key="toolbarButtons" Type="{x:Type ToggleButton}">
<ToggleButton Content="Bold"
Command="{x:Static ns1:EditingCommands.Bold}"
CommandTarget="{Binding}"
IsChecked="{Binding IsBold, Mode=TwoWay}"/>
<ToggleButton Content="Italic"
Command="{x:Static ns1:EditingCommands.Italic}"
CommandTarget="{Binding}"
IsChecked="{Binding IsItalic, Mode=TwoWay}"/>
</x:Array>
The toolbar control has datacontext which is bounded to a text editor which includes all of the command bindings and the boolean dependency properties (IsBold, IsItalic).
I set the Toolbar ItemSource to use the array like this:
<Setter Property="ItemsSource" Value="{StaticResource toolbarButtons}"/>
Now, when I open a window who hosts the toolbar for the first time on a given run, everything works great.
The problem is, when I close the window, and reopen it, the button bindings stop working (IsCheked property stops being connected to the dependency property).
I used snoop to check the bindings, and it says that the value of IsChecked is local, which means the binding is ignored.
I suspect that the problem is my array is a static resource, so the toolbar uses the same instance from time to time, and this somehow ruins the binding.
My question is how to solve this, or maybe should I use a different approach in order to achieve default buttons for my toolbar?
I think the problem is that when you declare your array in XAML, it's only instantiated once. So, the second toolbar (and on) are trying to use the exact same objects. When it's reused, your bindings are likely being overwritten.
Have you tried adding x:Shared="False" to the array declaration?
<x:Array x:Key="toolbarButtons" x:Shared="False" Type="{x:Type ToggleButton}">

Setting resource styles dynamically through binding in WPF

I am trying to make the color scheme of my application dynamic so that I can have a color value in a property(hopefully coming from the database) that determines the color scheme of my application.
I have a Resources.xaml file where I set my colors and styles for the application, which I then use throughout all my controls and windows. I would like to bind the Color of a SolidColorBrush in the resources file to a property in my ViewModel(s) so that this color can change based on the current application value. Here is what I have so far, but it isn't working so I must be missing something.
Code in the Resources.xaml file:
<SolidColorBrush x:Key="ApplicationMainBackgroundBrush" Color="{Binding Path=MainApplicationColor, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type ApplicationArchitecture:ViewModelBase}, Mode=FindAncestor}, FallbackValue=CornflowerBlue}"/>
Code in the MainWindow.xaml file:
<Grid Grid.Row="0" x:Name="gridControl" Background="{DynamicResource ApplicationMainBackgroundBrush}">
The DataContext of my MainWindow.xaml is a class called ApplicationViewModel, which inherits from ViewModelBase, which has a property "MainApplicationColor" returning the string "Teal" to change the color of that SolidColorBrush from it's FallbackValue. I'm hard coding the color for now, but this is where I would like to get my value from the database in the future. The color is currently not changing, so I'm assuming there is something wrong in my binding source as it is clearly not working like I think it should.
Thanks,
Klara
The problem seems to be your SolidColorBrush.Color property's Binding.
There the ancestor type should be ApplicationArchitecture:MainWindow and not ApplicationArchitecture:ViewModelBase.
The Path should include the DataContext in it.
Like this....
<SolidColorBrush x:Key="ApplicationMainBackgroundBrush"
Color="{Binding Path=DataContext.MainApplicationColor,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ApplicationArchitecture:MainWindow},
Mode=FindAncestor},
FallbackValue=CornflowerBlue}"/>
Let me know if this helps.

WPF: Setting named color to resource?

I am modifying the control template of the WPF Calendar control to change the color of text and the Previous and Next arrows on the control. I want to set the color to a local SolidColorBrush resource called MyTextBrush.
The Previous and Next buttons have separate control templates, and each draws a Path object for its button's arrow. Here is the relevant markup for the Previous button:
<Path Margin="14,-6,0,0" Height="10" Width="6" VerticalAlignment="Center" HorizontalAlignment="Left" Stretch="Fill" Data="M288.75,232.25 L288.75,240.625 L283,236.625 z">
<Path.Fill>
<SolidColorBrush x:Name="TextColor" Color="#FF333333" />
</Path.Fill>
</Path>
Note that the color is named TextColor, using the x:Name property.
Here is my problem: The x:Name property is required--WPF throws an exception if it is missing. That means I can't simply replace the entire brush with a reference to the MyTextBrush resource, because I would lose the x:Name value. So, how do I reference MyTextBrush, while still retaining the x:Name property for the brush in this particular control template?
Thanks for your help.
So, how do I reference MyTextBrush,
while still retaining the x:Name
property for the brush in this
particular control template?
Regarding this problem it sounds like you are using a dodgy/fragile template. What control template is it?
If you have full source control of the template, remove references to the named element (most likely in a storyboard). They must be animating the brush for some reason.
The other option might be to just create another unused brush within your template (Perhaps on a hidden element) with the correct name to keep the template happy.
Lastly, you can try adding the x:Name onto the brush in the shared RD, but this is quite complicated and not sure its worth it!
Two more potential solutions:
Try binding just the Color property of the SCB... that should work as its a DP
Change the template animations so they do not use a named brush, but instead use a named parent then access the brush via the TargetProperty e.g. Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="myNamedParent"
The best solution seems to be to break the Color property out to its own tag, and use a resource reference for that. Here is what it looks like:
<!-- FS: Changed template brush color -->
<SolidColorBrush x:Name="TextColor">
<SolidColorBrush.Color>
<StaticResource ResourceKey="FsTextColor" />
</SolidColorBrush.Color>
</SolidColorBrush>

Resources