WPF:如果UI元素不可见,则停止绑定

Ara*_*and 20 data-binding wpf visibility

如果元素当前不可见,我可以延迟ui元素的绑定.有时我的表单有一些隐藏/最小化的元素,如果它们不在屏幕上,我不想更新它们.我怀疑答案是否定的,但问问题从来没有伤害过?

Phi*_*ght 8

答案是否定的,因为绑定可能是导致元素再次可见的原因.因此,如果绑定对隐藏控件不起作用,则不允许绑定再次使其可见.


LoR*_*PMN 5

我知道这是一个老问题,但由于我找不到实现的类或其他东西,我按照@Nir 的回答自己做了。

这是一个标记扩展,它将普通绑定包装为仅在对象IsVisible属性第一次变为 true 时才真正绑定:

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();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用方法:

<ItemsControl ItemsSource="{mx:LazyBinding Documents}"/>
Run Code Online (Sandbox Code Playgroud)

在此示例中,仅当 ItemsControlIsVisible第一次变为 true 时才会绑定。

当再次变为 false 时,它​​不会解除绑定IsVisible,但我认为有人可以根据需要更改它。


Nir*_*Nir 3

没有内置的方法可以做到这一点 - 但您可以自己编写。

诀窍是将绑定包装在您自己的标记扩展中,该标记扩展使用原始绑定,但在其周围添加新行为(例如,当您不希望绑定工作时,将 UpdateSourceTrigger 设置为 Explicit。

这是一个示例(延迟绑定的数据传输):

http://www.paulstovell.com/wpf-delaybinding

现在,有很多可能的边缘条件会禁用不可见控件的绑定,特别是在显示和隐藏控件方面,因此我不会为此编写通用扩展 - 但也许在您的特定应用程序中这可能很有用。