WinUI3中如何绑定两个不同数据上下文的属性?
解决方案
针对WinUI3中无法使用MultiBinding的情况,有两种靠谱的实现方式:
方案一:用中间ViewModel包装数据(推荐)
这种方式逻辑清晰,能精准控制更新时机,适合value频繁变更的场景。
步骤1:创建包装ViewModel
实现INotifyPropertyChanged的ViewModel,组合Partial和value并自动计算TableProvider:
public class ReferenceTableViewModel : INotifyPropertyChanged { private Partial _partial; private IVisibleDataType _currentValue; private TableProvider _tableProvider; // Partial仅初始化一次,无需变更通知 public Partial Partial { get => _partial; init { _partial = value; UpdateTableProvider(); } } // value频繁变更,需触发变更通知 public IVisibleDataType CurrentValue { get => _currentValue; set { if (_currentValue != value) { _currentValue = value; OnPropertyChanged(nameof(CurrentValue)); UpdateTableProvider(); } } } // 供TableView绑定的最终属性 public TableProvider TableProvider { get => _tableProvider; private set { if (_tableProvider != value) { _tableProvider = value; OnPropertyChanged(nameof(TableProvider)); } } } // 更新TableProvider的核心逻辑 private void UpdateTableProvider() { if (_partial != null && _currentValue != null) { TableProvider = _partial.with(_currentValue); } else { TableProvider = null; } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
步骤2:修改OptionalReferenceBlock
在UserControl中维护ViewModel集合,监听references和value的变化同步更新:
public sealed partial class OptionalReferenceBlock : UserControl { // 供ListView绑定的ViewModel集合 public ObservableCollection<ReferenceTableViewModel> ReferenceViewModels { get; } = new(); public OptionalReferenceBlock() { InitializeComponent(); // 监听value属性变更 RegisterPropertyChangedCallback(ValueProperty, OnValueChanged); } // 原references依赖属性,修改回调逻辑 public IEnumerable<OptionalReference> References { get => (IEnumerable<OptionalReference>)GetValue(ReferencesProperty); set => SetValue(ReferencesProperty, value); } public static DependencyProperty ReferencesProperty = DependencyProperty.Register( nameof(References), typeof(IEnumerable<OptionalReference>), typeof(OptionalReferenceBlock), new PropertyMetadata(null, OnReferencesChanged)); private static void OnReferencesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is OptionalReferenceBlock control) { control.ReferenceViewModels.Clear(); if (e.NewValue is IEnumerable<OptionalReference> references) { foreach (var refItem in references) { control.ReferenceViewModels.Add(new ReferenceTableViewModel { Partial = refItem.partial, CurrentValue = control.Value }); } } } } // 原value依赖属性 public IVisibleDataType Value { get => (IVisibleDataType)GetValue(ValueProperty); set => SetValue(ValueProperty, value); } public static DependencyProperty ValueProperty = DependencyProperty.Register( nameof(Value), typeof(object), typeof(OptionalReferenceBlock), new PropertyMetadata(null)); // value变更时更新所有ViewModel的CurrentValue private void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var newValue = (IVisibleDataType)e.NewValue; foreach (var vm in ReferenceViewModels) { vm.CurrentValue = newValue; } } }
步骤3:修改XAML绑定
将ListView数据源切换为ViewModel集合,直接绑定TableProvider:
<UserControl x:Class="...OptionalReferenceBlock" ...> <ListView ItemsSource="{x:Bind ReferenceViewModels, Mode=OneWay}"> <ListView.ItemTemplate> <DataTemplate x:DataType="local:ReferenceTableViewModel"> <local1:TableView tableProvider="{x:Bind TableProvider, Mode=OneWay}" /> </DataTemplate> </ListView.ItemTemplate> </ListView> </UserControl>
方案二:用x:Bind表达式+转换器(简洁版)
如果不想额外维护ViewModel,可利用x:Bind的表达式能力直接传递参数:
步骤1:实现转换器
创建处理Tuple的转换器,将Partial和value转换为TableProvider:
public class PartialValueToTableProviderConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value is Tuple<Partial, IVisibleDataType> tuple && tuple.Item1 != null && tuple.Item2 != null) { return tuple.Item1.with(tuple.Item2); } return null; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } }
步骤2:修改XAML
声明转换器后,用x:Bind表达式组合当前Item的partial和父控件的Value:
<UserControl x:Class="...OptionalReferenceBlock" ...> <UserControl.Resources> <local:PartialValueToTableProviderConverter x:Key="PartialValueConverter" /> </UserControl.Resources> <ListView ItemsSource="{x:Bind References, Mode=OneWay}"> <ListView.ItemTemplate> <DataTemplate x:DataType="local:OptionalReference"> <local1:TableView tableProvider="{x:Bind Tuple.Create(partial, (local:OptionalReferenceBlock)RelativeSource(Mode=FindAncestor).Value), Converter={StaticResource PartialValueConverter}, Mode=OneWay}" /> </DataTemplate> </ListView.ItemTemplate> </ListView> </UserControl>
该方式通过RelativeSource找到父UserControl,结合当前Item的partial生成Tuple,转换器处理后得到TableProvider。当父控件的Value变化时,x:Bind会自动重新计算表达式更新绑定。
内容的提问来源于stack exchange,提问作者Jules May
相关产品推荐
相关产品推荐

