does WPF binding bypass the codebehind setter for dependencyproperties? - c#

I may be doing this all wrong... so hang with me
I am making a user control with a property which the user can bind to. The in setter for the property, I bind the PropertyChanged listener to the property so I can react to changes to its state. The code behind for this user control looks like this:
public static readonly DependencyProperty NodeProperty =
DependencyProperty.Register("Node", typeof(MockRequirementWrapper), typeof(RecNode2));
public MockRequirementWrapper Node
{
get
{
return (MockRequirementWrapper)GetValue(NodeProperty);
}
set
{
if(Node != null)
Node.PropertyChanged -= Update;
SetValue(NodeProperty, value);
Node.PropertyChanged += new PropertyChangedEventHandler(Update);
OnPropertyChanged(this, "Node");
}
}
then, in another user control, I bind to this property a node I've created elsewhere like this:
<local:RecNode2 Node="{Binding}"/>
What I am finding is the recnode exists and is bound to a node... but if I put a breakpoint in the setter, it never gets called. Am I misunderstanding how the binding works? How do I add my listener when the node changes?

The framework will always call GetValue and SetValue directly, the property is just for convenience and sould never contain logic besides those calls.
If you want to do something on changes register a PropertyChangedCallback in the Metadata when registering the DependencyProperty.
Taken from http://msdn.microsoft.com/en-us/library/ms753358.aspx:
public static readonly DependencyProperty AquariumGraphicProperty = DependencyProperty.Register(
"AquariumGraphic",
typeof(Uri),
typeof(AquariumObject),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnUriChanged)
)
);
public Uri AquariumGraphic
{
get { return (Uri)GetValue(AquariumGraphicProperty); }
set { SetValue(AquariumGraphicProperty, value); }
}

Related

DependencyProperty not update [duplicate]

When I set the value of IsClosed during runtime, OnIsClosedChanged() is called fine.
However, the Designer sets the value of the property but does not call the OnIsClosedChanged().
public static DependencyProperty IsClosedProperty = DependencyProperty.Register("IsClosed", typeof(bool), typeof(GroupBox), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
public bool IsClosed {
get {
return (bool)this.GetValue(IsClosedProperty);
}
set {
if ((bool)this.GetValue(IsClosedProperty) == value)
return;
this.SetValue(IsClosedProperty, value);
OnIsClosedChanged();
}
}
private void OnIsClosedChanged() {
_rowDefContent.Height = new GridLength((IsClosed ? 0 : 1), GridUnitType.Star);
}
Obviously IsClosed is not modified by the Designer and only IsClosedProperty receives the xaml change.
My question is: How can I run IsClosed after the value has been modified in the Designer. Or at least add some logic to the non-runtime changes.
You would have to register a PropertyChangedCallback with property metadata.
The reason is that dependency properties set in XAML or by bindings or some other source do not invoke the CLR wrapper (the setter method). The reason is explained in the XAML Loading and Dependency Properties article on MSDN:
For implementation reasons, it is computationally less expensive to
identify a property as a dependency property and access the property
system SetValue method to set it, rather than using the property
wrapper and its setter.
...
Because the current WPF implementation of the XAML processor behavior
for property setting bypasses the wrappers entirely, you should not
put any additional logic into the set definitions of the wrapper for
your custom dependency property. If you put such logic in the set
definition, then the logic will not be executed when the property is
set in XAML rather than in code.
Your code should look like this:
public static readonly DependencyProperty IsClosedProperty =
DependencyProperty.Register(
"IsClosed", typeof(bool), typeof(GroupBox),
new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.AffectsRender,
(o, e) => ((GroupBox)o).OnIsClosedChanged()));
public bool IsClosed
{
get { return (bool)GetValue(IsClosedProperty); }
set { SetValue(IsClosedProperty, value); }
}
private void OnIsClosedChanged()
{
_rowDefContent.Height = new GridLength((IsClosed ? 0 : 1), GridUnitType.Star);
}
Found the answer myself now. ValidateValueCallback comes really close! (as Alex K has pointed out) But it is a static method and I don't get any reference to the instance which has been changed. The key is to use a PropertyChangedCallback in FrameworkPropertyMetadata which is also an argument passed to the Property.Register method.
See:
public static DependencyProperty IsClosedProperty = DependencyProperty.Register("IsClosed", typeof(bool), typeof(GroupBox), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(OnIsClosedChangedPCC)));
public bool IsClosed {
get {
return (bool)this.GetValue(IsClosedProperty);
}
set {
this.SetValue(IsClosedProperty, value);
OnIsClosedChanged();
}
}
private static void OnIsClosedChangedPCC(DependencyObject d, DependencyPropertyChangedEventArgs e) {
GroupBox current = (GroupBox)d;
current.IsClosed = current.IsClosed;
}
private void OnIsClosedChanged() {
_rowDefContent.Height = new GridLength((IsClosed ? 0 : 1), GridUnitType.Star);
}
That does now re-set the IsClosedValue which triggers the OnIsClosedChanged to run.
Thank's for your help guys!

Property not change on two way binding mode with dependency property

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.

