Avalonia 学习之 绑定
绑定是一个非常强大的概念,它允许绑定两个属性,这样当其中一个属性发生变化时,另一个也会发生变化。
通常,绑定从source属性到target属性——正常OneWay绑定,但也有一个TwoWay绑定可以确保两个属性同步,无论哪个发生变化。还有另外两种绑定模式:OneWayToSource和OneTime使用频率较低的绑定。
也很少讨论,但同样重要的集合绑定,其中一个集合模仿另一个集合,或者两个集合互相模仿。
请注意,绑定的目标不必与绑定的源完全相同,可以在源和目标之间进行转换,反之亦然。
Avalonia绑定概念
-
绑定源对象——通过该对象可以获得绑定源属性的路径。
-
绑定目标对象——其AvaloniaProperty(附加、样式或直接)属性用作绑定目标的对象。目标对象只能是派生自AvaloniaObject(这意味着它可以是任何Avalonia视觉对象)的类。AvaloniaObject类似于WPF的DependencyObject。
-
绑定路径——从源对象到源属性的路径。Path由路径链接组成,每个链接都可以是常规(C#)属性或Avalonia属性。在XAML绑定中,Avalonia属性应放在括号中。下面是XAML中绑定路径的示例:MyProp1.(local:AttachedProperties.AttachedProperty1).MyProp2。此路径意味着在源对象上查找常规C# MyProp1属性,然后在第一个链接返回的对象中查找附加属性AttachedProperty1(在本地命名空间的AttachedProperties类中定义),然后在该附加属性值中查找常规C# MyProp2属性。
-
Target属性——只能是Attached、Style或Direct Property类型之一。
-
BindingMode可以:
- OneWay——从源头到目标
- TwoWay——当源或目标发生变化时,另一个也将得到更新。
- OneWayToSource——当目标更新时,源也会更新,但反之则不然。
- OneTime——仅在初始化期间从源同步目标一次。
- Default——依赖于目标属性的首选绑定模式。初始化Attached Style或Direct Property时,可以指定首选绑定模式,在这种情况下将使用该模式(在绑定本身内未指定BindingMode时)。
-
转换器——仅当源值和目标值不同时才需要。它用于将值从源转换为目标,反之亦然。对于通常的绑定,转换器应该实现IValueConverter接口。
不同的绑定源
字符串格式化
<TextBlock Text="{Binding FloatValue, StringFormat={}{0:0.0}}" />
<TextBlock Text="{Binding FloatValue, StringFormat=\{0:0.0\}}" />
<TextBlock Text="{Binding FloatValue, StringFormat='{}{0:0.0}'}" />
<TextBlock Text="{Binding MyText,StringFormat='后台值{0}'}" />
绑定转换器
转换器要实现IValueConverter接口,通过Convert(...)和ConvertBack(...) 方法定义了绑定源到绑定目标和绑定目标到绑定源的转换
我在这里定义一个字符的转换器
using Avalonia.Data;
using Avalonia.Data.Converters;
using System;
using System.Globalization;
namespace BindDemo.Converters
{
public class TextCaseConverter : IValueConverter
{
public object? Convert (object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is string sourceText && parameter is string targetCase
&& targetType.IsAssignableTo(typeof(string)))
{
switch (targetCase)
{
case "upper":
case "SQL":
return sourceText.ToUpper();
case "lower":
return sourceText.ToLower();
case "title": // Every First Letter Uppercase
var txtinfo = new System.Globalization.CultureInfo("en-US", false).TextInfo;
return txtinfo.ToTitleCase(sourceText);
default:
// invalid option, return the exception below
break;
}
}
// converter used for the wrong type
return new BindingNotification(new InvalidCastException(), BindingErrorType.Error);
}
public object? ConvertBack (object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
使用的时候首选要引用对应的命名空间
xmlns:c="clr-namespace:BindDemo.Converters"
引入资源,定义一个Key
<Window.Resources>
<c:TextCaseConverter x:Key="textCaseConverter" />
</Window.Resources>
在Bind后面加上转换器
<TextBlock Text="{Binding MyText,
Converter={StaticResource textCaseConverter},ConverterParameter=lower}" />
这样就相当于TextBlock的Text绑定到了ViewModel中的MyText属性,每次MyText(绑定源) 变化时,都会通知到TextBlock,显示的时候呢,会经过textCaseConverter的 Convert 方法进行转换。
那如果绑定到TextBox时,这边也输入会怎么样呢? 它会进入TextCaseConverter的ConvertBack方法里。 由于里面直接抛出异常,所以我们可以指定绑定方式为OneWay,完整写法如下
<TextBox Text="{Binding MyText,Mode=OneWay,
Converter={StaticResource textCaseConverter}, ConverterParameter=lower}" />
图片路径转换
这个其实挺常用的,绑定图片Source的时候,直接给个字符串的地址是不行的,因为他要求是个Bitmap的类型,那么我们就需要实现一个转换器
using System;
using System.Globalization;
using Avalonia;
using Avalonia.Data.Converters;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
namespace UserControlDemo.Convers
{
public class StringToImageSourceConverter : IValueConverter
{
#region Converter
private IAssetLoader _assetLoader;
public StringToImageSourceConverter()
{
_assetLoader = AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
string path = (string)value;
return new Bitmap(_assetLoader.Open(new Uri("avares://UserControlDemo/Assets/" + path)));
}
catch (Exception e)
{
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
#endregion
}
}
视图上绑定这样写
<Style Selector="StackPanel > Image">
<Setter Property="Source" Value="{Binding #Uc.MenuImg, Converter={StaticResource ToImage}}" />
</Style>
我为什么要把这个单独拉出来说呢。。。因为我在这里踩过一个坑
转换器中Bitmap引入类型一定要是 Avalonia.Media.Imaging
,希望你不像我一样马虎  ̄□ ̄||