Xamarin.Forms中为何一个控件绑定生效ContentView绑定却失效?
我正在开发一款Xamarin应用,目前在Android设备上测试。我将ViewModel中的枚举属性绑定到两个控件:一个Label显示文本值,另一个是ContentView通过IValueConverter绑定背景色。
相关XAML代码:
<ContentView BackgroundColor="{Binding MyField, Converter={StaticResource MyFieldEnumValueToColorConverter}}" > <ContentView.GestureRecognizers> <TapGestureRecognizer Command="{Binding MyFieldClickCommand}" /> </ContentView.GestureRecognizers> <Label Text="{Binding MyField}" /> </ContentView>
IValueConverter实现:
public class MyFieldEnumValueToColorConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is MyFieldEnum) { switch ((MyFieldEnum)value) { case MyFieldEnum.Value1: return Color.Orange; case MyFieldEnum.Value2: return Color.Green; case MyFieldEnum.Value3: return Color.Red; } } return Color.White; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
ViewModel中的属性(已实现INotifyPropertyChanged):
public event PropertyChangedEventHandler PropertyChanged; private MyFieldEnum _myField; public MyFieldEnum MyField { get => _myField; set { _myField= value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyField))); } }
页面加载时,控件显示正常:文本和背景色都匹配枚举值。但在点击处理程序中执行MyField = MyFieldEnum.Value2;后,Label的文本会更新,可ContentView的背景色却不变化。我尝试过新增一个Color类型的属性,在getter里直接实现转换器逻辑,再绑定到ContentView的BackgroundColor,问题依旧。页面其他数据绑定都正常,只有这一项出问题。
更新:问题出在ContentView上——把同样的BackgroundColor绑定应用到Label本身时,能正常更新,但ContentView不行。
这是Xamarin.Forms在Android平台上的已知绑定问题:ContentView的BackgroundColor属性绑定在属性变更时,不会自动触发视图刷新。可以通过以下几种方式解决:
用
Frame替代ContentView
Frame控件的BackgroundColor绑定在Android上能正常响应属性变更,且可以实现类似ContentView的容器功能,修改后的XAML如下:<Frame BackgroundColor="{Binding MyField, Converter={StaticResource MyFieldEnumValueToColorConverter}}" Padding="0" HasShadow="False" > <Frame.GestureRecognizers> <TapGestureRecognizer Command="{Binding MyFieldClickCommand}" /> </Frame.GestureRecognizers> <Label Text="{Binding MyField}" /> </Frame>强制刷新ContentView的绑定
在ViewModel的MyField属性变更后,手动触发ContentView的绑定刷新。可以给ContentView设置x:Name,然后在代码后台监听ViewModel的PropertyChanged事件,调用ForceUpdateBindings():
XAML中添加名称:<ContentView x:Name="MyContentView" BackgroundColor="{Binding MyField, Converter={StaticResource MyFieldEnumValueToColorConverter}}" > <!-- 其他代码不变 --> </ContentView>后台代码:
protected override void OnAppearing() { base.OnAppearing(); if (BindingContext is YourViewModel vm) { vm.PropertyChanged += (s, e) => { if (e.PropertyName == nameof(vm.MyField)) { MyContentView.ForceUpdateBindings(); } }; } }自定义ContentView,暴露可绑定的BackgroundColor属性
创建自定义ContentView类,重新定义一个可绑定的BackgroundColor属性,确保属性变更能触发视图刷新:public class BindableContentView : ContentView { public static new readonly BindableProperty BackgroundColorProperty = BindableProperty.Create(nameof(BackgroundColor), typeof(Color), typeof(BindableContentView), Color.White, propertyChanged: (bindable, oldValue, newValue) => { ((BindableContentView)bindable).BackgroundColor = (Color)newValue; }); public new Color BackgroundColor { get => (Color)GetValue(BackgroundColorProperty); set => SetValue(BackgroundColorProperty, value); } }然后在XAML中使用这个自定义控件:
<local:BindableContentView BackgroundColor="{Binding MyField, Converter={StaticResource MyFieldEnumValueToColorConverter}}" > <!-- 其他代码不变 --> </local:BindableContentView>
内容的提问来源于stack exchange,提问作者Mihaly Balas

