如何在 WPF 中动态设置绑定路径?

How can you dynamically set binding path in WPF?

我有一个 ContentControl,其中 ItemSource 设置为 Panel 的集合。我想制作一个 DataTemplate,其中 DataContext 设置为 PanelPanel有一个public属性Receiver,是接收数据的对象。 class 实现了 INotifyPropertyChanged 接口,当它的 public 属性 Parameters 完成更新时, PropertyChanged 事件被触发并且参数是更新。面板的内容应该是ParameterType(包含Param1Param2的枚举)给出的相应属性,它本身就是一个属性 Panel 对象。

Panel.cs

public class Panel : BindableBase
{
    private Receiver _receiver;
    private PanelType _parameterType;

    public Panel(Receiver receiver, PanelType parameterType)
    {
        _receiver = receiver;
        _parameterType = parameterType;

    }

    public Receiver CurrentReceiver
    {
        get { return _receiver; }
    }

    public PanelType ParameterType
    {
        get { return _parameterType; }
    }

}

Receiver.cs

public class Receiver : BindableBase
{
    ...

    public void Read()
    {
        // Receive some data
        Parameters.Param1 = 1;
        Parameters.Param2 = 5;

        // NotifyPropertyChanged (via Prism: RaisePropertyChanged())
        RaisePropertyChanged("Parameters");
    }

    public Params Parameters {get; set;}

    ...

}

Params.cs

public class Params : BindableBase
{
    public Params(int param1, int param2)
    {
        Param1 = param1;
        Param2 = param2;
    }

    public int Param1 {get; set;}
    public int Param2 {get; set;}

}

当 Receiver 通知它的 属性 Parameters 发生变化时,我希望我的界面在这个 属性 发生变化时更新。这对我有用,每当我像这样绑定 DataTemplate 时:

<TextBlock Text="{Binding CurrentReceiver.Parameters.Param1}" />

每当读取新数据时都会更新控件。

但是,我在 DataTemplate 中使用此控件,其中 PanelDataContext,我想绑定到如下内容:

<TextBlock Text="{Binding CurrentReceiver.Parameters.{Binding ParameterType}}" />

但显然,那是行不通的。我尝试将多重绑定与使用 returns 和 PropertyPath 的多值转换器结合使用,但没有成功。通过绑定 ParameterType 来动态构建 Path 是行不通的,因为 Path 不是 DependencyProperty。 (我读到这与编译时与运行时有关,这就是我尝试多值转换器方法的原因)

我通过在代码隐藏中设置 Binding 使它更早地工作(当不使用 DataTemplate 时):

...
valueTextBlock.SetBinding(
    TextBlock.TextProperty,
    new Binding("CurrentReceiver.Parameters." + Title)
    {
        Mode = BindingMode.OneWay,
        StringFormat = "0.00"
    });
...

但是(我认为)这种方法在使用 DataTemplate 时不可用(而且不可取)。谁能帮我指出正确的方向?

亲切的问候,

汤姆

BindableBase class 必须有一个方法来为 属性 赋值:Set (ref ...) 或 SetProperty (ref ..)。 如果我对你的任务理解正确,那么看看这样的实现。

Class 参数:

public class Params : BindableBase
{
    public Params() { }

    public Params(int param1, int param2)
    {
        Param1 = param1;
        Param2 = param2;
    }

    private int _param1;
    private int _param2;
    public int Param1 { get => _param1; set => Set(ref _param1, value); }
    public int Param2 { get => _param2; set => Set(ref _param2, value); }
}

Class 接收方:

public class Receiver : BindableBase
{

    public void Read()
    {
        // Receive some data
        Parameters.Param1 = 1;
        Parameters.Param2 = 5;

        // NotifyPropertyChanged (via Prism: RaisePropertyChanged())
        // RaisePropertyChanged("Parameters");
    }

    private Params _parameters;
    public Params Parameters { get => _parameters; set => Set(ref _parameters, value); }

}

Class面板:

public enum PanelType { Param1, Param2 }
public class Panel : BindableBase
{

    public Panel(Receiver currentReceiver, PanelType parameterType)
    {
        CurrentReceiver = currentReceiver;
        ParameterType = parameterType;

        CurrentReceiver.PropertyChanged += CurrentReceiver_PropertyChanged;
        CurrentReceiver.RaisePropertyChanged(null);
    }

    private void CurrentReceiver_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (Parameters != CurrentReceiver.Parameters)
        {
            if (Parameters != null)
                Parameters.PropertyChanged -= Parameters_PropertyChanged;

            Parameters = CurrentReceiver.Parameters;

            if (Parameters != null)
            {
                Parameters.PropertyChanged += Parameters_PropertyChanged;
                Parameters.RaisePropertyChanged(null);
            }
            else
                CurentValue = null;
        }
    }

    private void Parameters_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        switch (ParameterType)
        {
            case PanelType.Param1:
                CurrentValue = Parameters.Param1;
                break;
            case PanelType.Param2:
                CurrentValue = Parameters.Param2;
                break;
            default:
                throw new NotImplementedException();
        }
    }

    public Receiver CurrentReceiver { get; }

    public PanelType ParameterType { get; }

    private Params Parameters;

    // If the type is always the same and known in advance, 
    // then «object» must be replaced with a known type.
    private object _currentValue;
    public object CurrentValue { get => _currentValue; private set => Set(ref _currentValue, value); }
}

XAML:

<TextBlock Text="{Binding CurrentValue}" />

而且您为面板选择了一个非常糟糕的名称 class。 不由自主地与 System.Windows.Controls.Panel 有关联,这令人困惑。