wpf converter : setting multiple properties - wpf

I am used to using converters that return a value per property, such as Foreground color.
Is it possible to have a converter that works with multiple properties?
such as: Foreground, Background, Font-Weight, Font-Size
How can I create one converter (or less than 4) that could set multiple properties?

No, converters aren't designed for that. You could possibly go down the attached behaviour route and set the properties, based on a bound dependency property (I assume) on attach?
Edit: behaviours are part of the Blend SDK, the basic structure of what you want is:
public class MyBehavior : Behavior<TextBlock>
{
//// <-- Dependency property here
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.Foreground = CalculateForeground();
this.AssociatedObject.Background = CalculateBackground();
// etc..
}
private Brush CalculateForeground()
{
// Do some calculations based on the dependency property
}
private Brush CalculateBackground()
{
// Do some calculations based on the dependency property
}
protected override void OnDetaching()
{
base.OnDetaching();
// You might want to reset to default here, or just do nothing
}
}

Related

Implement mouse interaction on custom control based on FrameworkElement

This is just example to learn from.
I want create custom control with completely different looks. Hence, according to https://learn.microsoft.com/en-us/dotnet/framework/wpf/controls/control-authoring-overview, I derive from FrameworkElement
and override OnRender method, also OverriderMesure and ArrangeOverride if needed.
Now I want implement mouse interaction, for example: on hover change color from red to blue. How I should do it?
public class Box : FrameworkElement
{
private static Color defaultColor = Colors.Red;
public static DependencyProperty ColorProperty = DependencyProperty.Register("Color", typeof(SolidColorBrush), typeof(Box),
new FrameworkPropertyMetadata(new SolidColorBrush(defaultColor), FrameworkPropertyMetadataOptions.AffectsRender));
public SolidColorBrush Color
{
get { return (SolidColorBrush)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
static Box()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Box), new FrameworkPropertyMetadata(typeof(Box)));
}
protected override void OnRender(DrawingContext drawingContext)
{
// It's just example, I know shape is wayyy too simple to involve custom render.
drawingContext.DrawRectangle(Color, null, new Rect(0, 0, ActualWidth, ActualWidth));
}
protected override void OnMouseEnter(MouseEventArgs e)
{
Color = new SolidColorBrush(Colors.Blue); // Set to color
}
protected override void OnMouseLeave(MouseEventArgs e)
{
Color = new SolidColorBrush(defaultColor); // Back to default
}
protected override Size MeasureOverride(Size constraint)
{
...
}
protected override Size ArrangeOverride(Size finalSize)
{
...
}
}
So far I deduce:
Normally, if I had derive from Control, I would have use VSM for this. Unfortunately VSM isn't available until ControlTemplate hierarchy tree, so controls which have Template property. So if I decided to draw my control by myself I need use for this Routed Events, in this particular example OnMouseEnter(MouseEventArgs), OnMouseLeave(MouseEventArgs) and some dependency property, like code above.
It this right approach? Please remember it's for learning purpose so FrameworkElement as base is obligatory.
I can see some drawbacks, becouse If we want control onHover color (in code above is harcoded to blue) I need mess around with code behind, or create another dependency property for this.
Unfortunately VSM isn't available until ControlTemplate hierarchy tree, so controls which have Template property.
This it not true.
You can use VSM normally, with some small changes. Read: https://msdn.microsoft.com/en-us/library/system.windows.visualstatemanager(v=vs.110).aspx#Examples. Take a closer look on example.

Add Dependency Property to existing .NET class

