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

时间:2010-02-10 00:52:12

标签: wpf data-binding visibility

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

5 个答案:

答案 0 :(得分:8)

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

答案 1 :(得分:3)

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

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

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

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

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

答案 2 :(得分:3)

我知道这是一个老问题,但由于我没有找到一个已实现的类或其他东西,我自己做了,跟随@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();
            }
        }
    }
}

使用:

<ItemsControl ItemsSource="{mx:LazyBinding Documents}"/>

在这个例子中,它只会在ItemsControl IsVisible第一次变为true时绑定。

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

答案 3 :(得分:2)

对于解决方法,我绑定了对象的可见性,当对象设置为visible时,该属性会触发构造后面的元素,该元素通过ContentPresenter进行绑定。

答案 4 :(得分:1)

改进了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}"/>