Binding - callback is called twice

I've a simple control with dependency property like this
public class StatusProgress : Control
{
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(StatusProgress),
new FrameworkPropertyMetadata(null, (d, e) => (d as StatusProgress).TextUpdated(e.OldValue as string)));
private void TextUpdated(string text)
{
Trace.WriteLine("test");
}
}
then I have view model
public class ViewModelPageAnalyse : ViewModelPageBase
{
private string _progressText;
public string ProgressText
{
get { return _progressText; }
set
{
_progressText = value;
OnPropertyChanged(); // base class INotifyPropertyChanged implementation
}
}
}
Then there is a user control (displayed in window with ContentControl). User control is bound to view model with data template (maybe this is important)
<DataTemplate DataType="{x:Type local:ViewModelPageAnalyse}">
<local:UserControlAnalyse/>
</DataTemplate>
And this is the control in user control
<local:StatusProgress Text="{Binding ProgressText}"/>
Now the interesting part. When ProgressText in view model is set/changed, property changed callback is called twice. I see twice "test" in the debugger output window.
More interesting: when view is changed, for some reason callback is again called with e.NewValue = null, while there is nothing directly sets ProgressText to null.
I tried already to check if value is changed in the ProgressText setter before rising event, tried to set binding mode one-way, problem still - callback is called twice with same value, call stack looks same, but there are really a lot of calls within wpf to be really sure.
While double-shot is not a real issue, it bother me. Callback with null value is what my real problem (I think they are related). Anyone knows what is wrong?
Found a reason of the first problem: it was other control with Content. During transition it created a new Model (because Content is ViewModel) instead of reassigning existing user control. Totally my fault. Second problem still and I found this question (with workaround which is not suiting me).
Need help with
PropertyChanged callback is called with default value when ContentControl ViewModel is changed.
Which means null for Text in my case. Anyone? I couldn't figure out why is it called. My guess it is called by DataTemplate manager, if I can say so.
try to change this line:
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(StatusProgress),
new FrameworkPropertyMetadata(null, (d, e) => (d as StatusProgress).TextUpdated(e.OldValue as string)));
with this:
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(StatusProgress)
, new PropertyMetadata(""));

Default value on a custom WPF control

Here is my problem. I recently created a custom control, which works pretty well.
But i have a problem when i use it, i have a little problem :
In my control, i made a property named Value, defined like this :
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), new PropertyMetadata(1000));
public int Value
{
get
{
return (int)GetValue(ValueProperty);
}
set
{
SetValue(ValueProperty, value);
this.ValueText.Text = value.ToString();
}
}
When I do a databinding to this value, the binding works, but the default value is set to 1000, so it first print 1000. But actually, the property bound to Value isn't equal to 1000.
I would like to print in ValueText.Text the value of the bound property when the Value property is created.
Edit : Question is simple, how can I remove that default value and directly print the bound property ?
You should be able to setup a PropertyChanged event in your DependancyProperties metadata to update ValueText when Value changes.
somthing like this:
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown),
new PropertyMetadata(1000, (sender, e) => (sender as NumericUpDown).ValueText.Text = e.NewValue.ToString()));
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
The property setter will not get called as things change via WPF's data binding, so this technique will not work.
The default, initial value will always be 1000, but data binding may override it. You will need to add a Callback to appropriately notify you when the dependency property value is changed.
For details, see the Dependency Property Callbacks page to see how to implement a property changed callback correctly. This is the appropriate place to set your other (ValueText) property.

How to bind from a source property to a TARGET method

There are plenty of ways to bind a SOURCE method to target property, either by ValueConverter or by ObjectDataProvider. However, what if I want to have the binding affect the target METHOD?
Consider the following example:
class ListBoxViewModel
{
public static readonly DependencyProperty CurrentItemProperty = DependencyProperty.Register("CurrentItem", typeof (object), typeof (ListBoxViewModel));
public object CurrentItem
{
get { return (object) GetValue(CurrentItemProperty); }
set { SetValue(CurrentItemProperty, value); }
}
}
I'd like to bind the property CurrentItem to ListBox's CollectionView. However, since the CurrentItem property of CollectionView is read-only, I can't bind to it directly. Instead, I have to execute MoveCurrentToPosition function. How can I do it?
If there is a different way to do that - Without binding to a method, I'd love to hear it too, however, the main question is how to bind to a method, if not in this case, then in a similar one. If it is impossible, what is the best alternative? E.g one idea that comes to mind is subscribing to the change notification of the dependency property (CurrentItem in this case) and running the procedural code from that function.
Thanks!
You can register your property with a property changed callback in which you then can update the CollectionView manually:
public static readonly DependencyProperty CurrentItemProperty =
DependencyProperty.Register
(
"CurrentItem",
typeof(object),
typeof(ListBoxViewModel),
new UIPropertyMetadata(null, CurrentItemChanged)
);
public object CurrentItem
{
get { return (object)GetValue(CurrentItemProperty); }
set { SetValue(CurrentItemProperty, value); }
}
private static void CurrentItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Update current item logic
}

Categories