如何在 WPF 中动态设置绑定路径?
How can you dynamically set binding path in WPF?
我有一个 ContentControl
,其中 ItemSource
设置为 Panel
的集合。我想制作一个 DataTemplate
,其中 DataContext
设置为 Panel
。 Panel
有一个public属性Receiver
,是接收数据的对象。 class 实现了 INotifyPropertyChanged
接口,当它的 public 属性 Parameters
完成更新时, PropertyChanged
事件被触发并且参数是更新。面板的内容应该是ParameterType
(包含Param1
和Param2
的枚举)给出的相应属性,它本身就是一个属性 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
中使用此控件,其中 Panel
为 DataContext
,我想绑定到如下内容:
<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 有关联,这令人困惑。
我有一个 ContentControl
,其中 ItemSource
设置为 Panel
的集合。我想制作一个 DataTemplate
,其中 DataContext
设置为 Panel
。 Panel
有一个public属性Receiver
,是接收数据的对象。 class 实现了 INotifyPropertyChanged
接口,当它的 public 属性 Parameters
完成更新时, PropertyChanged
事件被触发并且参数是更新。面板的内容应该是ParameterType
(包含Param1
和Param2
的枚举)给出的相应属性,它本身就是一个属性 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
中使用此控件,其中 Panel
为 DataContext
,我想绑定到如下内容:
<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 有关联,这令人困惑。