WPF custom usercontrol property binding always default value [duplicate] - wpf

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!

Related

Custom Dependency Property only fire once

I created a dependency property on a custom user control and problem is that the "OnPeriodTypeChangedHandler" Property Change Call Back only fire once when control is created for the first time, subsequently if I try to call the property via Binding it doesn't fire at all. Any ideas?
#region PeriodType
public static readonly DependencyProperty PeriodTypeProperty =
DependencyProperty.Register(
"PeriodType",
typeof(PeriodTypeEnum),
typeof(Period),
new FrameworkPropertyMetadata(
PeriodTypeEnum.None,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnPeriodTypeChangedHandler)
)
);
public static void OnPeriodTypeChangedHandler(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
// Get instance of current control from sender
// and property value from e.NewValue
// Set public property on TaregtCatalogControl, e.g.
((Period)sender).PeriodType = (PeriodTypeEnum)e.NewValue;
if ((PeriodTypeEnum)e.NewValue == PeriodTypeEnum.Month)
((Period)sender).Periods = ((Period)sender).GetMonthlyPeriods();
if ((PeriodTypeEnum)e.NewValue == PeriodTypeEnum.Quarter)
((Period)sender).Periods = ((Period)sender).GetQuarterlyPeriods();
}
public PeriodTypeEnum PeriodType
{
get
{
return (PeriodTypeEnum)GetValue(PeriodTypeProperty);
}
set
{
SetValue(PeriodTypeProperty, value);
}
}
#endregion
In case you want your DP to bind TwoWay by default, you can specify it at time of DP registration using FrameworkPropertyMetadataOptions.BindsTwoWayByDefault. This way you don't have to set mode to TwoWay at time of binding.
public static readonly DependencyProperty PeriodTypeProperty =
DependencyProperty.Register(
"PeriodType",
typeof(string),
typeof(MyTextBox),
new FrameworkPropertyMetadata(
PeriodTypeEnum.None,
FrameworkPropertyMetadataOptions.AffectsRender
| FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, <- HERE
new PropertyChangedCallback(OnPeriodTypeChangedHandler)
)
);
I think I found the solution in this particular instance by changing the Mode property to "TwoWay" in ViewModel for its data binding property.

WPF Usercontrol Property Intitialization

I am playing around with WPF Usercontrols and have the following question: why does the behaviour of property initialization/assignment change after a property is made to a DependencyProperty?
Let me briefly illustrate:
Consider this code for a UserControl class:
public partial class myUserControl : UserControl
{
private string _blabla;
public myUserControl()
{
InitializeComponent();
_blabla = "init";
}
//public static DependencyProperty BlaBlaProperty = DependencyProperty.Register(
// "BlaBla", typeof(string), typeof(UserControlToolTip));
public string BlaBla
{
get { return _blabla; }
set { _blabla = value; }
}
}
And this is how the UserControl is initialized in the XAML file:
<loc:myUserControl BlaBla="ddd" x:Name="myUsrCtrlName" />
The problem I have is that the line set { _blabla = value; } is called ONLY when the DependencyProperty declaration is commented out (as per this example). However when the DependencyProperty line becomes part of the program the set { _blabla = value; } line is no longer called by the system.
Can some please explain this strange behavior to me?
Thanks a million!
The CLR wrapper (getter and setter) of a dependency property should only be used to call the GetValue and SetValue methods of the dependency property.
e.g.
public string BlaBla
{
get { return (string)GetValue(BlaBlaProperty) }
set { SetValue(BlaBlaPropert, value); }
}
and Nothing more...
The reason for this is that the WPF binding engine calls GetValue and SetValue directly (e.g. without calling the CLR wrapper) when binding is done from the XAML.
So the reason you don't see them called is because they really aren't and that is precisely the reason why you shouldn't add any logic to the CLR Get and Set methods.
Edit
Based on OPs comment - here is an example of creating a callback method when the DependencyProperty changes:
public static DependencyProperty BlaBlaProperty =
DependencyProperty.Register("BlaBla", typeof(string), Typeof(UserControlToolTip),
new FrameworkPropertyMetadata(null, OnBlachshmaPropertyChanged));
private static void OnBlachshmaPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UserControlToolTip owner = d as UserControlToolTip;
if (owner != null)
{
// Place logic here
}
}

DependencyProperty not triggered

I defined property in my usercontrol like this:
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set
{
SetValue(IsSelectedProperty, value);
StackPanelDetails.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
}
}
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof (bool), typeof (ucMyControl));
But when I set its property in xaml, it want trigger it (set is not called).
<DataTemplate><local:ucTopicItem IsSelected="False" /></DataTemplate>
What could be the problem?
The setter of your dependency property will not be called when the property is set in XAML. WPF will instead call the SetValue method directly.
See MSDN XAML Loading and Dependency Properties for an explanation why the setter is not called.
You would have to register a PropertyChangedCallback with property metadata.
You should use a property changed handler in your dependency property directly. This way you ensure that it gets called when set in XAML:
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof(bool), typeof(ucMyControl), new PropertyMetadata(false, OnIsSelectedChanged));
private static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Implement change logic
}

How to bind to a custom property in a Silverlight Custom control

