WPF:如果 UI 元素不可见,则停止绑定
Posted
技术标签:
【中文标题】WPF:如果 UI 元素不可见,则停止绑定【英文标题】:WPF: Stop Binding if a UI element is not visible 【发布时间】:2011-01-15 01:49:15 【问题描述】:如果元素当前不可见,我可以延迟绑定 ui 元素吗?有时我有一个包含一些隐藏/最小化元素的表单,如果它们不在屏幕上,我不想更新它们。我怀疑答案是否定的,但问也无妨?
【问题讨论】:
你为什么要这样做?为了表现? 是的性能,它实际上是懒惰(懒惰是发明之母),因为如果它们不可见,我应该从树中删除它们,以获得我需要的性能。 【参考方案1】:答案是否定的,因为绑定可能导致元素再次可见。因此,如果绑定在隐藏控件上不起作用,它将不允许绑定再次使其可见。
【讨论】:
【参考方案2】:我知道这是一个老问题,但由于我未能找到已实现的类或其他东西,我自己按照@Nir 的回答做了。
这是一个标记扩展,它将普通绑定包装为仅在对象IsVisible
属性第一次变为真时才真正绑定:
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace MakupExtensions
[MarkupExtensionReturnType(typeof(object))]
public class LazyBindingExtension : MarkupExtension
public LazyBindingExtension()
public LazyBindingExtension(PropertyPath path) : this()
Path = path;
public IValueConverter Converter
get;
set;
[TypeConverter(typeof(CultureInfoIetfLanguageTagConverter))]
public CultureInfo ConverterCulture
get;
set;
public object ConverterParamter
get;
set;
public string ElementName
get;
set;
[ConstructorArgument("path")]
public PropertyPath Path
get;
set;
public RelativeSource RelativeSource
get;
set;
public object Source
get;
set;
public UpdateSourceTrigger UpdateSourceTrigger
get;
set;
public bool ValidatesOnDataErrors
get;
set;
public bool ValidatesOnExceptions
get;
set;
public bool ValidatesOnNotifyDataErrors
get;
set;
private Binding binding;
private DependencyObject bindingTarget;
private DependencyProperty bindingTargetProperty;
public override object ProvideValue(IServiceProvider serviceProvider)
var valueProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
if (valueProvider != null)
bindingTarget = valueProvider.TargetObject as DependencyObject;
bindingTargetProperty = valueProvider.TargetProperty as DependencyProperty;
if (bindingTargetProperty == null || bindingTarget == null)
throw new NotSupportedException($"The property 'valueProvider.TargetProperty' on target 'valueProvider.TargetObject' is not valid for a LazyBinding. The LazyBinding target must be a DependencyObject, and the target property must be a DependencyProperty.");
binding = new Binding
Path = Path,
Converter = Converter,
ConverterCulture = ConverterCulture,
ConverterParameter = ConverterParamter
;
if (ElementName != null)
binding.ElementName = ElementName;
if (RelativeSource != null)
binding.RelativeSource = RelativeSource;
if (Source != null)
binding.Source = Source;
binding.UpdateSourceTrigger = UpdateSourceTrigger;
binding.ValidatesOnDataErrors = ValidatesOnDataErrors;
binding.ValidatesOnExceptions = ValidatesOnExceptions;
binding.ValidatesOnNotifyDataErrors = ValidatesOnNotifyDataErrors;
return SetBinding();
return null;
public object SetBinding()
var uiElement = bindingTarget as UIElement;
if (uiElement != null && !uiElement.IsVisible)
uiElement.IsVisibleChanged += UiElement_IsVisibleChanged;
else
ConsolidateBinding();
return bindingTarget.GetValue(bindingTargetProperty);
private void ConsolidateBinding() => BindingOperations.SetBinding(bindingTarget, bindingTargetProperty, binding);
private void UiElement_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
var uiElement = sender as UIElement;
if (uiElement != null && uiElement.IsVisible)
uiElement.IsVisibleChanged -= UiElement_IsVisibleChanged;
ConsolidateBinding();
使用方法:
<ItemsControl ItemsSource="mx:LazyBinding Documents"/>
在这个例子中,它只会在 ItemsControl IsVisible
第一次变为真时绑定。
IsVisible
再次变为 false 时不会解除绑定,但我认为有人可以根据需要更改它。
【讨论】:
【参考方案3】:没有内置的方法可以做到这一点 - 但您可以自己编写。
诀窍是将绑定包装在您自己的标记扩展中,该扩展使用原始绑定但在其周围添加新行为(例如,当您不希望绑定起作用时,通过将 UpdateSourceTrigger 设置为 Explicit。
这是一个示例(延迟绑定的数据传输):
http://www.paulstovell.com/wpf-delaybinding
现在,有很多可能的边缘条件禁用不可见控件的绑定,尤其是在显示和隐藏控件方面,所以我不会为此编写通用扩展 - 但也许在您的特定应用程序中这可能很有用。
【讨论】:
这很奇怪,我写了一个类似的东西 - codeproject.com/KB/WPF/DelayedBindingTextBox.aspx【参考方案4】:对于一种解决方法,我绑定了对象的可见性,当对象设置为可见时,该属性会触发其后面的元素的构造,该元素通过ContentPresenter
进行绑定。
【讨论】:
【参考方案5】:改进的 MarkupExtension 将普通绑定包装到自动绑定/取消绑定数据模型(如果可见已更改)。 参见之前的版本here。
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace UtilsWPF
[MarkupExtensionReturnType(typeof(object))]
public class LazyBindingExtension : MarkupExtension
public LazyBindingExtension()
public LazyBindingExtension(PropertyPath path) : this()
Path = path;
#region Properties
public IValueConverter Converter get; set;
[TypeConverter(typeof(CultureInfoIetfLanguageTagConverter))]
public CultureInfo ConverterCulture get; set;
public object ConverterParamter get; set;
public string ElementName get; set;
[ConstructorArgument("path")]
public PropertyPath Path get; set;
public RelativeSource RelativeSource get; set;
public object Source get; set;
public UpdateSourceTrigger UpdateSourceTrigger get; set;
public bool ValidatesOnDataErrors get; set;
public bool ValidatesOnExceptions get; set;
public bool ValidatesOnNotifyDataErrors get; set;
private Binding binding;
private UIElement bindingTarget;
private DependencyProperty bindingTargetProperty;
#endregion
#region Init
public override object ProvideValue(IServiceProvider serviceProvider)
var valueProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
if (valueProvider != null)
bindingTarget = valueProvider.TargetObject as UIElement;
if (bindingTarget == null)
throw new NotSupportedException($"Target 'valueProvider.TargetObject' is not valid for a LazyBinding. The LazyBinding target must be a UIElement.");
bindingTargetProperty = valueProvider.TargetProperty as DependencyProperty;
if (bindingTargetProperty == null)
throw new NotSupportedException($"The property 'valueProvider.TargetProperty' is not valid for a LazyBinding. The LazyBinding target property must be a DependencyProperty.");
binding = new Binding
Path = Path,
Converter = Converter,
ConverterCulture = ConverterCulture,
ConverterParameter = ConverterParamter
;
if (ElementName != null)
binding.ElementName = ElementName;
if (RelativeSource != null)
binding.RelativeSource = RelativeSource;
if (Source != null)
binding.Source = Source;
binding.UpdateSourceTrigger = UpdateSourceTrigger;
binding.ValidatesOnDataErrors = ValidatesOnDataErrors;
binding.ValidatesOnExceptions = ValidatesOnExceptions;
binding.ValidatesOnNotifyDataErrors = ValidatesOnNotifyDataErrors;
return SetBinding();
return null;
public object SetBinding()
bindingTarget.IsVisibleChanged += UiElement_IsVisibleChanged;
updateBinding();
return bindingTarget.GetValue(bindingTargetProperty);
#endregion
#region Event Handlers
private void UiElement_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
updateBinding();
#endregion
#region Update Binding
private void updateBinding()
if (bindingTarget.IsVisible)
ConsolidateBinding();
else
ClearBinding();
private bool _isBind;
private void ConsolidateBinding()
if (_isBind)
return;
_isBind = true;
BindingOperations.SetBinding(bindingTarget, bindingTargetProperty, binding);
private void ClearBinding()
if (!_isBind)
return;
BindingOperations.ClearBinding(bindingTarget, bindingTargetProperty);
_isBind = false;
#endregion
使用方法:
<ItemsControl ItemsSource="utils:LazyBinding Documents"/>
【讨论】:
以上是关于WPF:如果 UI 元素不可见,则停止绑定的主要内容,如果未能解决你的问题,请参考以下文章