1

I have custom control with dependency property and binding to ViewModel property only works like OneWayToSource. What did I mess up?

Binding

<local:MyControl SelectedItem="{Binding SelectedPage}"/>

ViewModel and control

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private ViewModelPageBase _selectedPage;
    public ViewModelPageBase SelectedPage
    {
        get { return _selectedPage; }
        set
        {
            _selectedPage = value;
            OnPropertyChanged();
        }
    }

    public void OnPropertyChanged([CallerMemberName] string property = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(property));
    }
}

public class MyControl : ContentControl
{
    public object SelectedItem
    {
        get { return (object)GetValue(SelectedItemProperty); }
        set
        {
            // never get here
            SetValue(SelectedItemProperty, value);
        }
    }
    public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
        "SelectedItem",
        typeof(object),
        typeof(MyControl),
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
}

Earlier I was using ItemsControl and binding was working two-way. Now, with MyControl, only if SelectedItem is changed, then SelectedPage get updated. Other way (setting SelectedPage somewhere) doesn't update SelectedItem.

Ideas?

Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • 1
    What is the OnPropertyChanged(); method? Show it. – Eugene Podskal Jun 26 '14 at 12:03
  • It works for other properties, but sure. This example is simplified, with only relevant parts (I hope). – Sinatr Jun 26 '14 at 12:05
  • As I understand you get the names of properties for PropertyChangedEventArgs through stack frames, don't you? – Eugene Podskal Jun 26 '14 at 12:06
  • @EugenePodskal, yep, 4.5 thingie. I tried specifying it like string already, still problem. – Sinatr Jun 26 '14 at 12:06
  • I'm not sure that your SelectedItem binding will have any change on the CustomControl behaviour. What does in the control handle the change of SelectedItem property? – Eugene Podskal Jun 26 '14 at 12:09
  • @EugenePodskal, setter is not called. So doesn't matter what I want to handle, it doesn't works. I put comment in code to show. – Sinatr Jun 26 '14 at 12:10
  • could you post a working sample which can reproduce the issue? – pushpraj Jun 26 '14 at 12:11
  • @pushpraj, I was hoping I did something wrong in binding, property, etc. There are much of code, posting all doesn't makes sense obviously. It could be problem elsewhere, but why it was working with `ItemsControl` with no problem? Should be related to custom control itself. – Sinatr Jun 26 '14 at 12:15
  • Works fine for me. Try registering a PropertyChanged handler with your dependencyProperty and check if it's really not changed or just an Problem in your Template. new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelecteItemChanged) – codeworx Jun 26 '14 at 12:22
  • @codeworx, `OnSelecteItemChanged` works, it gets there, but not in setter! Still have no clue what does that means and what to do. – Sinatr Jun 26 '14 at 12:25
  • If you change the value of the SelectedPage property in your ViewModel and the OnSelectedItemChanged Handler is called in your Control it means the Binding is working. – codeworx Jun 26 '14 at 12:27
  • Maybe you just don't see that the SelecteItem has changed because the Template of your control is wrong. – codeworx Jun 26 '14 at 12:29
  • @codeworx, I've no idea how template could be wrong and what template has to do with property. Could you explain better what you mean? Oh and check second William guth answer, do I really **must** use callback? oO – Sinatr Jun 26 '14 at 12:38
  • You have a custom control MyControl in your sample which is bound to a ViewModel Property. I'm just guessing but I think the MyControl is intendet to display the SelectedItem Value. To do so you need a ControlTemplate for your MyControl which knows how to Display the SelectedItem value. – codeworx Jun 26 '14 at 12:47
  • 1
    Yes, he's totally right. The Binding engine never sets the CLR property it always changes the underlying DependencyProperty. So does a Style Setter, Tigger or Animation. – codeworx Jun 26 '14 at 12:49
  • @codeworx, I display content by using code behind, that's why I am making control (to avoid code in mvvm). But I see what you mean now. Thanks for your help, that callback idea was really helpful. – Sinatr Jun 26 '14 at 12:57

3 Answers3

2

In the selectedItem Dependancyproperty declaration,

public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
        "SelectedItem",
        typeof(object),
        typeof(ViewNavigatorControl),
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));



typeof(ViewNavigatorControl) should be replaced by typeof(MyControl)

Let me know whether this resolves the issue

Sivasubramanian
  • 935
  • 7
  • 22
  • It's a cut from `ViewNavigatorControl`, I try to put only essential parts and did mistake when renaming it to `MyControl`, indeed >< Edited question, thanks. Problem is still. – Sinatr Jun 26 '14 at 12:08
  • ok, Post the XAML code which you tried to bind the properties – Sivasubramanian Jun 26 '14 at 12:21
2

i see your comment on the setter of the dependency property :

public object SelectedItem { get { return (object)GetValue(SelectedItemProperty); } set { // never get here SetValue(SelectedItemProperty, value); } }

I think you already know that, but in case you don't in Dependency Property the setter is never call, if you want to be notify of any change, you should add a PropertyChangedCallback in the third parameter of FrameworkPropertyMetadata

Setters not run on Dependency Properties?

Community
  • 1
  • 1
  • When I make dependency property, then I **must** use *callback* to update it for two-way binding? It doesn't match to [msdn](http://msdn.microsoft.com/en-us/library/ms752347.aspx): *TwoWay binding causes changes to either the source property or the target property to automatically update the other*. I am getting callback called, but property is not updated (via setter). Or do they mean that `static` property get updated? And I have to use callback to update control property? Sounds stupid and looks like extra work. – Sinatr Jun 26 '14 at 12:35
  • the property will be set, but if you set a breaking point in the setter, it won't be call. I don't know the rest of your code, so i hope you don't verify the binding by breaking point. – William guth Jun 26 '14 at 12:38
  • I am very confused with your last statement. How could property be set **without calling setter**??? – Sinatr Jun 26 '14 at 12:41
  • Actually, I see now what you mean. Lol. Thanks! – Sinatr Jun 26 '14 at 12:42
  • look at my link "Setters not run on Dependency Properties?" , the answer is in : The WPF binding engine calls GetValue and SetValue directly (bypassing the property setters and getters). You need the property to be there so it can be supported in the XAML markup (and compile correctly). – William guth Jun 26 '14 at 12:46
1

I see two problem:

Firstly you forgot the closed }:

SelectedItem="{Binding SelectedPage}"

Secondly on your Dependency Property, the third object should be your owner class. In your case MyControl

public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register(
     "SelectedItem",
     typeof(object),
     typeof(**ViewNavigatorControl**),
     new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

But maybe this mistake are only a bad copy/paste.

DIF
  • 2,470
  • 6
  • 35
  • 49
  • Thanks for spotting mistakes in code, it's only due to changing done to code here, while trying to reduce amount of code. Will fix them now. Problem is still. – Sinatr Jun 26 '14 at 12:16