How To Bind from Code
Binding from code in Avalonia works somewhat differently to WPF/UWP. At the low level, Avalonia's binding system is based on Reactive Extensions' IObservable
which is then built upon by XAML bindings (which can also be instantiated in code).
Subscribing to Changes to a Property
You can subscribe to changes on a property by calling the GetObservable
method. This returns an IObservable<T>
which can be used to listen for changes to the property:
var textBlock = new TextBlock();
var text = textBlock.GetObservable(TextBlock.TextProperty);
Each property that can be subscribed to has a static readonly field called [PropertyName]Property
which is passed to GetObservable
in order to subscribe to the property's changes.
IObservable
(part of Reactive Extensions, or rx for short) is out of scope for this guide, but here's an example which uses the returned observable to print a message with the changing property values to the console:
var textBlock = new TextBlock();
var text = textBlock.GetObservable(TextBlock.TextProperty);
text.Subscribe(value => Console.WriteLine(value + " Changed"));
When the returned observable is subscribed, it will return the current value of the property immediately and then push a new value each time the property changes. If you don't want the current value, you can use the rx Skip
operator:
var text = textBlock.GetObservable(TextBlock.TextProperty).Skip(1);
Binding to an observable
You can bind a property to an observable using the AvaloniaObject.Bind
method:
// 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();
Notice that the Bind
method returns an IDisposable
which can be used to terminate the binding. If you never call this, then then binding will automatically terminate when the observable finishes via OnCompleted
or OnError
.
Setting a binding in an object initializer
It is often useful to set up bindings in object initializers. You can do this using the indexer:
var source = new Subject<string>();
var textBlock = new TextBlock
{
Foreground = Brushes.Red,
MaxWidth = 200,
[!TextBlock.TextProperty] = source.ToBinding(),
};
Using this method you can also easily bind a property on one control to a property on another:
var textBlock1 = new TextBlock();
var textBlock2 = new TextBlock
{
Foreground = Brushes.Red,
MaxWidth = 200,
[!TextBlock.TextProperty] = textBlock1[!TextBlock.TextProperty],
};
Of course the indexer can be used outside object initializers too:
textBlock2[!TextBlock.TextProperty] = textBlock1[!TextBlock.TextProperty];
The only downside of this syntax is that no IDisposable
is returned. If you need to manually terminate the binding then you should use the Bind
method.
Transforming binding values
Because we're working with observables, we can easily transform the values we're binding!
var source = new Subject<string>();
var textBlock = new TextBlock
{
Foreground = Brushes.Red,
MaxWidth = 200,
[!TextBlock.TextProperty] = source.Select(x => "Hello " + x).ToBinding(),
};