WPF: Stop Binding if a UI element is not visible

后端 未结 5 479
滥情空心
滥情空心 2021-02-05 06:16

Can I delay binding of a ui element if the element is not currently visible. Sometimes I have a form that has some hidden/minimised elements, I would like to not update them if

5条回答
  •  借酒劲吻你
    2021-02-05 06:36

    I know this is an old question, but as I failed to find an implemented class or something, I did it myself, following @Nir answer.

    This is a Markup Extension that wraps normal binding to only really bind when the object IsVisible property becomes true for the first time:

    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();
                }
            }
        }
    }
    

    To use:

    
    

    In this example, it will only bind when the ItemsControl IsVisible becomes true for the first time.

    It will not unbind when the IsVisible gets false again, but I think someone can change it as needed.

提交回复
热议问题