Xamarin中对象属性无法绑定到AppThemeBinding的问题
Xamarin AppThemeBinding 绑定自定义控件属性失效的解决办法
问题核心是AppThemeBinding本身不支持嵌套{Binding}表达式——它的Dark/Light参数仅接受静态资源键或直接的ImageSource值,没法直接绑定到数据源的属性上,这就是你两种写法都失效的原因。而单独绑定DarkImage/LightImage是直接绑定数据源属性,所以能正常工作。
这里给你两种可行的解决思路:
方案一:ViewModel层面处理主题切换(推荐)
在ViewModel里维护一个对应当前主题的图片属性,监听系统主题变化自动切换图片,然后直接绑定这个属性到自定义控件。
1. 改造ViewModel
实现INotifyPropertyChanged,添加CurrentImage属性,并订阅主题变化事件:
using Xamarin.Essentials; using System.ComponentModel; using System.Runtime.CompilerServices; public class YourItemViewModel : INotifyPropertyChanged { // 原有属性 public ImageSource DarkImage { get; set; } public ImageSource LightImage { get; set; } // 对应当前主题的图片属性 private ImageSource _currentImage; public ImageSource CurrentImage { get => _currentImage; set { _currentImage = value; OnPropertyChanged(); } } public YourItemViewModel() { // 初始化时设置当前主题对应的图片 UpdateCurrentImage(AppInfo.RequestedTheme); // 订阅主题变化事件 AppInfo.AppThemeChanged += OnAppThemeChanged; } private void OnAppThemeChanged(object sender, AppThemeChangedEventArgs e) { UpdateCurrentImage(e.RequestedTheme); } private void UpdateCurrentImage(AppTheme theme) { CurrentImage = theme == AppTheme.Dark ? DarkImage : LightImage; } // INotifyPropertyChanged实现 public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
2. XAML绑定修改
直接绑定自定义控件的Source到CurrentImage:
<CollectionView.ItemTemplate> <DataTemplate> <local:YourCustomControl Source="{Binding CurrentImage}" /> </DataTemplate> </CollectionView.ItemTemplate>
方案二:自定义附加属性(进阶)
如果不想修改ViewModel,可以自定义一个附加属性,在控件层面处理主题绑定逻辑。不过这种方式代码量更大,适合需要复用多个控件的场景:
1. 创建附加属性类
using Xamarin.Forms; public class ThemeImageBinding : BindableObject { public static readonly BindableProperty DarkImageProperty = BindableProperty.CreateAttached("DarkImage", typeof(ImageSource), typeof(ThemeImageBinding), null, propertyChanged: OnImageChanged); public static readonly BindableProperty LightImageProperty = BindableProperty.CreateAttached("LightImage", typeof(ImageSource), typeof(ThemeImageBinding), null, propertyChanged: OnImageChanged); public static ImageSource GetDarkImage(BindableObject view) { return (ImageSource)view.GetValue(DarkImageProperty); } public static void SetDarkImage(BindableObject view, ImageSource value) { view.SetValue(DarkImageProperty, value); } public static ImageSource GetLightImage(BindableObject view) { return (ImageSource)view.GetValue(LightImageProperty); } public static void SetLightImage(BindableObject view, ImageSource value) { view.SetValue(LightImageProperty, value); } private static void OnImageChanged(BindableObject bindable, object oldValue, object newValue) { if (bindable is YourCustomControl customControl) { UpdateCustomControlSource(customControl); } } private static void UpdateCustomControlSource(YourCustomControl control) { var theme = Application.Current.RequestedTheme; var darkImage = GetDarkImage(control); var lightImage = GetLightImage(control); control.Source = theme == OSAppTheme.Dark ? darkImage : lightImage; } // 监听主题变化 static ThemeImageBinding() { Application.Current.RequestedThemeChanged += (s, e) => { foreach (var page in Application.Current.MainPage.Navigation.NavigationStack) { UpdateAllControls(page); } }; } private static void UpdateAllControls(Element element) { if (element is YourCustomControl customControl) { UpdateCustomControlSource(customControl); } if (element is Layout layout) { foreach (var child in layout.Children) { UpdateAllControls(child); } } } }
2. XAML中使用附加属性
<CollectionView.ItemTemplate> <DataTemplate> <local:YourCustomControl local:ThemeImageBinding.DarkImage="{Binding DarkImage}" local:ThemeImageBinding.LightImage="{Binding LightImage}" /> </DataTemplate> </CollectionView.ItemTemplate>
两种方案里,方案一更简单易维护,适合大多数场景;方案二适合需要在多个控件上复用主题绑定逻辑的情况。
内容的提问来源于stack exchange,提问作者Stefan Vogel
相关产品推荐
相关产品推荐

