Enable data binding in shared WPF resources inside a ResourceDictionary - wpf

I'm using the M-V-VM pattern in WPF and I have a background brush I'm going to be using rather often and I'd like to move it out in to a shared ResourceDictionary.
The only problem is the brush uses a color which it gets via Databinding to its hosted context.
Is there anyway I can move the brush out in to a ResourceDictionary and still have it find the value it needs?
The Brush:
<RadialGradientBrush>
<RadialGradientBrush.RelativeTransform>
<TransformGroup>
<ScaleTransform CenterX="0.5"
CenterY="0.5"
ScaleX="2.3"
ScaleY="2.3" />
<TranslateTransform X="-0.3"
Y="-0.3" />
</TransformGroup>
</RadialGradientBrush.RelativeTransform>
<GradientStop Color="{Binding Path=BackdropColor}"
Offset="1.2" />
<GradientStop Color="#FFFFFFFF"
Offset="-0.1" />
</RadialGradientBrush>
After re-factoring it out to a ResourceDictionary and adding a key, I called it as such:
<StackPanel Grid.Row="0"
Margin="0,0,0,0"
Orientation="Horizontal"
Background="{DynamicResource BackdropRadGradBrush}">
But this resulted in this output in the debugger:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=BackdropColor; DataItem=null; target element is 'GradientStop' (HashCode=16001149); target property is 'Color' (type 'Color')

I don't think that you can keep this Brush in your resource dictionary and use binding to pull in the color. Since the brush is only created once (which is why you want it in the resource dictionary in the first place), at the time of creation WPF doesn't know where it will be used, so it can't pull in the value for the color.
If the color were kept in Setings, for example, that would probably work - but I'm guessing that won't help you, because you probably want the color to change on each control that it is used on (otherwise, you could just hard code the color or it would already be in settings).
Maybe you could create a RadialGradientBrush subclass, and expose the first GradientStop color as a DependencyProperty? You could then create an instance of this subclass wherever you need it, and use binding to pull in the correct color there.

This is a little late, but take a look at using a StaticResource or a DynamicResource instead of a Binding - it will allow you to access another Resource. Not quite Binding, but it's better than nothing.

Related

Accessing user control properties in IvalueConverter | silverlight 4

I am trying to write a custom silverlight control which represents a water tank. It has two dependency properties, liquidLevel and liquidCapacity, and I want to pass both of these parameters into a converter along with a gradientBrush. The idea is the converter will perform a calcuation based on the liquidlevel and capacity and adjust the gradient stops on the brush to give the appearance of liquid rising and falling.
my tank has a "window" which is just a rectangle and gradientBrush, so far I have got this
My control template
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MoreControls;assembly=MoreControls"
xmlns:assets="clr-namespace:MoreControls.Assets">
<LinearGradientBrush x:Name="LiquidLevelTankWindow" StartPoint="0.469,0.997" EndPoint="0.487,0.013">
<GradientStop Color="#FF1010F1" Offset="0.0"/>
<GradientStop Color="#FF5555FB" Offset="0.55"/>
<GradientStop Color="#FFE4E4F1" Offset="0.6"/>
<GradientStop Color="#FFFAFAFD" Offset="1"/>
</LinearGradientBrush>
<assets:LiquidLevelBrushConverter x:Name="LiquidLevelBrushConverter" levelBrush="{StaticResource LiquidLevelTankWindow}"/>
<Style x:Key="Liquid" TargetType="local:LiquidTank">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:LiquidTank">
// * parts of the control here *
// the part of the control im interested in
<Rectangle x:Name="TankWindow" Width="32.3827" Height="64" Canvas.Left="27" Canvas.Top="42" Stretch="Fill" StrokeLineJoin="Round" Stroke="#FF000310"
Fill="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=LiquidLevel, Converter={StaticResource LiquidLevelBrushConverter}}" />
// * rest of control template *
Using the control in xaml (eventually I want to bind these properties)
<local:LiquidTank Style="{StaticResource Liquid}" LiquidCapacity="100" LiquidLevel="50"/>
and the converter
public class LiquidLevelBrushConverter : IValueConverter
{
public LinearGradientBrush levelBrush { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//I can access the liquid level parameter here
double level = 0;
double.TryParse(value.ToString(), out level);
GradientStopCollection gsc = levelBrush.GradientStops;
//some logic to alter gradient stops
return null;
}
Where I am now is that I want to access the second control property liquidCapacity from my converter so I can calculate the percentage of the tank that is full. I have tried passing liquidCapacity through as a converter parameter but if that's possible I cant figure out the syntax (I'm pretty new to silverlight).
Now that i've got this far im thinking I could have created a single dependancyproperty called fillpercentage, and perform this calculation in the eventual viewmodel, but with the way data will organised in there im pretty sure I will have a whole new set of challenges if I try this. It seems more manageable to me to be able to hardcode the liquidcapacity in the xaml, and bind the liquidlevel to the viewmodel. The view model will being pulling a bunch of values out of a database and into an observable dictionary, one of those being the liquidlevel, so it would be much easier I think to bind liquidlevel directly to the observable dictionary, rather than try and convert it to a "fillpercentage" in the view model before binding it.
Plus im pretty stubborn and in the interest of my education does anyone know if what I proposed to do is possible. If so, what the correct way to go about it ?
When you say that you
have tried passing liquidCapacity through as a converter parameter
I suspect you're trying to do something like
<Rectangle Fill="{Binding Path=LiquidLevel, ConverterParameter={Binding Path=LiquidCapacity} ...}" />
This won't work. You can't have a binding inside another binding.
Personally, I wouldn't use a converter for what you're trying to do. Instead, I'd add a method to adjust the gradient stops of the LinearGradientBrush to the code of the LiquidTank control. I'd then add PropertyChangedCallbacks to the LiquidLevel and LiquidCapacity dependency properties of the LiquidTank control and call this method from within these callbacks.

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.

Converter for Static Resources on Windows Phone

Hopefully this should be an easy one, i have a background of a rectangle i want to display as the phone accent colour or a disabled color based on a boolean in my view model.
I assume that converters are the way to go, but not sure of the syntax to get access to the static resources.
<Rectangle.Fill>
<SolidColorBrush Color="{StaticResource PhoneAccentColor}"/>
</Rectangle.Fill>
Grab the code for a generic BoolToValueConverter from this blog article:-
A Generic Boolean Value Converter
Also include in your code this specialisation for a converter to a Brush:-
public class BoolToBrushConverter : BoolToValueConverter<Brush> { }
Now add the converter to your Xaml like this:-
<Grid.Resources>
<local:BoolToBrushConverter x:Key="DisabledBrushConv"
FalseValue="{StaticResource PhoneAccentBrush}"
TrueValue="{StaticResource PhoneDisabledBrush}" />
</Grid>
Then in rectangle :-
<Rectangle Fill="{Binding Disabled, Converter={StaticResource DisabledBrushConv}}" ... />
This assumes the property in your view model is called Disabled.
You have two options:
Use a converter
Define a property on your viewmodel that returns a Brush based on the boolean value. I would prefer this solution because the performance hit of converters are more noticable on the phone than on the desktop.

XAML: Convert a Brush to a Color?

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.

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