Hathors
Hathors

Reputation: 753

Dependency Property not set from ViewModel

I create a custom control "CustomAutoCompleteBox" (which inherit of AutoCompleteBox) with one dependency property "CurrentItem".

public static readonly DependencyProperty CurrentItemProperty =
        DependencyProperty.Register("CurrentItem", typeof(CityEntity), typeof(CustomAutoCompleteBox),
            new FrameworkPropertyMetadata(
                null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

    public CityEntity CurrentItem
    {
        get { return (CityEntity)GetValue(CurrentItemProperty); }
        set { SetValue(CurrentItemProperty, value); }
    }

This custom control have also a property "InternalCurrentItem".

public CityEntity InternalCurrentItem
    {
        get { return _internalCurrentCity; }

        set
        {
            if (_internalCurrentCity == value) return;

            _internalCurrentCity = value;
            OnPropertyChanged();

            CurrentItem = value;
        }
    }

The DataContext is define to himself in the constructor :

public VilleAutoCompleteBox()
    {
        DataContext = this;

        ...
    }

And the Style set ItemsSource and SelectedItem like this:

<Style TargetType="{x:Type infrastructure_controls:CustomAutoCompleteBox}" BasedOn="{StaticResource AutoCompleteBoxFormStyle}">
    <Setter Property="ItemsSource" Value="{Binding InternalItems, Mode=OneWay}" />
    <Setter Property="SelectedItem" Value="{Binding InternalCurrentItem, Mode=TwoWay}" />
    ...
</Style>

In summary, ItemsSource is bind to internal property "InternalItems" and SelectedItem is bind to internal property "InternalCurrentItem".

For use it, I declare this CustomAutoCompleteBox like this :

<infrastructure_usercontrols:CustomAutoCompleteBox Width="200" CurrentItem="{Binding DataContext.VmCurrentItem, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Mode=TwoWay}" />

I have bind the dependency property "CurrentItem" to the ViewModel's property "VmCurrentItem".

Everything works fine except for one thing.

When I type text in the control, the InternalCurrentItem property changes correctly. Same for the CurrentItem property in my ViewModel.

Concretely, InternalCurrentItem is correctly modified (Set). This property sets the CurrentItem dependency property, and this dependency property sets VmCurrentItem.

The opposite is not true. If I change directly the value of the VmCurrentItem property in the ViewModel, the CurrentItem property is not changed. I do not understand why.

Upvotes: 0

Views: 801

Answers (2)

Grx70
Grx70

Reputation: 10339

The first case causes the following chain of events:

  • SelectedItem is changed
  • InternalCurrentItem is updated by the framework due to the binding
  • You manually update CurrentItem in the InternalCurrentItem setter
  • VmCurrentItem is updated by the framework due to the binding

In the opposite direction this is what happens:

  • VmCurrentItem is changed
  • CurrentItem is updated by the framework due to the binding

...and that's it. There's no binding and no piece of code that would update InternalCurrentItem when CurrentItem changes. So what you need to do is to register a PropertyChangedCallback for your CurrentItemProperty which will update InternalCurrentItem:

public static readonly DependencyProperty CurrentItemProperty =
    DependencyProperty.Register(
        "CurrentItem",
        typeof(CityEntity),
        typeof(CustomAutoCompleteBox),
        new FrameworkPropertyMetadata
        {
            BindsTwoWayByDefault = true,
            PropertyChangedCallback = CurrentItemPropertyChanged
        });

private static void CurrentItemPropertyChanged(
     DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var control = (CustomAutoCompleteBox)d;
    control.InternalCurrentItem = (CityEntity)e.NewValue;
}

Upvotes: 1

Romano Zumb&#233;
Romano Zumb&#233;

Reputation: 8079

You need to declare the property the same way as the first:

public static readonly DependencyProperty InternalCurrentItemProperty =
        DependencyProperty.Register("InternalCurrentItem", typeof(CityEntity), typeof(CustomAutoCompleteBox),
            new FrameworkPropertyMetadata(
                null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

public CityEntity InternalCurrentItem
{
    get{ return (CityEntity)GetValue(InternalCurrentItemProperty); }

    set
    {
        SetValue(InternalCurrentItemProperty, value);
    }
}

Upvotes: 0

Related Questions