You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 07:39:50