C# function called after dependency property have been set - c#

My code currently looks like this:
private Foo myFoo;
public Foo CurrentFoo
{
get { return myFoo; }
set { SetFoo(value); }
}
private void SetFoo(Foo newFoo)
{
// Do stuff
// Here be dragons
myFoo = newFoo;
}
To be able to bind it in XAML/WPF I need to turn Foo into a dependency property:
public static DependencyProperty CurrentFooProperty =
DependencyProperty.Register("CurrentFoo", typeof(Foo),
typeof(FooHandler), new PropertyMetadata(false));
public Foo CurrentFoo
{
get { return (Foo)GetValue(CurrentFooProperty); }
set { SetValue(CurrentFooProperty, value); }
}
Ive heard that you shouldnt do magic inside the actual C# property set {}, since it might not be called but the value is written directly to the dependency property. If this is false, let me know, it seems like the most obvious and simple route to take.
I know I can add a validation function to the dependency property but I assume that it shouldnt be used for this? I need to communicate the change to legacy systems that cannot yet be bound in XAML.
Whats the best way to approach this problem?

public static DependencyProperty CurrentFooProperty = DependencyProperty.Register(
"CurrentFoo",
typeof(Foo),
typeof(FooHandler),
new PropertyMetadata(OnCurrentFooChanged));
private static void OnCurrentFooChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var currentFoo = (Foo) e.NewValue;
// Use
}

This should be handled by a callback assigned to the Dependency Property. The proper place for this depends on what is actually happening here:
// Do stuff
// Here be dragons
There are three callbacks that can be assigned to a Dependency Property - If you just need to notify the legacy systems, then I recommend adding a Property Changed callback, and adding the notification there.
If, however, this is performing something that amounts to validation via the Legacy system, it may be more appropriate to put these dragons into the ValidationCallback.
I doubt that the CoerceValue callback is appropriate, given your scenario.

Related

WPF Dependency Property Based on another dependency property [duplicate]

