Binding to a usercontrol's DependencyProperty is not calling the setter - c#

My page xaml:
<header:DefaultText x:Name="header" HeaderText="{Binding Resources.HeaderTitle}"/>
My DefaultText.cs DependencyProperty:
public string HeaderText
{
get { return (string)GetValue(HeaderTextProperty); }
set
{ //This part is never called when binding, but it is with static text
SetValue(HeaderTextProperty, value);
SetText(value);
}
}
public readonly DependencyProperty HeaderTextProperty = DependencyProperty.Register("HeaderText", typeof(string), typeof(DefaultText), new PropertyMetadata(string.Empty));
My problem is that when I set the HeaderText property with a binding the setter isn't called, but when I use a normal string without binding it is called.
I already tried the answers of similar questions like: WPF Binding to variable / DependencyProperty

XAML binding internally doesn't call your Setter method but sets the dependency property's value directly, as is pointed out at MSDN:
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.
What you need to do is register a callback method that fires whenever the dependency property changes:
public static DependencyProperty HeaderTextProperty = DependencyProperty.Register(
"HeaderText",
typeof(string),
typeof(DefaultText),
new PropertyMetadata(string.Empty, PropertyChangedCallback)
);
private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
// this is the method that is called whenever the dependency property's value has changed
}

Related

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(""));

Attached property doesn't work unless I put "Property" at the end of its name

I have a simple attached property:
class TestAttached
{
public static readonly DependencyProperty TestProperty = DependencyProperty.RegisterAttached("TestProperty", typeof(string), typeof(TestAttached));
public static string GetTest(DependencyObject d)
{
return (string)d.GetValue(TestProperty);
}
public static void SetTest(DependencyObject d, string value)
{
d.SetValue(TestProperty, value);
TextBox tb = d as TextBox;
tb.Text = value;
}
}
and
<TextBox local:TestAttached.Test="Test" />
Nothing happens and no breakpoint is hit (which I put at SetTest) unless I set the first parameter of RegisterAttached to "TestProperty" instead of "Test". No tutorial I have found, including the one on MSDN, does this, and as far as I can tell, my code is the same as theirs and should work. What gives?
When Dependency property is get/set via XAML, wrapper method never gets called. So, you should avoid writing code there.
From MSDN:
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.
Instead you can use PropertyChangedCallback in case you want to put some code on property change of DP.
public static readonly DependencyProperty TestProperty =
DependencyProperty.RegisterAttached("Test", typeof(string),
typeof(TestAttached),
new PropertyMetadata(TestChanged));
public static void TestChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
// Place your code here
}
UPDATE
But if I change the name to "TestProperty" then the breakpoint gets
hit at SetTest.
Reason for that is: Test now behaves as normal CLR property. Property is nothing but instead Get/Set methods when you crack it down to IL code.
That's why setter gets hit like it does for normal CLR property.
If you try to bind with some other property,
<TextBox local:TestAttached.Test="{Binding SomeCLRProperty}" />
you will see application crashes stating:
A 'Binding' cannot be set on the 'SetTest' property of type 'TextBox'.
A 'Binding' can only be set on a DependencyProperty of a
DependencyObject.

Cant use control's dependency property

I have my custom control named "FileSelectDialog" with Dependency Property:
public static readonly DependencyProperty FilePathProperty =
DependencyProperty.Register("FilePath", typeof(string), typeof(FileSelectDialog));
public string FilePath
{
get { return (string)GetValue(FilePathProperty); }
set { SetValue(FilePathProperty, value); }
}
Then I'm trying to bind to this dependency property like this:
<controls:FileSelectDialog FilePath="{Binding FolderName}"/>
But nothing happing, no initial text shown in my control, no updated text's saving to 'FolderName' property! I got such error in Output window:
System.Windows.Data Error: 40 : BindingExpression path error: 'FolderName' property not found on 'object' ''FileSelectDialog' (Name='FolderSelector')'. BindingExpression:Path=FolderName; DataItem='FileSelectDialog' (Name='FolderSelector'); target element is 'FileSelectDialog' (Name='FolderSelector'); target property is 'FilePath' (type 'String')
So, as far as I understand, control try to find property 'FolderName on itself, while it must look for it in parents control DataContext. For example, when I use simple textbox:
<TextBox Text="{Binding Path=FolderName}"/>
All is working fine.
Seems a basic DataContext issue to me
How did you set the DataContext of your FileSelectDialog Control ? seems you set the dataContext in code as 'Me'/'this' or in xaml with 'RelativeSource Self' or something like this.
no initial text shown in my control
I understand you are exposing this property in a custom control, but are you updating some of the control in your customcontrol with the value set in your dependency property?
You may need to attach a callback and show the value set in your DP in some control in your customcontrol.
Like:
public static readonly DependencyProperty FilePathProperty = DependencyProperty.Register("FilePath", typeof(string), typeof(FileSelectDialog), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None,HandleFilePathPropertyChanged));
private static void HandleFilePathPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control= (FileSelectDialog)d;
control.SomeUIControl.Text= (string)e.NewValue;
}
If already doing this, then the second problem is the error shown in Binding. For this try setting the DataContext of your control to the object which has the source property.
<controls:FileSelectDialog x:Name="customControl" FilePath="{Binding FolderName}"/>
... code-behind.
customControl.DataContext = sourceObject.
You have to witre the property changed call back function
public static readonly DependencyProperty FilePathProperty =
DependencyProperty.Register("FilePath", typeof(string),
typeof(FileSelectDialog),new UIPropertyMetadata(
new PropertyChangedCallback(PropertyChanged)));
private static void PropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
//Do your Stuff
}

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
}

does WPF binding bypass the codebehind setter for dependencyproperties?

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); }
}

Categories