Задержка привязки из источника - PullRequest
7 голосов
/ 30 декабря 2011

Рассмотрим следующее свойство ViewModel:

private string _slowProperty;
public string SlowProperty
{
    get { return _slowProperty; }
    set
    {
        _slowProperty = value;
        RaisePropertyChanged("SlowProperty");
    }
}

Который привязан к текстовому полю, например:

<TextBox Text="{Binding SlowProperty}" />

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

Вместо этого я хотел бы получить что-то вроде:

<TextBlock Text="{z:DelayedSourceBinding SlowProperty}" />

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

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

<TextBox Text="{z:DelayBinding Path=SearchText}" />

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

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

Для полноты картины, есть еще 2 проекта, которые выполняют подобные задачи, но ни один из них не решает проблему, с которой я сталкиваюсь:

DeferredBinding - решение, подобное DelayBinding.Однако использовать его немного сложнее.

DelayedBindingTextBox - Реализует отложенное связывание с использованием пользовательского элемента управления textbox.

Спасибо!

Ответы [ 5 ]

3 голосов
/ 30 декабря 2011

Почему бы не решить эту проблему в представлении модели?Если ваше свойство быстро меняется, но получить его медленно, у вас может появиться второе «отложенное» свойство, отображаемое вашей моделью представления.Вы можете использовать таймер для периодического обновления этого свойства «delayed».

Или более чистая реализация может использовать функцию Throttle , предоставляемую средой реактивных расширений.

2 голосов
/ 12 июля 2012

Некоторое время назад у меня было похожее требование, когда мне нужно было иметь возможность задерживать и источник, и цель, поэтому я создал два расширения разметки под названием DelayBinding и DelayMultiBinding. Чтобы отложить обновление до источника, вы указываете UpdateSourceDelay

<TextBox Text="{db:DelayBinding SlowProperty,
                                UpdateSourceDelay='00:00:01'}" /> 

Исходный код и пример использования для DelayBinding и DelayMultiBinding можно загрузить здесь .
Если вы заинтересованы в деталях реализации, вы можете проверить мой пост в блоге об этом здесь:
DelayBinding и DelayMultiBinding с задержкой источника и цели

1 голос
/ 16 сентября 2016

Следует отметить, что в .Net 4.5 в инфраструктуру добавлено свойство задержки , которое позволяет вам устанавливать величину задержки привязки в миллисекундах.В примере Microsoft выделен режим twoway, но задержка привязки работает в любом режиме привязки.

Например, я использовал это в сетке данных, где выбранный элемент / значение нужно было изменить как изтекстовое поле в пользовательском контроле пользователя и, очевидно, из сетки данных.По причинам, которые я здесь не буду упоминать, текстовое поле должно было быть привязано к свойству, отличному от модели представления, но оба свойства должны были иметь одно и то же значение в конце дня, и любое изменение в одном из них должно было бытьотражается на другом.Когда выбранное значение изменилось в сетке данных, текстовое поле также пришлось обновить, и я проверил фактические изменения значений в установщике связанного свойства SelectedValue, чтобы предотвратить бесконечный цикл.Когда изменение было слишком быстрым, произошла ошибка при сохранении данных обратно в источник, когда текст изменился внутри текстового поля, которое было изменено установщиком SelectedValue.Задержка в два кадра устранила проблему без каких-либо сложных решений и без слишком медленного пользовательского интерфейса:

SelectedValue="{Binding SampleNumberSelect, Mode=OneWayToSource, Delay=33}"

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

1 голос
/ 30 декабря 2011

Мне кажется, что вы действительно хотите отложить точку при вызове RaisePropertyChanged ().
Итак, я попробовал это, и вот решение:

XAML:

<StackPanel>
    <TextBox Text="{Binding DelayedText, UpdateSourceTrigger=PropertyChanged}" />
    <TextBlock Text="{Binding DelayedText}" />
</StackPanel>

C #:

public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
        }

        private String m_DelayedText;
        public String DelayedText 
        {
            get
            {
                return m_DelayedText;
            }
            set
            {
                if (m_DelayedText != value)
                {
                    String delayedText;
                    m_DelayedText = delayedText = value;
                    Task.Factory.StartNew(() =>
                        {
                            Thread.Sleep(2000);
                            if (delayedText == m_DelayedText)
                            {
                                RaisePropertyChanged("DelayedText");
                            }
                        });
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void RaisePropertyChanged(String _Prop)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(_Prop));
            }
        }
    }

Вы можете проверить, работает ли он, установив точку останова на RaisePropertyChanged("DelayedText")

Я понимаю, что это может выглядеть довольно много кода для "просто "собственность"
Но вы можете использовать фрагменты кода или внедрить шаблонный код во время выполнения, используя Resharper и т. П.
И в любом случае, вам, вероятно, он не понадобится так часто.

Кроме того, если бы вы быличтобы использовать другой подход (например, путем настройки TextBox), вам придется обрабатывать все места, где вы привязываете свойство.
Делая это таким образом, в установщике свойства вы гарантируете, что каждый, кто обращается к этому свойству,ограничено полученными обновлениями.

HTH,

Bab.

0 голосов
/ 22 августа 2018

Существует свойство Delay как часть привязки, но оно работает только от цели к источнику.В любом случае, в этом рабочем примере я покажу вам , как вы можете использовать свойство Delay в обратном направлении :

.Tag (см. Foo_DataContextChanged).Привязка тега задает задержку и привязывается к свойству DelayedViewProperty в представлении.Фактическое содержание контрольного содержимого привязано к DelayedViewProperty.

<Window x:Class="DelayedControl.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<StackPanel>
    <Button Click="Button_Click">Set time</Button>
    <TextBox Text="{Binding ViewModelProperty}" IsReadOnly="True" ToolTip="Not delayed"></TextBox>

    <!-- The DelayedViewProperty is on the code behind (Window in this case), set DataContext as needed -->
    <TextBox x:Name="foo" DataContextChanged="Foo_DataContextChanged" IsReadOnly="True" ToolTip="Delayed"
             Tag="{Binding DelayedViewProperty, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window},
                    Delay=500, Mode=OneWayToSource}"
             Text="{Binding DelayedViewProperty, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, 
                    Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>

using System;
using System.ComponentModel;
using System.Windows;

namespace DelayedControl
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this; // treat this as a view model for simplification..
        }

        public string DelayedViewProperty
        {
            get => delayedViewProperty;
            set
            {
                delayedViewProperty = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DelayedViewProperty)));
            }
        }
        private string delayedViewProperty;


        private void Foo_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue is INotifyPropertyChanged vm)
            {
                // Forward updates of the view model property to the foo.Tag
                vm.PropertyChanged += (s, args) =>
                {
                    var propertyName = nameof(ViewModelProperty);
                    if (args.PropertyName == propertyName && s?.GetType().GetProperty(propertyName)?.GetValue(s) is var tag)
                    {
                        foo.Tag = tag;  // Dispatcher might be needed if the change is triggered from a different thread..
                    }
                };
            }
        }

        public string ViewModelProperty
        {
            get => viewModelProperty;
            set
            {
                viewModelProperty = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ViewModelProperty)));
            }
        }
        private string viewModelProperty;

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            ViewModelProperty = DateTime.Now.ToString("hh:mm:ss.fff");
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...