WPF: остановить привязку, если элемент пользовательского интерфейса не виден - PullRequest
19 голосов
/ 10 февраля 2010

Могу ли я отложить привязку элемента пользовательского интерфейса, если элемент в данный момент не виден. Иногда у меня есть форма, в которой есть скрытые / свернутые элементы, я бы не хотел их обновлять, если их нет на экране. Я подозреваю, что ответ отрицательный, но спросить никогда не больно?

Ответы [ 5 ]

8 голосов
/ 10 февраля 2010

Ответ отрицательный, поскольку привязка может стать причиной повторного отображения элемента. Таким образом, если привязка не работает со скрытыми элементами управления, она не позволит привязке снова сделать ее видимой.

4 голосов
/ 22 мая 2016

Я знаю, что это старый вопрос, но так как мне не удалось найти реализованный класс или что-то еще, я сделал это сам, следуя ответу @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 снова станет ложным, но я думаю, что кто-то может изменить его при необходимости.

3 голосов
/ 10 февраля 2010

Нет встроенного способа сделать это - но вы можете написать это самостоятельно.

Хитрость заключается в том, чтобы обернуть привязку в собственном расширении разметки, который использует исходную привязку, но добавляет новое поведение вокруг него (например, установив UpdateSourceTrigger в значение Explicit, если вы не хотите, чтобы привязка работала.

Вот пример (который задерживает передачу данных привязки):

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

Теперь существует множество возможных краевых условий с отключением привязок для невидимых элементов управления, особенно в том, что касается отображения и скрытия элементов управления, поэтому я бы не стал писать общее расширение для этого - но, возможно, в вашем конкретном приложении это может быть полезно.

2 голосов
/ 15 сентября 2010

Для обходного пути у меня есть привязка к видимости объекта, когда объект установлен на видимость, свойство запускает создание элемента позади него, который имеет привязку через ContentPresenter.

1 голос
/ 11 января 2018

Улучшено MarkupExtension, которое оборачивает обычную привязку для автоматической привязки / отсоединения модели данных, если она видна изменена.
Смотрите предыдущую версию здесь .

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}"/>
...