The code below is my current solution.
A great example of what I am trying to mimic would be the FrameworkElement.ActualWidth property. You know how the ActualWidth property is calculated and reassigned, whenever the Width property changes, or whenever the control is redrawn, or whenever else? ------
From the developer's perspective, it just looks like data-binding hard-at-work.
But ActualWidth is a read-only dependency-property. Does Microsoft really have to go through this gigantic trash-hole of code to make that work? Or is there a simpler way that utilizes the existing functionality of the data-binding system?
public class foo : FrameworkElement
{
[ValueConversion(typeof(string), typeof(int))]
public class fooConverter : IValueConverter
{ public object Convert( object value, Type targetType,
object parameter, CultureInfo culture)
{ ... }
public object ConvertBack( object value, Type targetType,
object parameter, CultureInfo culture)
{ ... }
}
private static readonly fooConverter fooConv = new fooConverter();
private static readonly DependencyPropertyKey ReadOnlyIntPropertyKey =
DependencyProperty.RegisterReadOnly( "ReadOnlyInt", typeof(int),
typeof(foo), null);
public int ReadOnlyInt
{ get { return (int)GetValue(ReadOnlyIntPropertyKey.DependencyProperty); }
}
public static readonly DependencyProperty ReadWriteStrProperty =
DependencyProperty.Register( "ReadWriteStr", typeof(string), typeof(foo),
new PropertyMetadata(ReadWriteStr_Changed));
public string ReadWriteStr
{ get { return (string)GetValue(ReadWriteStrProperty); }
set { SetValue(ReadWriteStrProperty, value); }
}
private static void ReadWriteStr_Changed( DependencyObject d,
DependencyPropertyChangedEventArgs e)
{ try
{ if (d is foo)
{ foo f = d as foo;
f.SetValue( ReadOnlyIntPropertyKey,
fooConv.Convert(f.ReadWriteStr, typeof(int), null,
CultureInfo.CurrentCulture));
}
}
catch { }
}
}
Unfortunately, you'll need most of what you have. The IValueConverter isn't required in this case, so you could simplify it down to just:
public class foo : FrameworkElement
{
private static readonly DependencyPropertyKey ReadOnlyIntPropertyKey =
DependencyProperty.RegisterReadOnly( "ReadOnlyInt", typeof(int),
typeof(foo), null);
public int ReadOnlyInt
{
get { return (int)GetValue(ReadOnlyIntPropertyKey.DependencyProperty); }
}
public static readonly DependencyProperty ReadWriteStrProperty =
DependencyProperty.Register( "ReadWriteStr", typeof(string), typeof(foo),
new PropertyMetadata(ReadWriteStr_Changed));
public string ReadWriteStr
{
get { return (string)GetValue(ReadWriteStrProperty); }
set { SetValue(ReadWriteStrProperty, value); }
}
private static void ReadWriteStr_Changed(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
foo f = d as foo;
if (f != null)
{
int iVal;
if (int.TryParse(f.ReadWriteStr, out iVal))
f.SetValue( ReadOnlyIntPropertyKey, iVal);
}
}
}
It's not as bad as you suggest, IMHO...
You could get rid of the converter : IValueConverter is for bindings, you don't need it for conversions in code-behind. Apart from that, I don't see how you could make it more concise...
Yes, there is a clean way to "make a read-only DependencyProperty reflect the value of another property," but it may require a pretty fundamental shift in the overall property programming model of your app. In short, instead of using the DependencyPropertyKey to push values into the property, every read-only DependencyProperty can have a CoerceValue callback which builds its own value by pulling all the source values it depends on.
In this approach, the 'value' parameter that's passed in to CoerceValue is ignored. Instead, each DP's CoerceValue function recalculates its value "from scratch" by directly fetching whatever values it needs from the DependencyObject instance passed in to CoerceValue (you can use dobj.GetValue(...) for this if you want to avoid casting to the owner instance type).
Try to suppress any suspicion that ignoring the value supplied to CoerceValue may be wasting something. If you adhere to this model, those values will never be useful and the overall work is the same or less than a "push" model because source values that haven't changed are, as always, cached by the DP system. All that's changed is who's responsible for the calculation and where it's done. What's nice here is that calculation of each DP value is always centralized in one place and specifically associated with that DP, rather than strewn across the app.
You can throw away the DependencyPropertyKey in this model because you'll never need it. Instead, to update the value of any read-only DP you just call CoerceValue or InvalidateValue on the owner instance, indicating the desired DP. This works because those two functions don't require the DP key, they use the public DependencyProperty identifier instead, and they're public functions, so any code can call them from anywhere.
As for when and where to put these CoerceValue/InvalidateValue calls, there are two options:
Eager: Put an InvalidateValue call for the (target) DP in the PropertyChangedCallback of every (source) DP that's mentioned in the (target) DP's CoerceValueCallback function, --or--
Lazy: Always call CoerceValue on the DP immediately prior to fetching its value.
It's true that this method is not so XAML-friendly, but that wasn't a requirement of the OPs question. Considering, however, that in this approach you don't ever even need to fetch or retain the DependencyPropertyKey at all, it seems like it might one of the sleekest ways to go, if you're able to reconceive your app around the "pull" semantics.
In a completely separate vein, there's yet another solution that may be even simpler:
Expose INotifyPropertyChanged on your DependencyObject and use CLR properties for the read-only properties, which will now have a simple backing field. Yes, the WPF binding system will correctly detect and monitor both mechanisms--DependencyProperty and INotifyPropertyChanged--on the same class instance. A setter, private or otherwise, is recommended for pushing changes to this read-only property, and this setter should check the backing field to detect vacuous (redundant) changes, otherwise raising the old-style CLR PropertyChanged event.
For binding to this read-only property, either use the owner's OnPropertyChanged overload (for self-binding) to push in the changes from DPs, or, for binding from arbitrary external properties, use System.ComponentModel.DependencyPropertyDescriptor.FromProperty to get a DependencyPropertyDescriptor for the relevant souce DPs, and use its AddValueChanged method to set a handler which pushes in new values.
Of course for non-DP properties or non-DependencyObject instances, you can just subscribe to their INotifyPropertyChanged event to monitor changes that might affect your read-only property. In any case, no matter which way you push changes into the read-only property, the event raised by its setter ensures that changes to the read-only property correctly propagate onwards to any further dependent properties, whether WPF/DP, CLR, data-bound or otherwise.