IN a WPF project I have a bunch of controls in which I would like to be able to set individual Margin properties and conserve the other values. So, I would like to avoid setting the complete margin to a new Thickness (Margin="0,5,0,15"). Because many margin's are set from styles etc. But in individual cases I would like to deviate from the generic styles for certain controls.
I thought, why not register a couple of new dependency properties on the .NET class FrameWorkElement like so (for example only MarginLeft is shown):
public class FrameWorkElementExtensions: FrameworkElement
{
public static readonly DependencyProperty MarginLeftProperty = DependencyProperty.Register("MarginLeft", typeof(Int16?), typeof(FrameworkElement), new PropertyMetadata(null, OnMarginLeftPropertyChanged));
public Int16? MarginLeft
{
get { return (Int16?)GetValue(MarginLeftProperty); }
set { SetValue(MarginLeftProperty, value); }
}
private static void OnMarginLeftPropertyChanged(object obj, DependencyPropertyChangedEventArgs e)
{
if (obj != null && obj is UIElement)
{
FrameworkElement element = (FrameworkElement)obj;
element.Margin = new Thickness((Int16?)e.NewValue ?? 0, element.Margin.Top, element.Margin.Right, element.Margin.Bottom);
}
}
}
But this property doesn't come available in code-behind or in XAML. I can understand it somehow, because this dummy class is never instantiated or whatsoever. Tried to make it a static class but then you can't derive from FrameWorkElement (which I need for the GetValue and SetValue methods).
I couldn't find any resource on the net that treats the more generic question: Can you add dependency properties to exiting .NET classes?
Any help / wise advice is appreciated.
BTW: a solution for changing only one component of a Margin (Thickness) is also appreciated ;)
If you want to define a property to be set on an object that you do not own then you want to define an attached property in which case you would use the RegisterAttached method instead of Register. Also you would define the property as static get/set methods and not as an instance property since this would not be set on an instance of your object but on some unknown frameworkelement. The help topic from the link shows an example. The links in the other comments also provide more information and examples.
If you want change only one component of a margin use in xaml Margin="1,2,3,4", where 1 - left, 2 - top, 3 - rigth, 4 - bottom

Triggering an animation from an event using MVVM

I seem to have reached some sort of MVVM breaking point here.
I would like for a control to have its opacity animated for half a second (DoubleAnimation from 0.5 to 1.0) when the underlying view model object have its "Status" property changed. I achieved this at first using a DataTrigger but since I haven't found a way to react to ANY change, just a given value, I had to always flip the VM objects "Status" property to a special "pending" value before setting it to its intended value. (Is there a way to react to ANY change btw?)
This was hacky so I started fiddling with EventTriggers instead...
This is what I've tried so far:
Using a normal EventTrigger
This seems to require a RoutedEvent but that, in turn, requires that my underlying view model object inherits from DependencyObject.
Using i:Interaction.Triggers
That way I can listen to and react to normal .NET events but I haven't found a way to start a StoryBoard using that approach.
Using i:Interaction.Triggers and writing a Behavior
This experiment fell short on the fact I found no way to attach my custom behavior to its associated control.
This is what the XAML looked like:
<cc:MyControl>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Updated">
<i:Interaction.Behaviors>
<cv:OpacityBehavior Duration="0:0:0:5" />
</i:Interaction.Behaviors>
</i:EventTrigger>
</i:Interaction.Triggers>
And here's the custom behavior:
class OpacityBehavior : Behavior<MyControl>
{
public Duration Duration { get; set; }
protected override void OnAttached()
{
base.OnAttached();
var animation = new DoubleAnimation(0.5, 1, Duration, FillBehavior.HoldEnd);
var associatedObject = lookupVisualParent(this);
associatedObject.BeginAnimation(UIElement.OpacityProperty, animation);
}
}
That didn't work because the XAML parser required it to be attached directly to "MyControl" but I need to attach it to the event trigger. I then tried this approach:
class OpacityBehavior : Behavior<DependencyObject>
{
public Duration Duration { get; set; }
protected override void OnAttached()
{
base.OnAttached();
var animation = new DoubleAnimation(0.5, 1, Duration, FillBehavior.HoldEnd);
var associatedObject = lookupVisualParent(this);
associatedObject.BeginAnimation(UIElement.OpacityProperty, animation);
}
private UIElement lookupVisualParent(DependencyObject dObj)
{
if (dObj is UIElement)
return (UIElement) dObj;
if (dObj == null)
return null;
return lookupVisualParent(LogicalTreeHelper.GetParent(dObj));
}
}
This failed on the fact that lookupVisualParent doesn't work. The logical parent of the behavior is always null.
It strikes me this should be a fairly common task? Is there a good solution to this problem? I find it strange that I will have write my view model classes so that they derive from DependencyObject in order to start an animation when an event fires.
Cheers
You could simply use a flag: set a flag on your VM called 'RecentlyChangedFlag'. Pulse it to true, then false, whenever the appropriate value(s) change. You could do that like this:
private bool _changedFlag;
public bool ChangedFlag
{
get
{
if (_changedFlag)
{
_changedFlag = false;
OnPropertyChanged("ChangedFlag");
return true;
}
// (else...)
return false;
}
protected set
{
_changedFlag = value;
OnPropertyChanged("ChangedFlag");
}
}
I.e., with the above code set ChangedFlag = true when you want to signal the animation to start. It will be reset to false after WPF queries the true value.
Then have the animation occur when the value of RecentlyChangedFlag is true, as an EnterAction for instance.
Hope that helps.

