Привязка из кода
Привязка из кода в 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);