【问题标题】:WPF: Stop Binding if a UI element is not visibleWPF:如果 UI 元素不可见,则停止绑定
【发布时间】:2011-01-15 01:49:15
【问题描述】:

如果元素当前不可见,我可以延迟绑定 ui 元素吗?有时我有一个包含一些隐藏/最小化元素的表单,如果它们不在屏幕上,我不想更新它们。我怀疑答案是否定的,但问也无妨?

【问题讨论】:

  • 你为什么要这样做?为了表现?
  • 是的性能,它实际上是懒惰(懒惰是发明之母),因为如果它们不可见,我应该从树中删除它们,以获得我需要的性能。

标签: wpf data-binding visibility


【解决方案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

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

      【讨论】:

      【解决方案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}"/>
        

        【讨论】:

          猜你喜欢
          • 2012-11-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-04-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多