Custom control binding setter not firing

I have a custom control with following code:
public partial class TableSelectorControl : UserControl
{
private Brush _cellHoverBrush = new SolidColorBrush(Colors.CadetBlue) { Opacity = 0.3 };
public static readonly DependencyProperty ActiveSelectionProperty =
DependencyProperty.Register("ActiveSelection", typeof(TableSelectorSelection),
typeof(TableSelectorControl));
public TableSelectorSelection ActiveSelection
{
get => (TableSelectorSelection)GetValue(ActiveSelectionProperty);
set
{
SetValue(ActiveSelectionProperty, value);
_cellHoverBrush = value.HoverBrush;
}
}
}
As you can see, I'm trying to set _cellHoverBrush on each ActiveSelectionProperty update, which is done from ViewModel. Binding works well and the ActiveSelectionProperty seemes to change, but the setter is not firing. I surely can use a FrameworkProperyMetadata, but I don't want _cellHoverBrush to become static, the idea is to change it with respect to selected ActiveSelection. How can I achieve this?
I can provide more info, if needed.
There are two types of properties in WPF: .NET Framework properties and dependency properties (which are specific for WPF). Each dependency property has associated a .Net Framework property, but this property is only a wrapper over WPF dependencies properties. This is done to standardize the way we work with properties in WPF. When a dependency property is used in bindings from .xaml files, the WPF framework will not use the .Net wrapper property to get or set the value. This is why, it's not indicated to use other code than GetValue and SetValue in your .NET wrapper property.
For what you need, you should use PropertyChangedCallback, like in the example below:
public partial class TableSelectorControl : UserControl
{
private Brush _cellHoverBrush = new SolidColorBrush(Colors.CadetBlue) { Opacity = 0.3 };
public static readonly DependencyProperty ActiveSelectionProperty =
DependencyProperty.Register("ActiveSelection", typeof(TableSelectorSelection),
typeof(TableSelectorControl), new PropertyMetadata(new PropertyChangedCallback(OnActiveSelectionChanged)));
public TableSelectorSelection ActiveSelection
{
get => (TableSelectorSelection)GetValue(ActiveSelectionProperty);
set => SetValue(ActiveSelectionProperty, value);
}
private static void OnActiveSelectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var tableSelCtrl = d as TableSelectorControl;
if (tableSelCtrl != null)
{
tableSelCtrl._cellHoverBrush = (e.NewValue as TableSelectorSelection)?.HoverBrush;
}
}
}
Using the PropertyChangedCallback of FrameworkPropertyMetadata doesn't necessarily mean you need to make your field static. Your handler method will get a reference to the instance that is invoking it which you can then modify - you will need to cast it to your type first though.
The PropertyChanged walkthrough on this page shows one way you might do it.
https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/dependency-property-callbacks-and-validation

Better way to implement a behavior

