I have a Template Control which has a StackPanel as it's root. What I want is a way to make something like this:
<Controls:MyControl Kind="SomeKind"/>
And based on SomeKind the StackPanel's background would change. There would be a limited number of "Kinds", the same way there is a limited number of HorizontalAlignments in a Button, for example.
<Button HorizontalAlignment="Center"/>
I've searched in the internet a bit and it does seems like it involves Attached Properties, but I haven't found a clean, simple and easy-to-follow example for UWP. I've found some examples for WPF but they doesn't seem to work.
No you don't need attached properties since it's likely to be only associated within your custom control. What you need is a dependency property of an enum type.
Say if you have this enum -
public enum PanelBackgroundType
{
Orange,
Pink,
Offwhite
}
Then your dependency property will look something like this -
public PanelBackgroundType PanelBackgroundType
{
get { return (PanelBackgroundType)GetValue(PanelBackgroundTypeProperty); }
set { SetValue(PanelBackgroundTypeProperty, value); }
}
// Using a DependencyProperty as the backing store for PanelBackgroundType. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PanelBackgroundTypeProperty =
DependencyProperty.Register("PanelBackgroundType", typeof(PanelBackgroundType), typeof(MyControl),
new PropertyMetadata(PanelBackgroundType.Offwhite, (s, e) =>
{
if ((PanelBackgroundType)e.NewValue != (PanelBackgroundType)e.OldValue)
{
// value really changed, invoke your changed logic here
var control = (MyControl)s;
switch ((PanelBackgroundType)(e.NewValue))
{
case PanelBackgroundType.Orange:
control.MyStackPanel.Background = new SolidColorBrush(Colors.Orange);
break;
case PanelBackgroundType.Pink:
control.MyStackPanel.Background = new SolidColorBrush(Colors.Pink);
break;
case PanelBackgroundType.Offwhite:
default:
control.MyStackPanel.Background = new SolidColorBrush(Colors.Wheat);
break;
}
}
else
{
// else this was invoked because of boxing, do nothing
}
}));
Note that I have a check (PanelBackgroundType)e.NewValue != (PanelBackgroundType)e.OldValue inside the property changed callback to see if the value of the dp really has changed. This may seem redundant but according to MSDN, this is the best practise as -
If the type of a DependencyProperty is an enumeration or a structure,
the callback may be invoked even if the internal values of the
structure or the enumeration value did not change. This is different
from a system primitive such as a string where it only is invoked if
the value changed. This is a side effect of box and unbox operations
on these values that is done internally. If you have a
PropertyChangedCallback method for a property where your value is an
enumeration or structure, you need to compare the OldValue and
NewValue by casting the values yourself and using the overloaded
comparison operators that are available to the now-cast values. Or, if
no such operator is available (which might be the case for a custom
structure), you may need to compare the individual values. You would
typically choose to do nothing if the result is that the values have
not changed.
Have a look at this link: https://msdn.microsoft.com/en-us/windows/uwp/xaml-platform/custom-dependency-properties
It will show you how to add custom dependency properties for an object that you'll be able to edit in the UI.
I'll give you a quick example of what you want event though I recommend you to take a look at Microsoft's docs
public sealed partial class MyControl : UserControl
{
public MyControl()
{
this.InitializeComponent();
}
public static readonly DependencyProperty KindProperty = DependencyProperty.Register(
"Kind", typeof(string), typeof(MyControl),
new PropertyMetadata(null, new PropertyChangedCallback(OnKindChanged)));
public string Kind
{
get { return (string)GetValue(KindProperty); }
set { SetValue(KindProperty, value); }
}
private static void OnKindChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Executed method when the KindProperty is changed
}
}
Related
I have the following dependency property:
public bool UndoZoom
{
get { return (bool)GetValue(UndoZoomProperty); }
set { SetValue(UndoZoomProperty, value); }
}
// Using a DependencyProperty as the backing store for UndoZoom. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UndoZoomProperty =
DependencyProperty.Register("UndoZoom", typeof(bool), typeof(ZoomBehavior), new PropertyMetadata(false, new PropertyChangedCallback(OnUndoZoomChanged)));
On the callback methods I change this property to false:
private static void OnUndoZoomChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
ZoomBehavior zoomBehavior = sender as ZoomBehavior;
if (zoomBehavior == null || zoomBehavior.UndoZoom == false)
return;
//DO SOMETHING...
zoomBehavior.UndoZoom = false;
//ALSO TRY: zoomBehavior.SetValue(UndoZoomProperty, false);
}
The dependency property changes well, but the property that binding to it not!
The binding look like that :
UndoZoom="{Binding MyClass.UndoZoom,Mode=TwoWay}"
I would like to note that the binding does work, when I change the property in the model, the callback is fire, that's mean that the dependency property changes.
I just do not understand why it does not work the other way too, I expect that when I change the dependency property, the property in the model will also change.
I would love help.
The coercion callback is more suited to doing this. The property changed callback is meant for reacting to a change of value. The Coercion callback is for making sure the value is valid based on the current state which feels more like what you are doing here.
I'm new to wpf, as the title suggests. I was using wpf as it was winforms (what's all that binding non sense anyway) until, of course, I tried it and got blown away.
So I was digging into user controls and dependency properties. I read that, in order to get the ui to remain in sync with what's under the hood you need to use observable collections, notifypropertychanged / changing and dependency properties for the stuff that you use.
My question is this:
Let's say I have this dep. prop. for a user control (type of Media.Color) :
public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
The xaml uses it for a binding, it works, all is good. But, when it gets updated, I would like to do something with it in code.
So I tried putting a Console.writeline("fired") like so:
public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set {
Console.WriteLine("Fired");
SetValue(ColorProperty, value);
}
}
No dice. Could someone please enlighten me how this stuff works? I am obviously missing something (just the other day someone on stack told me about MouseCapture so... ).
Thank you for your time.
Edit
http://www.wpftutorial.net/DependencyProperties.html
Basically it says, in big bold letters,
Important: Do not add any logic to these properties, because they are
only called when you set the property from code. If you set the
property from XAML the SetValue() method is called directly.
If you are using Visual Studio, you can type propdp and hit 2x tab to
create a dependency property.
And goes on to explain why and how you should proceed.
Solution
So, I tried what #Krishna suggested, and my user control crashed and burned.
Here was my dep prop. (as it was before asking this question).
public static readonly DependencyProperty ColorProperty = DependencyProperty.Register("Color", typeof(Color), typeof(ColorPickerMaster), new PropertyMetadata(default(Color)));
Turns out the problem was using
(...) new Prop.Metadata(null, OnPropChanged)
Using
public static readonly DependencyProperty ColorProperty = DependencyProperty.Register("Color", typeof(Color), typeof(ColorPickerMaster), new PropertyMetadata(OnColorChanged));
private static void OnColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine(e.NewValue);
}
yields a beautiful win.
Thank you for your time and answers.
When it comes to DependencyProperties you use property changed callback to track changes to your property like below example. And then you use e.NewValue and e.OldValue to write your logic. More about DependencyProperties on MSDN
public Color color
{
get { return (Color)GetValue(colorProperty); }
set { SetValue(colorProperty, value); }
}
public static readonly DependencyProperty colorProperty =
DependencyProperty.Register("color", typeof(Color), typeof(YourClass), new PropertyMetadata(null,colorChanged));
private static void colorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
YourClass c = d as YourClass;
if(c!=null)
{
}
}
From MSDN - XAML Loading and Dependency Properties:
The current WPF implementation of its XAML processor is inherently dependency property aware. The WPF XAML processor uses property system methods for dependency properties when loading binary XAML and processing attributes that are dependency properties. This effectively bypasses the property wrappers. When you implement custom dependency properties, you must account for this behavior and should avoid placing any other code in your property wrapper other than the property system methods GetValue and SetValue.
If you want to add custom logic in a setter you will have to make it a simple field (not a DependecyProperty) implement INotifyPropertyChanged and bind to it.
I have this simple example in the ViewModel of a WPF application:
class VM_DiskPartition : DependencyObject
{
// (...) Other properties
public bool IsLowOnSpace
{
get { return (bool)GetValue(IsLowOnSpaceProperty); }
set { SetValue(IsLowOnSpaceProperty, value); }
}
public static readonly DependencyProperty IsLowOnSpaceProperty = DependencyProperty.Register("IsLowOnSpace", typeof(bool), typeof(VM_DiskPartition), new PropertyMetadata(false, OnLowOnSpaceChanged));
private static void OnLowOnSpaceChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
{
((VM_DiskPartition)d).CoerceValue(BgColorProperty);
}
public Brush BgColor
{
get { return (Brush)GetValue(BgColorProperty); }
set { SetValue(BgColorProperty, value); }
}
public static readonly DependencyProperty BgColorProperty = DependencyProperty.Register("BgColor", typeof(Brush), typeof(VM_DiskPartition), new PropertyMetadata(Brushes.Red, null, Coerce_BgColor));
private static object Coerce_BgColor(DependencyObject d, object baseValue)
{
return UIUtils.GetBgColor(((VM_DiskPartition)d).IsLowOnSpace);
}
}
I want the BgColor property to have its default value automatically set by its coercion function.
Is there a more elegant way to achieve this instead of calling CoerceValue(BgColorProperty) from the constructor?
The reason is that I may have many properties like this in the future and it doesn't look very clean to use a lot of CoerceValue() calls in the constructor.
Maybe it's better to use Converters in this scenario? I was trying to go without them and create new ViewModel properties instead.
You seem to be somewhat confused... the DependencyObject and DependencyProperty classes are UI classes. They don't belong in a view model. In view models, we use normal CLR properties and the INotifyPropertyChanged interface to handle property change notification. Therefore, there's no need to use them in a view model at all.
If you want to set a default value in a view model, you simply do this:
private int number = 5; // <-- default value
public int Number
{
get { return number; }
set { number = value; NotifyPropertyChanged("Number"); }
}
If you want property value coercion in a view model, you just do this:
public int Number
{
get { return number; }
set { number = Math.Max(0, value); NotifyPropertyChanged("Number"); }
}
UPDATE >>>
Looking again at your code, it occurs to me that it shouldn't be in a view model at all. It looks like it should be in the code behind of some UserControl. We put data in view models, not UI elements like Brushes. If you want to set a default value for a DependencyProperty, the correct way to do it is how you have shown us:
public static readonly DependencyProperty BgColorProperty =
DependencyProperty.Register("BgColor", typeof(Brush), typeof(VM_DiskPartition),
new PropertyMetadata(Brushes.Red/* <-- default value */, null, Coerce_BgColor));
Property coercion is for ensuring that a value stays within certain bounds like the example I gave above that ensures that the value will never be negative.
Consider this scenario, using MVVM:
On my ModelView, I have one property, of type "string", it does notify the change of properties through INotifyPropertyChanged.
In the view, there is (or not) one control, with a DependencyProperty "Notification" of a type which is not a string. That control may or may not change that property depending on facts that only the control knows (neither the ModelView or the View knows about those). That control might even be on other View which may or may not be on the current visual tree.
In the View, I need a bridge between that control's DependencyProperty and the ViewModel's property, so that changing the view property makes the control change its property, and changing the control's DependencyProperty makes the viewmodel's property change its value.
I've got it to work, but I don't think it's an elegant solution. I might be thinking fuzzy these days so I'm asking if there's something obvious that I might have missed.
The obvious way would be either having the ViewModel Property be a DependencyProperty (so it could be bound two ways), however that is not possible right now (plus, it'd break the MVVM pattern, adding view-specific implementations to the viewmodel).
The other obvious way would be binding the Control's DependencyProperty to the ViewModel's property: this works, but just for one view... several properties cannot (or, I don't know how to do it) be bound to the same DependencyProperty: when I set one binding, I lose the other.
Currently this is what I do:
public class BaseViewUserControl : UserControl
{
// Dependency property, bound to the view's property
public string AudioNotification
{
get { return (string)GetValue(AudioNotificationProperty); }
set { SetValue(AudioNotificationProperty, value); }
}
public static readonly DependencyProperty AudioNotificationProperty = DependencyProperty.Register("AudioNotification", typeof(string), typeof(BaseViewUserControl), new FrameworkPropertyMetadata("None", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnAudioNotificationPropertyChanged));
// Dependency property, bound to the control's dependency property
public AudioNotificationType AudioNotificationToControl
{
get { return (AudioNotificationType)GetValue(AudioNotificationToControlProperty); }
set { SetValue(AudioNotificationToControlProperty, value); }
}
public static readonly DependencyProperty AudioNotificationToControlProperty = DependencyProperty.Register("AudioNotificationToControl", typeof(AudioNotificationType), typeof(BaseViewUserControl), new FrameworkPropertyMetadata(AudioNotificationType.None, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, OnAudioNotificationToControlCoerceValue));
// Converter
private static IValueConverter _audioNotificationTypeConverter;
private static IValueConverter AudioNotificationTypeConverter
{
get { return _audioNotificationTypeConverter ?? (_audioNotificationTypeConverter = new AudioNotificationConverter()); }
}
private Binding _audioNotificationBinding;
private bool PrepareAudioNotificationControlBinding()
{
if (_audioNotificationBinding != null) return true;
var b = this.FindVisualTreeRoot().TryFindChild<AudioNotification>();
if (b == null) return false;
_audioNotificationBinding = new Binding { Source = b, Mode = BindingMode.TwoWay, Path = new PropertyPath("Notification") };
SetBinding(AudioNotificationToControlProperty, _audioNotificationBinding);
return true;
}
private static void OnAudioNotificationPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
if (!(source is BaseViewUserControl)) return;
var src = (BaseViewUserControl)source;
if(src.PrepareAudioNotificationControlBinding())
{
var val = AudioNotificationTypeConverter.ConvertValue<AudioNotificationType>(e.NewValue);
src.AudioNotificationToControl = val;
}
}
private static object OnAudioNotificationToControlCoerceValue(DependencyObject source, object basevalue)
{
if (!(source is BaseViewUserControl)) return basevalue;
var src = (BaseViewUserControl)source;
var val = AudioNotificationTypeConverter.ConvertBackValue<string>(basevalue);
src.AudioNotification = val;
return basevalue;
}
public BaseViewUserControl()
{
var ab = new Binding { Path = new PropertyPath("AudibleNotification"), Mode = BindingMode.TwoWay };
SetBinding(AudibleNotificationProperty, ab);
}
}
NOTE: I'm using this for several things, not just for audio notification (that's an example only). Do not rely on the names to give a solution (if any), this needs to be quite generic. Also, any typos come from simplifying the code to the problem (I've removed much code and changed some property names for clarification).
As I said, it works... I just find it quite not-elegant and I'm sure there should be a better solution than this.
Any suggestions will be more than welcome.
Update
Based on Julien's code, I made this Behavior, which does exactly what I wanted. I implemented it using Converter, but for clarity's sake, I ended up doing the conversion on the control itself and using strings to pass variables along (with an undocumented property in the control's if I still want to use the native data type)
public class BridgePropertyBinderBehavior : Behavior<DependencyObject>
{
public static BridgePropertyBinderBehavior PrepareBindingToControl(FrameworkElement sourceView, string viewModelPropertyPath, FrameworkElement targetControl, string controlPropertyPath)
{
var b = new BridgePropertyBinderBehavior();
BindingOperations.SetBinding(b, AProperty, new Binding(viewModelPropertyPath) { Source = sourceView.DataContext, Mode = BindingMode.TwoWay, BindsDirectlyToSource = true, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged });
BindingOperations.SetBinding(b, BProperty, new Binding(controlPropertyPath) { Source = targetControl, Mode = BindingMode.TwoWay });
Interaction.GetBehaviors(sourceView).Add(b);
return b;
}
public object A { get { return GetValue(AProperty); } set { SetValue(AProperty, value); } }
public static readonly DependencyProperty AProperty = DependencyProperty.Register("A", typeof(object), typeof(BridgePropertyBinderBehavior), new FrameworkPropertyMetadata(null, (d, e) => ((BridgePropertyBinderBehavior)d).OnAChanged(e.NewValue)));
public object B { get { return GetValue(BProperty); } set { SetValue(BProperty, value); } }
public static readonly DependencyProperty BProperty = DependencyProperty.Register("B", typeof(object), typeof(BridgePropertyBinderBehavior), new FrameworkPropertyMetadata(null, (d, e) => ((BridgePropertyBinderBehavior)d).OnBChanged(e.NewValue)));
private void OnAChanged(object value) { B = value; }
private void OnBChanged(object value) { A = value; }
protected override Freezable CreateInstanceCore()
{
return new BridgePropertyBinderBehavior();
}
}
Which I use like this on my view:
var audioNotificationControl = this.FindVisualTreeRoot().TryFindChild<AudioNotification>();
BridgePropertyBinderBehavior.PrepareBindingToControl(this, "AudioNotification", audioNotificationControl, "Notification");
or
<AudioNotification x:Name="Control">
<ia:Interaction.Behaviors>
<BridgePropertyBinderBehavior
A="{Binding Path=Notification, ElementName=Control, Mode=TwoWay}"
B="{Binding Path=AudioNotification, Mode=TwoWay}" />
</ia:Interaction.Behaviors>
</AudioNotification>
I've accepted his answer since it's what got me on the right track, thanks
If I understand you correctly, you need to bind one DP to two sources, one as a source and the other as a target. I actually have a behavior to do that.
The principle of this behavior is quite simple: it uses two dependency properties and makes the data of one (In) flows into the other (Out). Bind In with a one way binding and Out with a one way to source binding and you're done.
public class BindingBehavior : Behavior<DependencyObject> {
public static readonly DependencyProperty InProperty = DependencyProperty.Register(
"In",
typeof(object),
typeof(BindingBehavior),
new FrameworkPropertyMetadata(null, (d, e) => ((BindingBehavior) d).OnInPropertyChanged(e.NewValue)));
public static readonly DependencyProperty OutProperty = DependencyProperty.Register(
"Out",
typeof(object),
typeof(BindingBehavior),
new FrameworkPropertyMetadata(null));
// Bind OneWay
public object In {
get { return GetValue(InProperty); }
set { SetValue(InProperty, value); }
}
// Bind OneWayToSource
public object Out {
get { return GetValue(OutProperty); }
set { SetValue(OutProperty, value); }
}
private void OnInPropertyChanged(object value) {
Out = value;
}
protected override Freezable CreateInstanceCore() {
return new BindingBehavior();
}
}
This behavior needs a reference to System.Windows.Interactivity from Blend SDK, which you might be familiar with.
Assuming you remove your string property and only keep a AudioNotificationType one named AudtioNotification, the usage should be similar to:
<YourView x:Name="View">
<YourControl x:Name="Control" AudioNotification="{Binding Notification, ElementName=View}>
<i:Interaction.Behaviors>
<BindingBehavior
In="{Binding AudioNotification, ElementName=Control, Mode=OneWay}"
Out="{Binding YourVmProperty, Mode=OneWayToSource, Converter=YourConverter}" />
</i:Interaction.Behaviors>
</YourControl>
</YourView>
You can place the behavior on any element being on the correct name scope for resolving element names and having the view model as the data context.
This looks like it might be a useful time to add a layer of abstraction. I know. Ick. But bear with me.
What if you had a bridge object that an abitrary number of things can bind to that handles the notifications on change. It doesn't even need to be that complex. Just something that implements INotifyPropertyChanged and then has a property (or properties) that release a notification on change. That way, your ViewModel, your View and your control can all bind to the same property on this bridge object and when one of those changes the bridge object's property, all the others will know that it's time to change as well. As long as all the objects are bound two-way, everything should synch just fine.
That's essentially what you've done on your BaseViewUserControl, but encapsulating the behavior in a separate object might provide you flexibility benefits.
Would it be possible for an Adorner to be hidden / displayed depending on the value of a property in a class ?
Should I use attached properties for this purpose ?
If so, how exactly can the Adorner's visibility be controlled; do I have to manually remove it / add it to the Adorner Layer within the Dependency Object's OnChanged event ?
This is just a very quick code representation of what I'm trying to do:
(Note: I'm not even sure if its the right way of doing things. I want the Adorner's visibility to be controlled by the value of a property that is modified by the code in my business model. The problem with Attached Properties is that its the control's responsibility to update the value of the property instead of the code in my business domain.)
public static class IsValidBehavior
{
public static readonly DependencyProperty IsValidProperty = DependencyProperty.RegisterAttached("IsValid",
typeof(bool),
typeof(IsValidBehavior),
new UIPropertyMetadata(false, OnIsValidChanged));
public static bool GetIsValid(DependencyObject obj)
{
return (bool)obj.GetValue(IsValidProperty);
}
public static void SetIsValid(DependencyObject obj, bool value)
{
obj.SetValue(IsValidProperty, value);
}
private static void OnIsValidChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
UIElement element = dependencyObject as UIElement;
if (element == null)
return;
if ((bool)e.NewValue == true)
{
// Display the Adorner
}
else
{
// Hide the Adorner
}
}
}
Well, if I right understood your question, in WPF you have 2 ways to do that, from code or from XAML. From code, you more or less already did, in XAML you can do something like this, I think:
Visibility="{Binding Path=MyVisibilityVariant,
Converter={StaticResource VisibilityConverter}}
In other words bind it to some property. My general suggestion: is use XAML whenever you can, considering a couple of variants:
XAML declaration makes the software very scallable, but also more complex (consider your, or your group cappabilities, somehow doing the stuff in code behind is best, if not only solution available)
Consider you deadlines, cause on XAML stuff implementing/debugging/fixing you will spend more time then on code.
EDIT
Defining custom Adorder in order to be able to define it in XAML