WPF多绑定中目标元素在不同UserControl时如何指定ElementName?
问题描述
原本在WPF窗口中,按钮的命令参数通过多绑定关联两个密码框,代码如下:
<Button.CommandParameter> <MultiBinding Converter="{converters:Converter_MultipleCommandParameters}"> <Binding ElementName="passwordBoxSQL" /> <Binding ElementName="passwordBoxR2" /> </MultiBinding> </Button.CommandParameter>
密码框定义:
<PasswordBox x:Name="passwordBoxSQL" Grid.Row="4" Grid.Column="1" Margin="0,0,10,0" VerticalAlignment="Center" />
将包含密码框的Grid移至名为Test的UserControl(位于XYZ.Control命名空间)后,在窗口中引用该控件:
<Window xmlns:local="clr-namespace:XYZ.Control" .... <local:Test />
程序运行后界面正常,但出现绑定失败错误:XAML Binding Failures Description - Cannot find source: ElementName=passwordBoxSQL。
需要解决如何正确指定Binding的ElementName,或采用其他实现方式。
解决方案
方法1:在UserControl中暴露依赖属性(推荐,低耦合)
把密码框的密码值封装成UserControl的依赖属性,窗口直接绑定这些属性,避免跨控件树查找元素。
- 在
TestUserControl中添加两个依赖属性:
public static readonly DependencyProperty SQLPasswordProperty = DependencyProperty.Register( "SQLPassword", typeof(string), typeof(Test), new PropertyMetadata(string.Empty)); public string SQLPassword { get => (string)GetValue(SQLPasswordProperty); set => SetValue(SQLPasswordProperty, value); } public static readonly DependencyProperty R2PasswordProperty = DependencyProperty.Register( "R2Password", typeof(string), typeof(Test), new PropertyMetadata(string.Empty)); public string R2Password { get => (string)GetValue(R2PasswordProperty); set => SetValue(R2PasswordProperty, value); }
- 在UserControl的XAML中,为密码框添加
PasswordChanged事件处理:
<PasswordBox x:Name="passwordBoxSQL" Grid.Row="4" Grid.Column="1" Margin="0,0,10,0" VerticalAlignment="Center" PasswordChanged="PasswordBoxSQL_PasswordChanged" />
- 后台处理事件更新依赖属性:
private void PasswordBoxSQL_PasswordChanged(object sender, RoutedEventArgs e) { if (sender is PasswordBox pb) SQLPassword = pb.Password; } // 同理处理passwordBoxR2的PasswordChanged事件
- 窗口中绑定UserControl的依赖属性作为命令参数(记得给UserControl命名):
<Button.CommandParameter> <MultiBinding Converter="{converters:Converter_MultipleCommandParameters}"> <Binding ElementName="testControl" Path="SQLPassword" /> <Binding ElementName="testControl" Path="R2Password" /> </MultiBinding> </Button.CommandParameter> <local:Test x:Name="testControl" />
方法2:使用RelativeSource跨控件树查找(不推荐,高耦合)
如果不想修改UserControl,可以用RelativeSource找到UserControl,再指向内部密码框,但UserControl内部结构变更会导致绑定失效。
- 修改窗口中的绑定:
<Button.CommandParameter> <MultiBinding Converter="{converters:Converter_MultipleCommandParameters}"> <Binding RelativeSource="{RelativeSource AncestorType={x:Type local:Test}}" Path="passwordBoxSQL.Password" /> <Binding RelativeSource="{RelativeSource AncestorType={x:Type local:Test}}" Path="passwordBoxR2.Password" /> </MultiBinding> </Button.CommandParameter>
- 把UserControl内部的密码框
x:FieldModifier设为public,允许外部访问:
<PasswordBox x:Name="passwordBoxSQL" x:FieldModifier="public" Grid.Row="4" Grid.Column="1" Margin="0,0,10,0" VerticalAlignment="Center" />
方法3:MVVM模式下绑定ViewModel(最佳实践)
如果采用MVVM,在ViewModel中定义密码属性,UserControl和窗口都绑定到ViewModel:
- ViewModel中添加属性(需实现
INotifyPropertyChanged):
private string _sqlPassword; public string SQLPassword { get => _sqlPassword; set => SetProperty(ref _sqlPassword, value); } private string _r2Password; public string R2Password { get => _r2Password; set => SetProperty(ref _r2Password, value); }
- UserControl中处理密码框的
PasswordChanged事件更新ViewModel:
private void PasswordBoxSQL_PasswordChanged(object sender, RoutedEventArgs e) { if (DataContext is YourViewModel vm && sender is PasswordBox pb) vm.SQLPassword = pb.Password; }
- 窗口按钮直接绑定ViewModel的属性:
<Button.CommandParameter> <MultiBinding Converter="{converters:Converter_MultipleCommandParameters}"> <Binding Path="SQLPassword" /> <Binding Path="R2Password" /> </MultiBinding> </Button.CommandParameter>
内容的提问来源于stack exchange,提问作者user26526535
相关产品推荐
相关产品推荐

