WPF:PropertyChangedCallback 仅触发一次
我有一个用户控件,它公开一个名为 VisibileItems 的 DependencyProperty 每次更新该属性时,我都需要触发另一个事件。 为了实现这一点,我添加了带有 PropertyChangedCallback 事件的 FrameworkPropertyMetadata。
由于某种原因,此事件仅被调用一次,并且下次 VisibleItems 更改时不会触发。
XAML:
<cc:MyFilterList VisibleItems="{Binding CurrentTables}" />
CurrentTables 是 MyViewModel 上的 DependencyProperty。 CurrentTables 经常更改。我可以将另一个 WPF 控件绑定到 CurrentTables,并且可以看到 UI 中的更改。
将 VisibleItems 与 PropertyChangedCallback 连接的方式
public static readonly DependencyProperty VisibleItemsProperty =
DependencyProperty.Register(
"VisibleItems",
typeof(IList),
typeof(MyFilterList),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(VisiblePropertyChanged))
);
public IList VisibleItems {
get { return (IList)GetValue(VisibleItemsProperty); }
set { SetValue(VisibleItemsProperty, value); }
}
这是我通过步入 VisiblePropertyChanged ,我可以看到它在第一次设置 CurrentTables 时被触发。但随后的时间就不会了。
更新,
当你们中的一些人质疑 CurrentTables 的修改方式时,它会在更改时完全重新分配:
OnDBChange()...
CurrentTables = new List<string>(MainDatabaseDataAdapter.GetTables(this.SelectedServer, this.SelectedDatabase));
每次更改都会调用此行,但我的 VisiblePropertyChanged 处理程序仅在第一次时被调用。
更新
如果我直接分配 VisibleItems,处理程序每次都会被调用!
TestFilterList.VisibleItems = new List<string>( Enumerable.Range(1, DateTime.Now.Second).ToList().Select(s => s.ToString()).ToList() );
因此,问题似乎源于 DependencyProperty (VisibleItems) 监视另一个 DependencyProperty (CurrentTables)。不知何故,绑定适用于第一个属性更改,但不适用于后续属性更改? 正如你们中的一些人所建议的,尝试用窥探来检查这个问题。
I have a user control, which exposes a DependencyProperty called VisibileItems
Every time that property gets updated, i need to trigger another event.
To achieve that, i added a FrameworkPropertyMetadata with PropertyChangedCallback event.
For some reason, this event gets called only once, and doesn't trigger the next time VisibleItems is changed.
XAML:
<cc:MyFilterList VisibleItems="{Binding CurrentTables}" />
CurrentTables is a DependencyProperty on MyViewModel. CurrentTables gets changed often. I can bind another WPF control to CurrentTables, and i see the changes in the UI.
Here is the way i wired VisibleItems with PropertyChangedCallback
public static readonly DependencyProperty VisibleItemsProperty =
DependencyProperty.Register(
"VisibleItems",
typeof(IList),
typeof(MyFilterList),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(VisiblePropertyChanged))
);
public IList VisibleItems {
get { return (IList)GetValue(VisibleItemsProperty); }
set { SetValue(VisibleItemsProperty, value); }
}
by stepping into VisiblePropertyChanged, i can see that it gets triggered the first time CurrentTables gets set. but not subsequent times.
UPDATE
as some of you questioned the way CurrentTables is modified, it is re-assigned completely on change:
OnDBChange()...
CurrentTables = new List<string>(MainDatabaseDataAdapter.GetTables(this.SelectedServer, this.SelectedDatabase));
this line gets called on every change, but my VisiblePropertyChanged handler gets called only the first time.
UPDATE
if i assign VisibleItems directly, the handler does get called every time!
TestFilterList.VisibleItems = new List<string>( Enumerable.Range(1, DateTime.Now.Second).ToList().Select(s => s.ToString()).ToList() );
So, it looks like the problem stems from the DependencyProperty (VisibleItems) watching another DependencyProperty (CurrentTables). Somehow the binding works on first property change, but not on subsequent ones?
Attempting to inspect this issue with snoop as some of you suggested.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您是否正在为也具有
OneWay
绑定的依赖项属性设置“本地”值(即直接分配给依赖项属性设置器)?如果是这样,设置本地值将删除绑定,如 MSDN 依赖属性概述:当依赖属性机制被要求在依赖属性上存储本地值时,它没有太多其他可以做的事情。它无法通过绑定发送值,因为绑定“指向”错误的方向。设置为本地值后,它不再显示从绑定获得的值。由于它不再显示绑定的值,因此它删除了绑定。
绑定消失后,当绑定的源属性更改其值时,将不再调用
PropertyChangedCallback
。这可能就是回调未被调用的原因。如果您将绑定设置为TwoWay,则绑定系统确实有地方可以存储您设置的“本地”值:在绑定的源属性中。在这种情况下,无需消除绑定,因为依赖属性机制可以将值存储在源属性中。
这种情况不会导致堆栈溢出,因为会发生以下情况:
PropertyChanged
,PropertyChanged
事件,检查源属性的新值,发现它没有改变并且不做任何进一步的事情。这里的关键点是,如果您为值未更改的属性触发
PropertyChanged
事件,则依赖项属性上的任何PropertyChangedCallback
都会绑定您的财产将不会被调用。为了简单起见,我忽略了上面的
IValueConverter
。如果您有转换器,请确保它能够正确地双向转换值。我还假设另一端的属性是实现 INotifyPropertyChanged 的对象上的视图模型属性。绑定的源端可能存在另一个依赖属性。依赖属性机制也可以处理这个问题。碰巧的是,WPF(和 Silverlight)不包含堆栈溢出检测。如果在
PropertyChangedCallback
中,您将依赖属性的值设置为与其新值不同(例如,通过递增整数值属性或将字符串附加到字符串值属性),则您可以将会出现堆栈溢出。Are you setting a 'local' value (i.e. assigning directly to a dependency property setter) to a dependency property that also has a
OneWay
binding on it? If so, setting the local value will remove the binding, as mentioned on the the MSDN dependency property overview:The dependency property mechanism doesn't have much else it can do when it gets asked to store a local value on a dependency property. It can't send the value through the binding because the binding 'points' the wrong way. After being set to the local value, it's no longer showing the value it got from the binding. Since it's not showing the value from the binding any more, it removes the binding.
Once the binding's gone, the
PropertyChangedCallback
will no longer get called when the source property for the binding changes its value. This may be why the callback isn't being called.If you set the binding to be
TwoWay
, the binding system does have somewhere to store the 'local' value you've set: in the binding's source property. In this case, there's no need to eliminate the binding as the dependency property mechanism can store the value in the source property.This situation does not cause a stack-overflow because the following happens:
PropertyChanged
,PropertyChanged
event, checks new value of source property, finds that it hasn't changed and does nothing further.The key point here is that if you fire a
PropertyChanged
event for a property whose value hasn't changed, anyPropertyChangedCallback
s on dependency properties bound to your property will not be called.For simplicity I've ignored
IValueConverter
s in the above. If you do have a converter, make sure that it is correctly converting values in both directions. I've also assumed that the property at the other end is a view-model property on an object implementingINotifyPropertyChanged
. There could have been another dependency property at the source end of the binding. The dependency property mechanism can handle that as well.As it happens, WPF (and Silverlight) contain no detection of stack overflows. If, in a
PropertyChangedCallback
, you set the value of the dependency property to be different to its new value (e.g. by incrementing an integer-valued property or appending a string to a string-valued property), you will get a stack overflow.我的代码中也有同样的问题,卢克是对的。我在 PropertyChangedCallback 内通过 mystake 调用了 SetValue,导致潜在的无限循环。 WPF 防止这种情况静默禁用回调!
我的 WPF UserControl 是
我的 C# 属性 注意:
我的 WPF 属性
I have the same issue in my code and Luke is right. I called SetValue by mystake inside the PropertyChangedCallback causing a potential infinite loop. WPF prevent this disabling silently the callback !!
my WPF UserControl is
my C# property is Note:
my WPF property
您可能会遇到一个问题:集合的内容正在更改,但实际实例却没有更改。在这种情况下,您需要使用 ObservableCollection 并执行如下操作:
You might be having an issue where the contents of the collection is changing but not the actual instance. in this case you'll want to use an ObservableCollection and do something like this:
如果您只是实例化一个
MyFilterList
并通过如下代码设置VisibleItems
:您可能会看到 PropertyChangedCallback 每次都会发生。意思是,问题出在绑定上,而不是回调上。确保您没有绑定错误,您正在引发
PropertyChanged
,并且您没有破坏绑定(例如,通过在代码中设置VisibleItems
)If you just instantiate a
MyFilterList
and setVisibleItems
via code like this:You will likely see the PropertyChangedCallback happen every time. Meaning, the problem is with the binding, not the callback. Make sure you don't have binding errors, you're raising
PropertyChanged
, and you're not breaking the binding (e.g. by settingVisibleItems
in code)