Run Animation when Dependency Property Changes - wpf

I have created a UserControl with two dependency properties: Value and Color. The Color of the UserControl is dependent on the Value property. For example if Value=0 Color=Blue, Value=0.5 Color=Red and so on. This I have achieved using a custom converter that is bound to the Fill property, like so:
<Ellipse Name="dotForeground" Stroke="Transparent" StrokeThickness="1" Fill="{Binding ElementName=control1, Converter={StaticResource colorConverter}, Path=Value}"/>
Now what I need is that when the Value property changes from for example 0.0 to 0.5, which consequently also changes the Color property, I would want to create a ColorAnimation such that it fades from the previous color to the new color.
I would appreciate any help on this.

There are a few methods to do this, one would be to bind a brush to the Color property instead of using a converter:
<Ellipse Name="dotForeground" Stroke="Transparent" StrokeThickness="1">
<Ellipse.Background>
<SolidColorBrush Color="{Binding Color, ElementName=control1}" />
</Ellipse.Background>
</Ellipse>
Then start a ColorAnimation in your UserControl when the Value changes.
public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
public static readonly DependencyProperty ColorProperty = DependencyProperty.Register("Color", typeof(Color), typeof(MyUserControl), new UIPropertyMetadata(Colors.Red));
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(MyUserControl), new UIPropertyMetadata(0.0,ValueChanged));
private static void ValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var control = (MyUserControl)sender;
var anim = new ColorAnimation { To = Color.FromRgb((byte)control.Value, 128, 60), FillBehavior = FillBehavior.HoldEnd};
control.BeginAnimation(MyUserControl.ColorProperty, anim);
}

Related

Binding a Color to a DependencyProperty in UserControl

I have a UserControl that has a Grid with a Background property that is bound. All of my other bindings work as expected, but for some reason, the only color I get in my UserControl is the default value I set for the DependencyProperty.
Referencing the UserControl in MainWindow.xaml:
<controls:MyUserControl Title="{Binding Path=MyObjects[0].Title" MyControlColor="{Binding Path=MyObjects[0].Color}" />
Title shows up as expected but the color is unchanged.
MyUserControl code (I use MyControlColorBrush for the color source, which just converts MyControlColor to a SolidColorBrush. Code on down.):
<Grid Background="{Binding Path=MyControlColorBrush, RelativeSource={RelativeSource AncestorType=UserControl}}">
<TextBlock Text="{Binding Path=Title, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</Grid>
MyUserControl.xaml.cs code:
public Color MyControlColor
{
get { return (Color)GetValue(MyControlColorProperty); }
set { SetValue(MyControlColorProperty, value); }
}
public static readonly DependencyProperty MyControlColorProperty = DependencyProperty.Register("MyControlColor", typeof(Color), typeof(MyUserControl), new PropertyMetadata(Colors.Black));
And then a property that just converts the color to a SolidColorBrush:
public SolidColorBrush MyControlColorBrush
{
get { return new SolidColorBrush(MyControlColor); }
}
Any ideas on what I could be missing? If I check the value of MyControlColor, it's showing the right color, but the background of the Grid just isn't changing from Black.
The binding to MyControlColorBrush only happens once when your page is first loaded. Your binding to MyObjects[0].Color is causing your dependency property to update but there's nothing indicating to the rest of your app that MyControlColorBrush needs to be updated as well.
There are a few ways to achieve this, the easiest is probably to just create a read-only dependency property for your brush that you update whenever you detect a change in your color property (this is similar to how the Width/ActualWidth properties work). Your control will need a DP for the color:
public Color MyControlColor
{
get { return (Color)GetValue(MyControlColorProperty); }
set { SetValue(MyControlColorProperty, value); }
}
public static readonly DependencyProperty MyControlColorProperty =
DependencyProperty.Register("MyControlColor", typeof(Color), typeof(MyUserControl),
new PropertyMetadata(Colors.Black, OnColorChanged));
And then a read-only DP for the brush:
public Brush MyControlColorBrush
{
get { return (Brush)GetValue(MyControlColorBrushProperty); }
protected set { SetValue(MyControlColorBrushPropertyKey, value); }
}
private static readonly DependencyPropertyKey MyControlColorBrushPropertyKey
= DependencyProperty.RegisterReadOnly("MyControlColorBrush", typeof(Brush), typeof(MyUserControl),
new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.None));
public static readonly DependencyProperty MyControlColorBrushProperty = MyControlColorBrushPropertyKey.DependencyProperty;
And you'll update the brush whenever your color DP changes:
private static void OnColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as MyUserControl).MyControlColorBrush = new SolidColorBrush((Color)e.NewValue);
}
GUI elements in your custom control then bind to the read-only DP, e.g.:
<Grid Background="{Binding Path=MyControlColorBrush, RelativeSource={RelativeSource AncestorType=local:MyUserControl}}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />

