【问题标题】:Dependency Property not set from ViewModel未从 ViewModel 设置依赖属性
【发布时间】:2017-06-01 07:50:53
【问题描述】:

我用一个依赖属性“CurrentItem”创建了一个自定义控件“CustomAutoCompleteBox”(继承自 AutoCompleteBox)。

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;

        ...
    }

Style 设置 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 属性不会更改。我不明白为什么。

【问题讨论】:

  • 请注意,通常不应将控件的 DataContext 设置为自身,因为它会阻止控件继承其父控件或窗口的 DataContext。当您查看 CurrentItem 绑定的复杂性时,您可以轻松找到此规则的证明。最好用RelativeSource 编写控件的“内部”绑定。参见例如这个答案:stackoverflow.com/a/28982771/1136211
  • 我已按照您的建议更新了我的代码。这更干净,但不能解决问题。

标签: c# wpf xaml binding custom-controls


【解决方案1】:

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

  • SelectedItem改了
  • InternalCurrentItem 由于绑定而被框架更新
  • 您在InternalCurrentItem 设置器中手动更新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;
}

【讨论】:

  • 感谢您非常清晰准确的回答。但我带来了一点修正。在相反的方向上, CurrentItem 没有更新 :( 那是我不明白的一点。如果 CurrentItem 被正确修改,我将不得不像您的示例一样手动更改 InternalCurrentItem。
  • 是的,我刚刚注意到了。您的视图模型是否提高了PropertyChanged?您是否在输出窗口中收到任何与绑定相关的消息?此外,如果您使用某种视图模型注入,请确保您正在修改您的控件绑定到的同一视图模型实例。
  • 是的,ViewModel 提升 PropertyChanged : public CityEntity VmCurrentItem { get { return _vmCurrentItem; } 设置 { if (_vmCurrentItem == value) 返回; _vmCurrentItem = 值; OnPropertyChanged(() => VmCurrentItem); } }
  • 输出窗口中没有任何内容。
  • 我已经通过添加 PropertyChangedCallBack 解决了这个问题。
【解决方案2】:

你需要和第一个一样声明属性:

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

【讨论】:

  • 小额外精度:当我直接更改 ViewModel 中 VmCurrentItem 属性的值时,CurrentItem 属性不会更改。然而事实恰恰相反。当我修改 CurrentItem 时,VmCurrentItem 发生了变化。问题不是来自 InternalCurrentItem。
猜你喜欢
  • 1970-01-01
  • 2016-03-28
  • 1970-01-01
  • 2015-03-19
  • 1970-01-01
  • 1970-01-01
  • 2013-02-01
  • 2017-05-04
  • 2012-06-01
相关资源
最近更新 更多