I'm relatively new to WPF and Behaviors.
I have this behavior, I need to execute DoSomething() every time I set IsRedundant in the ViewModel.
Each time I need to trigger DoSomething, I would need to change the value of the property and this is confusing (if ture => set it to false, If false => set it to true). IsRedundant only used to raise the property changed event and for nothing else.
Is there a better way of achieving this ?
Any ideas ?
wpf
<i:Interaction.Behaviors>
<local:UIElementBehavior Redundant="{Binding IsRedundant, Mode=TwoWay}"/ >
</i:Interaction.Behaviors>
C#
class UIElementBehavior : Behavior<UIElement>
{
public static readonly DependencyProperty RedundantProperty = DependencyProperty.Register(
"Redundant",
typeof(bool),
typeof(UIElementBehavior),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, DoSomething));
public bool Redundant
{
get { return (bool)GetValue(RedundantProperty); }
set { SetValue(RedundantProperty, value); }
}
private static void DoSomething(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//Do something on the AssociatedObject
}
}
Each time I need to trigger DoSomething, I would need to change the value of the property and this is confusing (if true => set it to false, If false => set it to true)
The problem is that you are using binding. Binding required target to be dependency property. And those are special, their setters aren't called, so you have to use callback to get informed when their value is changed via binding.
Moreover there is internally a check if value is different, for performance reasons callback is not called if value is the same, so you must change it as you do already.
An alternative solution is to simply add event in the view model:
public class ViewModel: INotifyPropertyChanged
{
public EventHandler SomethingHappens;
// call this to tell something to listener if any (can be view or another view model)
public OnSomethingHappens() => SomethingHappens?.Invoke(this, EventArgs.Empty);
...
}
Now you can subscribe/unsubscribe in the view to/from this event and do something in the event handler. If you are purist, then refactor code from the view into reusable behavior.
Is it shorter? Nope. Is it more clear? Yes, compared to using bool and such "wonderful" code:
IsRedundant = false;
IsRedundant = true; // lol
I was using bool properties like you do to inform the view in the past.
Then I used events.
Now I use combination of both. Every view model already implements INotifyPropertyChanged so why not use it?
Think about IsRedundant as a state. It can be used not only to trigger some method, but also used by the view to run animations via data triggers, control visibility of dynamic layout, etc. So you need a normal bool property in view model.
The view then can subscribe to/unsubscribe from PropertyChanged and simply have to check:
if(e.PropertyName == nameof(ViewModel.IsRedudant)) { ... }

Wrapped WPF Control

