Документация по Avalonia UI
< Все темы
Печать

Привязка из кода

Привязка из кода в Avalonia работает несколько иначе, чем в WPF/UWP. На низком уровне система привязки Avalonia основана на IObservable от Reactive Extensions, который затем строится на привязках XAML (которые также могут быть реализованы в коде).

Подписка на изменения свойства

Вы можете подписаться на изменения свойства, вызвав метод GetObservable. Это возвращает IObservable<T>, который можно использовать для прослушивания изменений свойства:

var textBlock = new TextBlock();
var text = textBlock.GetObservable(TextBlock.TextProperty);

Каждое свойство, на которое можно подписаться, имеет статическое поле только для чтения с именем [PropertyName]Property, которое передается в GetObservable для подписки на изменения свойства.

IObservable (часть Reactive Extensions или сокращенно rx) выходит за рамки этого руководства, но вот пример, который использует возвращенный observable для вывода сообщения с изменяющимися значениями свойств на консоль:

var textBlock = new TextBlock();
var text = textBlock.GetObservable(TextBlock.TextProperty);
text.Subscribe(value => Console.WriteLine(value + " Changed"));

Когда возвращаемый наблюдаемый объект подписан, он немедленно возвращает текущее значение свойства, а затем выдает новое значение каждый раз, когда свойство изменяется. Если вам не нужно текущее значение, вы можете использовать оператор rx Skip:

var text = textBlock.GetObservable(TextBlock.TextProperty).Skip(1);

Привязка к наблюдаемому

Вы можете привязать свойство к наблюдаемому объекту с помощью метода AvaloniaObject.Bind:

// We use an Rx Subject here so we can push new values using OnNext
var source = new Subject<string>();
var textBlock = new TextBlock();

// Bind TextBlock.Text to source
var subscription = textBlock.Bind(TextBlock.TextProperty, source);

// Set textBlock.Text to "hello"
source.OnNext("hello");
// Set textBlock.Text to "world!"
source.OnNext("world!");

// Terminate the binding
subscription.Dispose();

Обратите внимание, что метод Bind возвращает IDisposable, который можно использовать для завершения привязки. Если вы никогда не вызовете это, то привязка автоматически завершится, когда наблюдаемое завершится через OnCompleted или OnError.

Установка привязки в инициализаторе объекта

Часто бывает полезно настроить привязки в инициализаторах объектов. Вы можете сделать это с помощью индексатора:

var source = new Subject<string>();
var textBlock = new TextBlock
{
    Foreground = Brushes.Red,
    MaxWidth = 200,
    [!TextBlock.TextProperty] = source.ToBinding(),
};

Используя этот метод, вы также можете легко привязать свойство одного элемента управления к свойству другого:

var textBlock1 = new TextBlock();
var textBlock2 = new TextBlock
{
    Foreground = Brushes.Red,
    MaxWidth = 200,
    [!TextBlock.TextProperty] = textBlock1[!TextBlock.TextProperty],
};

Конечно, индексатор можно использовать и вне инициализаторов объектов:

textBlock2[!TextBlock.TextProperty] = textBlock1[!TextBlock.TextProperty];

Единственным недостатком этого синтаксиса является то, что IDisposable не возвращается. Если вам нужно вручную завершить привязку, вы должны использовать метод Bind.

Преобразование значений привязки

Поскольку мы работаем с наблюдаемыми, мы можем легко преобразовать значения, которые мы связываем!

var source = new Subject<string>();
var textBlock = new TextBlock
{
    Foreground = Brushes.Red,
    MaxWidth = 200,
    [!TextBlock.TextProperty] = source.Select(x => "Hello " + x).ToBinding(),
};

Использование привязок XAML из кода

Иногда, когда вам нужны дополнительные функции, предоставляемые привязками XAML, проще использовать привязки XAML из кода. Например, используя только наблюдаемые объекты, вы можете привязаться к свойству в DataContext следующим образом:

var textBlock = new TextBlock();
var viewModelProperty = textBlock.GetObservable(TextBlock.DataContext)
    .OfType<MyViewModel>()
    .Select(x => x?.Name);
textBlock.Bind(TextBlock.TextProperty, viewModelProperty);

Однако в этом случае предпочтительнее использовать привязку XAML:

var textBlock = new TextBlock
{
    [!TextBlock.TextProperty] = new Binding("Name")
};

Или, если вам нужен IDisposable для прекращения привязки:

var textBlock = new TextBlock();
var subscription = textBlock.Bind(TextBlock.TextProperty, new Binding("Name"));

subscription.Dispose();

Подписка на свойство любого объекта

Метод GetObservable возвращает наблюдаемое, которое отслеживает изменения свойства в одном экземпляре. Однако если вы пишете элемент управления, вам может понадобиться реализовать метод OnPropertyChanged, который не привязан к экземпляру объекта.

Для этого вы можете подписаться на AvaloniaProperty.Changed, который является наблюдаемым, который срабатывает каждый раз, когда свойство изменяется в любом экземпляре.

В WPF это делается путем передачи статического PropertyChangedCallback методу регистрации DependencyProperty, но это позволяет только автору элемента управления зарегистрировать обратный вызов измененного свойства.

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

Например, если вы хотите прослушать изменения в свойстве Foo вашего элемента управления, вы должны сделать это следующим образом:

static MyControl()
{
    FooProperty.Changed.AddClassHandler<MyControl>(x => x.FooChanged);
}

private void FooChanged(AvaloniaPropertyChangedEventArgs e)
{
    // The 'e' parameter describes what's changed.
}

Привязка к объектам INotifyPropertyChanged

Также доступна привязка к объектам, реализующим INotifyPropertyChanged.

var textBlock = new TextBlock();

var binding = new Binding 
{ 
    Source = someObjectImplementingINotifyPropertyChanged, 
    Path = nameof(someObjectImplementingINotifyPropertyChanged.MyProperty)
}; 

textBlock.Bind(TextBlock.TextProperty, binding);

 

Оглавление