Binding two dependency properties from two different usercontrols

I have created two separate usercontrols, they are meant to work together.
The first one is a simple usercontrol with a thumb attached to it, the thumb makes the control move around by dragging, this is simple and working.
XAML:
<Canvas>
<Thumb x:Name="Thumb" Width="15" Height="15" DragDelta="Thumb_DragDelta"/>
</Canvas>
Code-Behind: A dependency property called Position, when Setter is called it updates the usercontrol's margin.
public partial class ThumbPoint : UserControl
{
public Point Position
{
get { return (Point)GetValue(PositionProperty); }
set { SetValue(PositionProperty, value); this.Margin = new Thickness(value.X, value.Y, 0, 0); }
}
// Using a DependencyProperty as the backing store for Position. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PositionProperty =
DependencyProperty.Register("Position", typeof(Point), typeof(ThumbPoint), new PropertyMetadata(new Point()));
public ThumbPoint()
{
InitializeComponent();
Position = new Point(0, 0);
}
private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
Position = new Point(Position.X + e.HorizontalChange, Position.Y + e.VerticalChange);
}
}
The second UserControl is called StraightLine, its composed of a Line control
XAML:
<Canvas>
<Line x:Name="Line" Stroke="Gray" StrokeThickness="1"/>
</Canvas>
Code-Behind: A dependency property called StartPosition, when Setter is called it updates the Line X1 and Y1 (starting position of the line).
public partial class StraightLine : UserControl
{
public Point StartPosition
{
get { return (Point)GetValue(StartPositionProperty); }
set { SetValue(StartPositionProperty, value); Line.X1 = value.X; Line.Y1 = value.Y; }
}
// Using a DependencyProperty as the backing store for StartPosition. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StartPositionProperty =
DependencyProperty.Register("StartPosition", typeof(Point), typeof(StraightLine), new PropertyMetadata(new Point()));
public StraightLine()
{
InitializeComponent();
Line.X1 = 0;
Line.Y1 = 0;
Line.X2 = 300;
Line.Y2 = 200;
}
}
Here I am trying to bind them together on the mainwindow.xaml:
<Canvas>
<local:ThumbPoint x:Name="ThumbPoint"/>
<local:StraightLine StartPosition="{Binding Position, ElementName=ThumbPoint}"/>
</Canvas>
Desired effect: DependencyProperty StartPosition of the StraightLine should be updated.
Whats happening: It's not being updated so only the ThumbPoint is moving.
binding doesn't use common property wrappers for DP (public Point StartPosition), it uses SetValue() directly, so code in setter isn't invoked.
What is needed is propertyChangedCallback:
public Point StartPosition
{
get { return (Point)GetValue(StartPositionProperty); }
set { SetValue(StartPositionProperty, value); }
}
public static readonly DependencyProperty StartPositionProperty =
DependencyProperty.Register("StartPosition", typeof(Point), typeof(StraightLine), new PropertyMetadata(new Point(), OnStartPositionChanged));
private static void OnStartPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
StraightLine c = (StraightLine) d;
c.Line.X1 = c.StartPosition.X;
c.Line.Y1 = c.StartPosition.Y;
}
in ThumbPoint public Point Position property has the same issue but it works there because you use setter directly: Position = new Point()
Alternatively bind X1 and Y1 value in xaml:
<Canvas>
<Line x:Name="Line" Stroke="Gray" StrokeThickness="1"
X1="{Binding StartPosition.X, RelativeSource={RelativeSource AncestorType=StraightLine}}"
Y1="{Binding StartPosition.Y, RelativeSource={RelativeSource AncestorType=StraightLine}}"/>
</Canvas>
(or use ElementName instead of RelativeSource)

Bind to property of a control in another view