I'm trying to create a GUI (WPF) Library where each (custom) control basically wraps an internal (third party) control. Then, I'm manually exposing each property (not all of them, but almost). In XAML the resulting control is pretty straightforward:
<my:CustomButton Content="ClickMe" />
And the code behind is quite simple as well:
public class CustomButton : Control
{
private MyThirdPartyButton _button = null;
static CustomButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomButton), new FrameworkPropertyMetadata(typeof(CustomButton)));
}
public CustomButton()
{
_button = new MyThirdPartyButton();
this.AddVisualChild(_button);
}
protected override int VisualChildrenCount
{
get
{ return _button == null ? 0 : 1; }
}
protected override Visual GetVisualChild(int index)
{
if (_button == null)
{
throw new ArgumentOutOfRangeException();
}
return _button;
}
#region Property: Content
public Object Content
{
get { return GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
"Content", typeof(Object),
typeof(CustomButton),
new FrameworkPropertyMetadata(new PropertyChangedCallback(ChangeContent))
);
private static void ChangeContent(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
(source as CustomButton).UpdateContent(e.NewValue);
}
private void UpdateContent(Object sel)
{
_button.Content = sel;
}
#endregion
}
The problem comes after we expose MyThirdPartyButton as a property (in case we don't expose something, we would like to give the programmer the means to use it directly). By simply creating the property, like this:
public MyThirdPartyButton InternalControl
{
get { return _button; }
set
{
if (_button != value)
{
this.RemoveVisualChild(_button);
_button = value;
this.AddVisualChild(_button);
}
}
}
The resulting XAML would be this:
<my:CustomButton>
<my:CustomButton.InternalControl>
<thirdparty:MyThirdPartyButton Content="ClickMe" />
</my:CustomButton.InternalControl>
And what I'm looking for, is something like this:
<my:CustomButton>
<my:CustomButton.InternalControl Content="ClickMe" />
But (with the code I have) its impossible to add attributes to InternalControl...
Any ideas/suggestions?
Thanks a lot,
--
Robert
WPF's animation system has the ability to set sub-properties of objects, but the XAML parser does not.
Two workarounds:
In the InternalControl property setter, take the value passed in and iterate through its DependencyProperties copying them to your actual InternalControl.
Use a build event to programmatically create attached properties for all internal control properties.
I'll explain each of these in turn.
Setting properties using the property setter
This solution will not result in the simplified syntax you desire, but it is simple to implement and will probably solve the main problem with is, how to merge values set on your container control with values set on the internal control.
For this solution you continue to use the XAML you didn't like:
<my:CustomButton Something="Abc">
<my:CustomButton.InternalControl>
<thirdparty:MyThirdPartyButton Content="ClickMe" />
</my:CustomButton.InternalControl>
but you don't actually end up replacing your InternalControl.
To do this, your InternalControl's setter would be:
public InternalControl InternalControl
{
get { return _internalControl; }
set
{
var enumerator = value.GetLocalValueEnumerator();
while(enumerator.MoveNext())
{
var entry = enumerator.Current as LocalValueEntry;
_internalControl.SetValue(entry.Property, entry.Value);
}
}
}
You may need some additional logic to exclude DPs not publically visible or that are set by default. This can actually be handled easily by creating a dummy object in the static constructor and making a list of DPs that have local values by default.
Using a build event to create attached properties
This solution allows you to write very pretty XAML:
<my:CustomButton Something="Abc"
my:ThirdPartyButtonProperty.Content="ClickMe" />
The implementation is to automatically create the ThirdPartyButtonProperty class in a build event. The build event will use CodeDOM to construct attached properties for each property declared in ThirdPartyButton that isn't already mirrored in CustomButton. In each case, the PropertyChangedCallback for the attached property will copy the value into the corresponding property of InternalControl:
public class ThirdPartyButtonProperty
{
public static object GetContent(...
public static void SetContent(...
public static readonly DependencyProperty ContentProperty = DependencyProperty.RegisterAttached("Content", typeof(object), typeof(ThirdPartyButtonProperty), new PropertyMetadata
{
PropertyChangedCallback = (obj, e) =>
{
((CustomButton)obj).InternalControl.Content = (object)e.NewValue;
}
});
}
This part of the implementation is straightforward: The tricky part is creating the MSBuild task, referencing it from your .csproj, and sequencing it so that it runs after the precompile of my:CustomButton so it can see what additional properties it needs to add.

Is there a *clean* way to make a read-only dependency-property reflect the value of another property?

The code below is my current solution.
A great example of what I am trying to mimic would be the FrameworkElement.ActualWidth property. You know how the ActualWidth property is calculated and reassigned, whenever the Width property changes, or whenever the control is redrawn, or whenever else? ------
From the developer's perspective, it just looks like data-binding hard-at-work.
But ActualWidth is a read-only dependency-property. Does Microsoft really have to go through this gigantic trash-hole of code to make that work? Or is there a simpler way that utilizes the existing functionality of the data-binding system?
public class foo : FrameworkElement
{
[ValueConversion(typeof(string), typeof(int))]
public class fooConverter : IValueConverter
{ public object Convert( object value, Type targetType,
object parameter, CultureInfo culture)
{ ... }
public object ConvertBack( object value, Type targetType,
object parameter, CultureInfo culture)
{ ... }
}
private static readonly fooConverter fooConv = new fooConverter();
private static readonly DependencyPropertyKey ReadOnlyIntPropertyKey =
DependencyProperty.RegisterReadOnly( "ReadOnlyInt", typeof(int),
typeof(foo), null);
public int ReadOnlyInt
{ get { return (int)GetValue(ReadOnlyIntPropertyKey.DependencyProperty); }
}
public static readonly DependencyProperty ReadWriteStrProperty =
DependencyProperty.Register( "ReadWriteStr", typeof(string), typeof(foo),
new PropertyMetadata(ReadWriteStr_Changed));
public string ReadWriteStr
{ get { return (string)GetValue(ReadWriteStrProperty); }
set { SetValue(ReadWriteStrProperty, value); }
}
private static void ReadWriteStr_Changed( DependencyObject d,
DependencyPropertyChangedEventArgs e)
{ try
{ if (d is foo)
{ foo f = d as foo;
f.SetValue( ReadOnlyIntPropertyKey,
fooConv.Convert(f.ReadWriteStr, typeof(int), null,
CultureInfo.CurrentCulture));
}
}
catch { }
}
}
Unfortunately, you'll need most of what you have. The IValueConverter isn't required in this case, so you could simplify it down to just:
public class foo : FrameworkElement
{
private static readonly DependencyPropertyKey ReadOnlyIntPropertyKey =
DependencyProperty.RegisterReadOnly( "ReadOnlyInt", typeof(int),
typeof(foo), null);
public int ReadOnlyInt
{
get { return (int)GetValue(ReadOnlyIntPropertyKey.DependencyProperty); }
}
public static readonly DependencyProperty ReadWriteStrProperty =
DependencyProperty.Register( "ReadWriteStr", typeof(string), typeof(foo),
new PropertyMetadata(ReadWriteStr_Changed));
public string ReadWriteStr
{
get { return (string)GetValue(ReadWriteStrProperty); }
set { SetValue(ReadWriteStrProperty, value); }
}
private static void ReadWriteStr_Changed(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
foo f = d as foo;
if (f != null)
{
int iVal;
if (int.TryParse(f.ReadWriteStr, out iVal))
f.SetValue( ReadOnlyIntPropertyKey, iVal);
}
}
}
It's not as bad as you suggest, IMHO...
You could get rid of the converter : IValueConverter is for bindings, you don't need it for conversions in code-behind. Apart from that, I don't see how you could make it more concise...
Yes, there is a clean way to "make a read-only DependencyProperty reflect the value of another property," but it may require a pretty fundamental shift in the overall property programming model of your app. In short, instead of using the DependencyPropertyKey to push values into the property, every read-only DependencyProperty can have a CoerceValue callback which builds its own value by pulling all the source values it depends on.
In this approach, the 'value' parameter that's passed in to CoerceValue is ignored. Instead, each DP's CoerceValue function recalculates its value "from scratch" by directly fetching whatever values it needs from the DependencyObject instance passed in to CoerceValue (you can use dobj.GetValue(...) for this if you want to avoid casting to the owner instance type).
Try to suppress any suspicion that ignoring the value supplied to CoerceValue may be wasting something. If you adhere to this model, those values will never be useful and the overall work is the same or less than a "push" model because source values that haven't changed are, as always, cached by the DP system. All that's changed is who's responsible for the calculation and where it's done. What's nice here is that calculation of each DP value is always centralized in one place and specifically associated with that DP, rather than strewn across the app.
You can throw away the DependencyPropertyKey in this model because you'll never need it. Instead, to update the value of any read-only DP you just call CoerceValue or InvalidateValue on the owner instance, indicating the desired DP. This works because those two functions don't require the DP key, they use the public DependencyProperty identifier instead, and they're public functions, so any code can call them from anywhere.
As for when and where to put these CoerceValue/InvalidateValue calls, there are two options:
Eager: Put an InvalidateValue call for the (target) DP in the PropertyChangedCallback of every (source) DP that's mentioned in the (target) DP's CoerceValueCallback function, --or--
Lazy: Always call CoerceValue on the DP immediately prior to fetching its value.
It's true that this method is not so XAML-friendly, but that wasn't a requirement of the OPs question. Considering, however, that in this approach you don't ever even need to fetch or retain the DependencyPropertyKey at all, it seems like it might one of the sleekest ways to go, if you're able to reconceive your app around the "pull" semantics.
In a completely separate vein, there's yet another solution that may be even simpler:
Expose INotifyPropertyChanged on your DependencyObject and use CLR properties for the read-only properties, which will now have a simple backing field. Yes, the WPF binding system will correctly detect and monitor both mechanisms--DependencyProperty and INotifyPropertyChanged--on the same class instance. A setter, private or otherwise, is recommended for pushing changes to this read-only property, and this setter should check the backing field to detect vacuous (redundant) changes, otherwise raising the old-style CLR PropertyChanged event.
For binding to this read-only property, either use the owner's OnPropertyChanged overload (for self-binding) to push in the changes from DPs, or, for binding from arbitrary external properties, use System.ComponentModel.DependencyPropertyDescriptor.FromProperty to get a DependencyPropertyDescriptor for the relevant souce DPs, and use its AddValueChanged method to set a handler which pushes in new values.
Of course for non-DP properties or non-DependencyObject instances, you can just subscribe to their INotifyPropertyChanged event to monitor changes that might affect your read-only property. In any case, no matter which way you push changes into the read-only property, the event raised by its setter ensures that changes to the read-only property correctly propagate onwards to any further dependent properties, whether WPF/DP, CLR, data-bound or otherwise.

Categories

Resources