DependencyProperty Strange Behaviour - c#

I tried this out just to see what will happen and it did work but I have no idea why so. Can somebody explain me what is happening in the background of DependencyProperties?
I have a class which declares a DependencyProperty but then in another class I target that DependencyProperty by using GetValue and SetValue.
Here is an example:
public class DependencyProperties : DependencyObject
{
public Size EstimatedSize
{
get { return (Size)GetValue(EstimatedSizeProperty); }
set { SetValue(EstimatedSizeProperty, value); }
}
public static readonly DependencyProperty EstimatedSizeProperty =
DependencyProperty.Register("EstimatedSize", typeof(Size), typeof(DependencyProperties), null);
}
public class MyControl: ContentControl
{
public Size CalculatedSize
{
get { return (Size)GetValue(DependencyProperties.EstimatedSizeProperty); }
set { SetValue(DependencyProperties.EstimatedSizeProperty, value); }
}
protected override OnApplyTemplate()
{
// This works but why? How is it possible to do this? What is happening under the hood?
this.CalculatedSize = new Size(123, 123);
}
}
Why is it possible to do this? What is happening in the background of this example? The MyControl class didnt register the DP but it can use it. Can somebody tell me what is happening under the hood?

I googled the image that I'd like to show you.
See the link below, and the concept of DP is well documented.
http://www.abhisheksur.com/2011/07/internals-of-dependency-property-in-wpf.html
And, let's go to the bottom-line directly, when you invite and use MyControl in your app, contained DP is automatically registered.
This is why DP is use static prefix. For the reason of static readonly in DP declaration, kindly read https://stackoverflow.com/a/5610015/361100 link (Priyank's quoted answer).

Related

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

How to use a programmatic 'get' equivalent in a WPF dependency property?

Where this is the clr way of writing a property:
public byte Value
{
get{
return GetByteData();
}
set{
SetByteData(value);
}
}
I've read up on how to do the same the dependency property way, and this is all I could do by myself:
public byte Value
{
get { return (byte)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value", typeof(byte), typeof(MyControl),
new FrameworkPropertyMetadata((byte)0, ValueChanged));
public static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
byte r = (byte)e.NewValue;
MyControl v = (MyControl)d;
v.SetByteData(r);
}
With the examples I've looked at, from which I've made the above snippet, I can't find a place to put the GetByteData(), which calculates an output value for the current UI-state when the user interacts, so as to update the 'Value'.
Until previously I was putting it in the getter anyway like with clr, but I get the feeling that it's the wrong approach, but I could be wrong. Where should I be putting it? If not, what should be my approach? Is it at all possible to have a programmatic getter for a dependency property?
It's possible that I've been using the wrong keywords to look for a solution. Any help putting me in the right direction would be much appreciated. Thanks in advance.
As long as it is only the source (and not the target) property of a Binding, as in
{Binding Value, ElementName=MyControlName}
and you don't want to apply a value by a Style Setter, or animate the value, the property does not need to be a dependency property.
Just implement INotifyPropertyChanged like this:
public partial class MyControl : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public byte Value
{
get { return GetByteData(); }
set
{
SetByteData(value);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));
}
}
...
}

nested property propertychanged event only fired once in usercontrol in silverlight

