【问题标题】:How can you dynamically set binding path in WPF?如何在 WPF 中动态设置绑定路径?
【发布时间】:2020-10-26 14:12:03
【问题描述】:

我有一个ContentControlItemSource 设置为Panels 的集合。我想创建一个DataTemplate,其中DataContext 设置为PanelPanel 有一个公共属性Receiver,它是一个接收数据的对象。该类实现了INotifyPropertyChanged 接口,因此当其公共属性Parameters 完成更新时,将触发PropertyChanged 事件并更新参数。面板的内容应该是ParameterType(一个包含Param1Param2的枚举)给出的对应属性,它本身就是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;}

    ...

}

参数.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;}

}

当接收器通知其属性Parameters 已更改时,我希望我的界面在此属性更改时更新。这对我有用,每当我像这样绑定我的DataTemplate 时:

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

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

但是,我在 DataTemplate 内部使用此控件,它的 PanelDataContext,我想绑定到类似的东西:

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

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

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

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

但是(我认为)这种方法在使用 DataTemplate 时不可用(也不可取)。任何人都可以帮助我并指出正确的方向吗?

亲切的问候,

汤姆

【问题讨论】:

  • 您可以向 Panel 类添加一个属性,该属性仅返回当前参数,即 CurrentReceiver.Parameters.Param1 或 CurrentReceiver.Parameters.Param2。 Panel 类可以将 PropertyChanged 处理程序附加到其 CurrentReceiver,以便在更新时得到通知。

标签: c# wpf xaml mvvm binding


【解决方案1】:

BindableBase 类必须具有为属性分配值的方法:Set (ref ...) 或 SetProperty (ref ..)。 如果我正确理解您的任务,请查看这样的实现。

类参数:

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); }
}

班级接收者:

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); }

}

类面板:

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}" />

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

【讨论】:

    猜你喜欢
    • 2011-01-23
    • 1970-01-01
    • 2011-06-07
    • 2011-04-07
    • 2016-09-08
    • 2023-01-04
    • 2011-03-31
    • 2019-06-03
    • 1970-01-01
    相关资源
    最近更新 更多