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.
Related
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.
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.
I have a rectangle with its Visibility property bound to the view-model. For (apparent) performance reasons, I'm setting the DataContext in the page's Loaded event. This rectangle should be collapsed by default. Unfortunately, with this "late-context" pattern, the rectangle shows for a few fractions of a second.
Is there a no-code way to make it collapsed by default?
If no, I could simply set the property to Collapsed and bind it in the Loaded event, but I have many such rectangles in my app.
Could I implement a ContentControl that is collapsed until loaded? Is that second idea too far-fetched?
#Martin - have you tried setting the FallbackValue in your binding?
I don't have the VS at hand to check it, but I think that if your setup is like (sorry for 'errors', thats just a draft:
<rectangle visibility={Binding mydata.somthing.someVisibilityProperty} />
then, if binding at Loaded, your control may "flash" with the default Visibility.Visible value. Your binding fails at the first render, because there's no data bound yet. If so, then just setup the fallbackvalue:
<rectangle visibility={Binding mydata.somthing.someVisibilityProperty, FallbackValue=Collapsed} />
this will cause the binding to return "Visibility.Collapsed" whenever it fails to read from the source.
In case of
<Rectangle Visibility="{Binding TipRoundingHasError, Converter={StaticResource VisibilityConverter}}">
that you have presented in your second answer, it would basically look like:
<Rectangle Visibility="{Binding TipRoundingHasError, FallbackValue=DEFAULTVALUE, Converter={StaticResource VisibilityConverter}}">
but I dont recall now, whether yout Converter will be invoked on the FallbackValue or not. That means, that I cant tell you now, if you should substitute DEFAULTVALUE for "Collapsed" or rather for "False". But I think you will test&choose the correct one in an instant.
For more examples on Fallback, try looking at the BindingBase.FallbackValue - there's nice example with a custom binding class (yeah, not only converters may be custom:) )
I made an AppearingControl who's implementation defies calling this "coding" since it's so simple:`
public class AppearingControl : ContentControl
{
public AppearingControl()
{
if( !System.ComponentModel.DesignerProperties.IsInDesignTool )
{
this.Visibility = System.Windows.Visibility.Collapsed;
this.Loaded += new RoutedEventHandler( AppearingControl_Loaded );
}
}
void AppearingControl_Loaded( object sender, RoutedEventArgs e )
{
this.Loaded -= new RoutedEventHandler( AppearingControl_Loaded );
this.ClearValue( AppearingControl.VisibilityProperty );
}
}
I can use the control this way:
<slim:AppearingControl HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch">
<Rectangle Visibility="{Binding TipRoundingHasError, Converter={StaticResource VisibilityConverter}}">
<Rectangle.Fill>
<SolidColorBrush Color="#FFFF4040" Opacity="0.5"/>
</Rectangle.Fill>
</Rectangle>
</slim:AppearingControl>
Can someone confirm I didn't just reinvent the wheel, or worse, use a bazooka to kill a fly?
Thanks.
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.
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.