Update Source Without Losing Focus - WPF Telerik RADDatePicker

I have a Telerik RadDatePicker, which I am binding to the SelectedDate property. I want this control to show a validation error when the default date set in the control is removed/deleted. I was able to achieve this, but the problem was that the validation error occurs only when Enter is pressed or when we click outside the control.
Is there a way tell RadDatePicker to update the source without moving the focus? (Tried UpdateSourceTrigger=PropertyChanged, but still it wasnt working)
You can use a Behavior that catches whatever (keystrokes, value changes, etc.) and force databinding update.
I have written a similar one to use in Silverlight's TextBox, since in Silverlight you can't change the UpdateSourceTrigger like in WPF.
You can use the following code and make the required adjustments:
public class TextBoxUpdateBehavior : Behavior<TextBox>
{
public TextBoxUpdateBehavior()
{
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.TextChanged += AssociatedObjectOnTextChanged;
}
private void AssociatedObjectOnTextChanged(object sender, TextChangedEventArgs args)
{
var bindingExpr = AssociatedObject.GetBindingExpression(TextBox.TextProperty);
bindingExpr.UpdateSource();
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.TextChanged -= AssociatedObjectOnTextChanged;
}
}
I might be a bit late now, but you can use CurrentDateTimeText property of RadDatePicker
To extend zish's answer, I use the RadDatePicker's SelectedDate property bound to my VM's DateTime? SelectedDate property with no validation set in the binding. I use the RadDatePicker's CurrentDateTimeText property bound to my VM's string SelectedDateText property with ValidatesOnErrors=True in the binding. Then, in the VM, I validate that !String.IsNullOrEmpty(SelectedDateText). This gives me the behavior that I want without the need for a behavior.

how and where to use DependencyProperty

I am new to WPF so after reading for a while I deduce that my problem needs to be handled with this pattern: DependencyProperty.
I want my ToggleButton to have another boolean property.
My problem is where should I assign this property, and how? Inside the object that is bound to the ToggleButton?
Let's say I have a class cell (which is bound to this Button) that when clicked I want that from this point on, it would hold new face with trigger on.
My new property will be:
bool wasClick
Can someone explain to me how I should write it and tell me more about this new concept?
EDIT:
The main topic is where should I define it so I want it asoocited to a Button but where should I write the code. Lets say I have a class that is bound to a Button. Should I write:
public static readonly DependencyProperty IsSpinningProperty =
DependencyProperty.Register(
... "IsSpinning", typeof(Boolean),
in this class or should I write it in my view model? If so, where and how?
As the name implies (kind of poorly), a dependency property is a property whose value can depend on something else. Generally, this means a property whose value gets determined automatically (and dynamically) by the WPF framework under certain conditions. The most common conditions are:
The property has a default value, or inherits its value from an ancestor in the visual tree. In this case, the property's value is determined without it ever being set.
The property is the target of data binding.
The property's value is set by an animation.
Not all properties whose value gets set by the WPF framework need to be dependency properties. Any CLR property with a public getter and setter can be the source of a two-way data binding.
In your case, it sounds like you don't really need a dependency property, not if you're using a view model. You could just do this (assuming that you've implemented property-change notification in your class):
private bool _IsChecked;
public bool IsChecked
{
get { return _IsChecked; } }
set
{
if (value == _IsChecked)
{
return;
}
_IsChecked = value;
WasChecked = WasChecked || value;
OnPropertyChanged("IsChecked");
}
}
private bool _WasChecked;
public bool WasChecked
{
get { return _WasChecked; }
private set
{
if (value == _WasChecked)
{
return;
}
_WasChecked = value;
OnPropertyChanged("WasChecked");
}
}

Resources