I've created a custom control with, amongst others, the following:
public partial class MyButton : UserControl
{
public bool Enabled
{
get { return (bool)GetValue(EnabledProperty); }
set {
SetValue(EnabledProperty, value);
SomeOtherStuff();
}
}
}
public static readonly DependencyProperty EnabledProperty =
DependencyProperty.Register("Enabled", typeof(bool), typeof(MyButton), new PropertyMetadata(true));
public static void SetEnabled(DependencyObject obj, bool value)
{
obj.SetValue(EnabledProperty, value);
}
public static bool GetEnabled(DependencyObject obj)
{
return (bool) obj.GetValue(EnabledProperty);
}
}
In my XAML, I (try to) use binding to set the Enabled property:
<MyButton x:Name="myButtom1" Enabled="{Binding CanEnableButton}"/>
I know the bind between my control and the underlying data model is valid and working as I can bind 'IsEnabled' (a native property of the underlying UserControl) and it works as expected. However, my Enabled property is never set via the above binding. I've put breakpoints on my property set/get and they never get hit at all.
I can only imaging I've missed something relating to binding in my custom control. Can anyone see what?
I've tried implementing INotifyPropertyChanged on my control (and calling the PropertyChanged event from my Enabled setter) ... but that didn't fix it.
[ BTW: In case you are wondering "Why?": I can't intercept changes to the IsEnabled state of the base control, so I decided to implement and use my own version of a Enable/disable property (which I called Enabled) - one where I could plug my own code into the property setter ]
First of all drop the SetEnabled and GetEnabled pair, these only make sense for an attached property which is not what you are doing.
Now your main problem is that you are under the false assumption that the get/set members of your propery get called during binding, they don't.
What you need is to pass a call back method in the property meta data, it's here that you intercept changes and take other actions like so:-
public bool IsEnabled
{
get { return (bool)GetValue(IsEnabledProperty); }
set { SetValue(IsEnabledProperty, value); }
}
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.Register(
"IsEnabled",
typeof(bool),
typeof(MyButton),
new PropertyMetadata(true, OnIsEnabledPropertyChanged));
private static void OnIsEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyButton source = d as MyButton;
source.SomeOtherStuff();
}
private void SomeOtherStuff()
{
// Your other stuff here
}
With this in place regardless of how the propery is changed the SomeOtherStuff procedure will execute.
I'd suggest using the IsEnabledChanged event which is part of every Control/UserControl.
That would allow you to hook up to the event and do whatever actions you want to take.
public MainPage()
{
InitializeComponent();
this.IsEnabledChanged += new DependencyPropertyChangedEventHandler(MainPage_IsEnabledChanged);
}
void MainPage_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// Do SomeStuff
}

Why doesn't this Silverlight 4 DependencyObject's DependencyProperty getting data bound?

I have no idea why data binding is not happening for certain objects in my Silverlight 4 application. Here's approximately what my XAML looks like:
<sdk:DataGrid>
<u:Command.ShortcutKeys>
<u:ShortcutKeyCollection>
<u:ShortcutKey Key="Delete" Command="{Binding Path=MyViewModelProperty}"/>
</u:ShortcutKeyCollection>
</u:Command.ShortcutKeys>
</sdk:DataGrid>
The data context is set just fine since other data bindings that I have set on the grid are working just fine. The Command.ShortcutKeys is an attached DependencyProperty that is declared as follows:
public static readonly DependencyProperty ShortcutKeysProperty = DependencyProperty.RegisterAttached(
"ShortcutKeys", typeof(ShortcutKeyCollection),
typeof(Command), new PropertyMetadata(onShortcutKeysChanged));
private static void onShortcutKeysChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var shortcuts = args.NewValue as ShortcutKeyCollection;
if (obj is UIElement && shortcuts != null)
{
var element = obj as UIElement;
shortcuts.ForEach(
sk => element.KeyUp += (s, e) => sk.Command.Execute(null));
}
}
public static ShortcutKeyCollection GetShortcutKeys(
DependencyObject obj)
{
return (ShortcutKeyCollection)obj.GetValue(ShortcutKeysProperty);
}
public static void SetShortcutKeys(
DependencyObject obj, ShortcutKeyCollection keys)
{
obj.SetValue(ShortcutKeysProperty, keys);
}
I know this attached property is working just fine since the event handlers are firing. However, the Command property of the ShortcutKey objects are not getting data bound. Here's the definition of ShortcutKey:
public class ShortcutKey : DependencyObject
{
public static readonly DependencyProperty KeyProperty = DependencyProperty.Register(
"Key", typeof(Key), typeof(ShortcutKey), null);
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command", typeof(ICommand), typeof(ShortcutKey), null);
public Key Key
{
get { return (Key)GetValue(KeyProperty); }
set { SetValue(KeyProperty, value); }
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
}
public class ShortcutKeyCollection : ObservableCollection<ShortcutKey> { }
The property that is getting bound to has its value set in the constructor of my view model, and its type is ICommand. So why isn't my Command property getting data bound? Also, have you found an effective way to debug data binding issues in Silverlight?
Edit:
At least one thing that was wrong was that ShortcutKey derived from DependencyObject instead of FrameworkElement, which is apparently the only root class that binding can be applied to. However, even after that change, the binding continued to not work properly.
You need to specify the Source of the Binding, since the DataContext is not inherited by members of the ObservableCollection.
edit:
Try setting the ShortcutKey.DataContext in onShortcutKeysChanged:
private static void onShortcutKeysChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var shortcuts = args.NewValue as ShortcutKeyCollection;
if (obj is FrameworkElement && shortcuts != null)
{
var element = obj as FrameworkElement;
ForEach(ShortcutKey sk in shortcuts)
{
sk.DataContext = element.DataContext;
element.KeyUp += (s, e) => sk.Command.Execute(null));
}
}
}
It looks like unless an object is inserted into the visual tree, no DataContext inheritance takes place, and thus no data binding works. I couldn't find a way to get the container's data context to be passed to the ShortcutKey objects, so as a workaround, I set up the binding in the code behind.
Hopefully someone else has a different answer that will show me how I won't have to resort to setting up this data binding in the code.

Resources