There are two Viewmodels, both of them had implemented the INotifyPropertyChanged interface (I have called the OnpropertyChanged("propertyname") in my actual code).
Public Class A{
public B BProperty
{
get
{
return _BProperty;
}
set
{
if (_BProperty != null)
_BProperty.PropertyChanged -= _BProperty_PropertyChanged;
_BProperty = value;
OnPropertyChanged("BProperty");
if (_BProperty != null)
_BProperty.PropertyChanged += _BProperty_PropertyChanged;
}
}
void _BProperty_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "status")
{
OnPropertyChanged("BProperty");
}
}
B _BProperty;
}
Public Class B
{
public int status{get;set;}
}
I also had a userControl:
<MyUserControl ...
... >
<Grid>
</Grid>
</MyUserControl>
And I had a dependencyProperty:
/// <summary>
/// File status
/// </summary>
public int Filestatus
{
get { return (int)GetValue(FilestatusProperty); }
set { SetValue(FilestatusProperty, value); }
}
public static readonly DependencyProperty FilestatusProperty =
DependencyProperty.Register(
"Filestatus",
typeof(int),
typeof(MyUserControl),
new PropertyMetadata(0, OnFilestatusPropertyChanged));
private static void OnFilestatusPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyUserControl control = (MyUserControl)d;
if (e.NewValue != null)
{
}
}
edit:2015/09/21
Add the get/set methods:
public static readonly DependencyProperty FileStatusProperty = DependencyProperty.RegisterAttached(
"FileStatus", typeof(int), typeof(FileStatusIconControl), new PropertyMetadata(0, PropertyChangedCallback));
public static int GetFileStatus(DependencyObject source)
{
return (int)source.GetValue(FileStatusProperty);
}
public static void SetFileStatus(DependencyObject target, int value)
{
target.SetValue(FileStatusProperty, value);
}
private static void PropertyChangedCallback(
DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
FileStatusIconControl fsic = dependencyObject as FileStatusIconControl;
if(fsic != null)
wahahahahaha;
}
edit end
I used this userControl in my mainPage like this:
<mainPage ...
...>
<Grid>
<MyUserControl Filestatus={Binding Bproperty.status} />
<TextBox Text={Binding Bproperty.status} />
</Grid>
</mainPage>
The datacontext of mainPage is an instance of Class A.
My question is:
When status is changed, the text of textbox is changed, but the OnFilestatusPropertyChanged method only was called once when Usercontrol is loaded. Why?
Thanks.
I will start by saying that while I was looking into your question I ran into some problems with the code you have provided. I appreciate that somewhere you have some real code which has a problem and you cannot share this code with us so have tried to reproduce the problem in a smaller set of files.
However, if you do do this, please at least verify that the code you have provided runs and exhibits the problem. It is evident that you haven't tried to run your sample code (particularly the XAML) as there are problems with it:
attribute values should be surrounded in double-quotes,
the binding path Bproperty.status should be BProperty.status (first P capitalised).
All these things slow down someone trying to help you. Worse still, when I do find a problem with your code I can't be sure whether it's a genuine problem that your real code also has or whether it's something you introduced putting together your sample code. So all I can do is point out all the problems I find in the hope that one of them is the problem you have in your real code.
Firstly, your TextBox's Text property binding doesn't contain Mode=TwoWay. In WPF, this binding is TwoWay by default, but in Silverlight all bindings are OneWay by default. If you are familiar with WPF, this may be why you omitted Mode=TwoWay.
Secondly, I don't see why you have implemented class B as you have, apparently leaving it up to class A to fire property-changed events on its behalf. This approach doesn't work: when Silverlight updates the value in the status property of a B instance, it does so by calling the status setter of the B instance. Your B class has only an autogenerated property setter, which certainly doesn't fire the PropertyChanged event. Because this event wasn't fired, Silverlight doesn't know that is has some updates to do, and furthermore your A class isn't aware that it has changed either.
I would implement INotifyPropertyChanged in the usual way in class B, by calling OnPropertyChanged in the status setter. I would also remove the BProperty_PropertyChanged event handler in class A as I don't think it does you any favours.

Correct way to check for property change in design mode C# WPF?

Ok, so I'm trying to run some code that modifies a UI when a user changes a custom control's dependency property value in design mode; but only when in design mode.
I've tried these approaches:
1.
public static DependencyProperty x = ...Register(..., new PropertyMetadata(null, changeMethod));
2.
set { SetValue(XProp, value); changeMethod(value); }
3.
var observable = x as INotifyPropertyChanged;
observable.PropertyChanged += ObservablePropertyChanged;
But all of them seem to have their own issues in that they either trigger errors or don't work at all.
So does anyone know what the correct way to listen to a dependency property change in design mode is, and if so can you give an example?
The right way to handle DependencyProperty changes is to:
. Declare the DependencyProperty:
public static DependencyProperty MyXProperty;
. Create the public get/set Property:
public string MyX
{
get { return (string)GetValue(MyXProperty); } //Supposing that the property type is string
set { SetValue(MyXProperty, value); }
}
. Register the DependencyProperty in your static constructor:
static MyClass()
{
MyXProperty= DependencyProperty.Register("MyX", typeof(string), typeof(MyClass), new FrameworkPropertyMetadata("", OnMyXPropertyChanged));
}
. Declare the Property Changed Method:
private static void OnMyXPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyClass thisClass = d as MyClass ;
//Do Something
}
Please provide more information if you still can't find your solution.

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.

Categories

Resources