I have set up in a view the height of a control using dockpanels and alignments.
I want to use the computed size of this control as an input for another control in another view.
MainWindow
<StackPanel>
<local:View1 />
<local:View2 />
</StackPanel>
View1
<DockPanel>
...
<Button x:Name="myButton" />
...
</DockPanel>
View2 (where I want to bind the button's height to the first view)
<Button Height="{Binding Path=Button.Height, RelativeSource={RelativeSource AncestorType={x:Type local:View1}}}" />
But it does not work...
I am looking if possible for a xaml-only solution with binding...
You may want to try using dependency properties in order to achieve this. Here is a sample based on your case:
View1:
<DockPanel>
<Button x:Name="myButton" Content="Button in view1" FontSize="32"/>
</DockPanel>
View1 codebehind. Notice that we handle loaded event in order to get the actual height of the button and to assign its value to the DependencyProperty we created:
public static readonly DependencyProperty ButtonHeightProperty = DependencyProperty.Register(
"ButtonHeight", typeof (double), typeof (View1), new PropertyMetadata(default(double)));
public double ButtonHeight
{
get { return (double) GetValue(ButtonHeightProperty); }
set { SetValue(ButtonHeightProperty, value); }
}
public View1()
{
InitializeComponent();
}
private void View1_OnLoaded(object sender, RoutedEventArgs e)
{
ButtonHeight = myButton.ActualHeight;
}
Then in view2 we bind the button height to another dependency property in that user control:
And in view2 codebehind:
public static readonly DependencyProperty ButtonHeightProperty = DependencyProperty.Register(
"ButtonHeight", typeof (double), typeof (View2), new PropertyMetadata(default(double)));
public double ButtonHeight
{
get { return (double) GetValue(ButtonHeightProperty); }
set { SetValue(ButtonHeightProperty, value); }
}
public View2()
{
InitializeComponent();
}
Finally the mainWindow xaml looks like this:
<StackPanel>
<local:View1 x:Name="View1"/>
<local:View2 ButtonHeight="{Binding ElementName=View1,Path=ButtonHeight}"/>
</StackPanel>
And the output:
Hope this helps

How to expose internal control's property to its parent UserControl in WPF

I have a DialogPrompt UserControl that will have an Image and a TextBlock. Here is the template:
<UserControl>
<Button x:Name="_OkButton" Content="OK"/>
<DockPanel >
<Image/>
<TextBlock x:Name="_DialogTextBox" />
</DockPanel>
</UserControl>
How do I expose Source property of the Image and Text property of the TextBlock inside my UserControl?
I would create two DependencyProperties, one for the Text and one for the Image Source.
The Image Source DependencyProperty will automatically set the inner Image control's source whenever it is updated. Similarly, the Text DependencyProperty will be setting the Text of the inner TextBlock control as well.
Here is the setup:
public partial class MyUserControl : UserControl
{
#region ImageSource
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register
(
"ImageSource",
typeof(Uri),
typeof(MyUserControl),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnImageSourceChanged))
);
public Uri ImageSource
{
get { return (Uri)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
#endregion ImageSource
#region Text
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register
(
"Text",
typeof(string),
typeof(MyUserControl),
new FrameworkPropertyMetadata("")
);
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
#endregion Text
public MyUserControl()
{
InitializeComponent();
}
private static void OnImageSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var myUserControl = sender as MyUserControl;
if (myUserControl != null)
{
myUserControl.ImageSource.Source = new BitmapImage((Uri) e.NewValue);
}
}
}
Whenever the Image Source changes, this will automatically update the source of the inner Image control. Note, we need to do some conversion here since the Image control itself uses an ImageSource type.
XAML can then be updated to:
<UserControl x:Name="ControlName">
<Button x:Name = "OkButton" Content="OK"/>
<DockPanel >
<Image x:Name = "MyImage" />
<TextBlock x:Name = "DialogTextBox" Text="{Binding ElementName=ControlName, Path=Text}"/>
</DockPanel>
</UserControl>
Here, the inner TextBlock control simply binds to the Text DependencyProperty of the parent (the main UserControl).
In your code behind, add 2 DependencyProperties and bind them to your Image Source and to your TextBlock Text.
Here is a tutorial on how to use and create Dependency Properties :
http://www.wpftutorial.net/dependencyproperties.html
For your binding in your xaml, here is an example :
<Image Source="{Binding YourProperty, RelativeSource={RelativeSource FindAncestor, AncestorType=YourUserControl}}/>

DependencyProperty and UserControl in WPF

I created a UserControl with a Label and a Rectangle inside 2 grid rows.
I added the property
public string SetText
{
get
{
return (string)GetValue(mLabel.ContentProperty);
}
set
{
SetValue(mLabel.ContentProperty, value);
}
}
Usage of the property
<local:PlayerMiniImage SetText="Player 1" ...
When I used the property, the font of the label changed and the rectangle disappeared
Have any idea?
If you define a UserControl...
<UserControl x:Class="...">
<Border>
<!-- ... -->
</Border>
</UserControl>
Then everything inside it, here a Border, is the Content, hence if you set the ContentProperty everything will be replaced.
To set the label content create a new DP:
public static readonly DependencyProperty LabelContentProperty =
DependencyProperty.Register("LabelContent", typeof(object), typeof(MyUserControl), new UIPropertyMetadata(null));
public object LabelContent
{
get { return (object)GetValue(LabelContentProperty); }
set { SetValue(LabelContentProperty, value); }
}
and bind the label to it:
<Label Content="{Binding LabelContent, RelativeSource={RelativeSource AncestorType=UserControl}}"/>

Resources