依赖项 属性 未从 ViewModel 设置

Dependency Property not set from ViewModel

我创建了一个自定义控件 "CustomAutoCompleteBox"(继承自 AutoCompleteBox),具有一个依赖项 属性 "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); }
    }

此自定义控件还有一个 属性 "InternalCurrentItem"。

public CityEntity InternalCurrentItem
    {
        get { return _internalCurrentCity; }

        set
        {
            if (_internalCurrentCity == value) return;

            _internalCurrentCity = value;
            OnPropertyChanged();

            CurrentItem = value;
        }
    }

DataContext 在构造函数中为自己定义:

public VilleAutoCompleteBox()
    {
        DataContext = this;

        ...
    }

样式设置 ItemsSource 和 SelectedItem 如下:

<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>

总而言之,ItemsSource 绑定到内部 属性 "InternalItems",SelectedItem 绑定到内部 属性 "InternalCurrentItem".

为了使用它,我这样声明这个 CustomAutoCompleteBox :

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

我已经将依赖项 属性 "CurrentItem" 绑定到 ViewModel 的 属性 "VmCurrentItem".

除一件事外一切正常。

当我在控件中键入文本时,InternalCurrentItem 属性 会正确更改。与我的 ViewModel 中的 CurrentItem 属性 相同。

具体来说,InternalCurrentItem 被正确修改(设置)。此 属性 设置 CurrentItem 依赖项 属性,此依赖项 属性 设置 VmCurrentItem.

反之则不然。如果我直接更改 ViewModel 中 VmCurrentItem 属性 的值,则 CurrentItem 属性 不会更改。我不明白为什么。

您需要以与第一个相同的方式声明 属性:

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

第一种情况导致以下事件链:

  • SelectedItem 已更改
  • InternalCurrentItem 由于绑定
  • 由框架更新
  • 您在 InternalCurrentItem setter
  • 中手动更新 CurrentItem
  • VmCurrentItem 由于绑定
  • 由框架更新

相反的方向是这样的:

  • VmCurrentItem 已更改
  • CurrentItem 由于绑定
  • 由框架更新

...就是这样。当 CurrentItem 更改时,没有任何绑定,也没有任何代码会更新 InternalCurrentItem。因此,您需要做的是为您的 CurrentItemProperty 注册一个 PropertyChangedCallback,这将更新 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;
}