WPF 多重绑定失败。为什么?
我有这个标记:
<GroupBox BorderThickness="2">
<GroupBox.BorderBrush>
<SolidColorBrush x:Name="Border">
<SolidColorBrush.Color>
<MultiBinding Converter="{StaticResource ConnectionAndLoggedInToBorderBrush}">
<Binding Path="IsConnected"/>
<Binding Path="IsLoggedIn"/>
</MultiBinding>
</SolidColorBrush.Color>
</SolidColorBrush>
</GroupBox.BorderBrush>
在后面的代码中,我在 window_loaded 方法中有这一行:
DataContext = uiManager;
uiManager 属于 UIManager 类型,有两个名为 IsConnected 和 IsLoggedIn 的公共属性。
此代码在启动时失败,因为多重绑定调用的转换器中的值数组未填充布尔值,而是具有 DependencyProperty.UnsetValue 值。
当我使用下面的标记(并更改转换器的返回类型)时,它确实有效。
<GroupBox BorderThickness="2">
<GroupBox.BorderBrush>
<MultiBinding Converter="{StaticResource ConnectionAndLoggedInToBorderBrush}">
<Binding Path="IsConnected"/>
<Binding Path="IsLoggedIn"/>
</MultiBinding>
</GroupBox.BorderBrush>
看来后面的代码中通过 DataContext 设置的绑定在第一个示例中失败,但在第二个示例中有效。为什么?
为了完整性,UIManager 类下面:
public class UIManager:IUIManager
{
#region Implementation of IUIManager
private const string IsLoggedInProperty = "IsLoggedIn";
private bool loggedIn;
private readonly object loggedInLock = new object();
public bool IsLoggedIn
{
get
{
lock (loggedInLock)
{
return loggedIn;
}
}
set
{
lock (loggedInLock)
{
if(value==loggedIn)return;
loggedIn = value;
OnPropertyChanged(IsLoggedInProperty);
}
}
}
private void OnPropertyChanged(string property)
{
if(PropertyChanged!=null)PropertyChanged(this,new PropertyChangedEventArgs(property));
}
private const string IsConnectedProperty = "IsConnected";
private bool isConnected;
private object isConnectedLock = new object();
public bool IsConnected
{
get
{
lock (isConnectedLock)
{
return isConnected;
}
}
set
{
lock (isConnectedLock)
{
if(value==isConnected)return;
isConnected = value;
OnPropertyChanged(IsConnectedProperty);
}
}
}
#endregion
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
编辑: 转换为 bool of values[0] 失败:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var is_connected = (bool) values[0];
var is_loggedin = (bool) values[1];
return is_loggedin
? is_connected
? Colors.YellowGreen
: Colors.Red
: Colors.Gray;
}
失败的 XAML 的转换方法(对于正常工作的 XAML,
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var is_connected = (bool) values[0];
var is_loggedin = (bool) values[1];
return is_loggedin
? is_connected
? Brushes.YellowGreen
: Brushes.Red
: Brushes.Gray;
}
I have this Markup:
<GroupBox BorderThickness="2">
<GroupBox.BorderBrush>
<SolidColorBrush x:Name="Border">
<SolidColorBrush.Color>
<MultiBinding Converter="{StaticResource ConnectionAndLoggedInToBorderBrush}">
<Binding Path="IsConnected"/>
<Binding Path="IsLoggedIn"/>
</MultiBinding>
</SolidColorBrush.Color>
</SolidColorBrush>
</GroupBox.BorderBrush>
In the code behind I have this line in the window_loaded method:
DataContext = uiManager;
uiManager is of type UIManager that has two public properties named IsConnected and IsLoggedIn.
This code fails at at startup because the values array in the Converter that is called by the Multibinding is not filled with booleans but have a value of DependencyProperty.UnsetValue.
When I use the markup below (and change the returntype of the converter) it does work.
<GroupBox BorderThickness="2">
<GroupBox.BorderBrush>
<MultiBinding Converter="{StaticResource ConnectionAndLoggedInToBorderBrush}">
<Binding Path="IsConnected"/>
<Binding Path="IsLoggedIn"/>
</MultiBinding>
</GroupBox.BorderBrush>
It seems that the binding set through the DataContext in the code behind fails in the first example, but works in the second one. Why?
For completeness below the UIManager class:
public class UIManager:IUIManager
{
#region Implementation of IUIManager
private const string IsLoggedInProperty = "IsLoggedIn";
private bool loggedIn;
private readonly object loggedInLock = new object();
public bool IsLoggedIn
{
get
{
lock (loggedInLock)
{
return loggedIn;
}
}
set
{
lock (loggedInLock)
{
if(value==loggedIn)return;
loggedIn = value;
OnPropertyChanged(IsLoggedInProperty);
}
}
}
private void OnPropertyChanged(string property)
{
if(PropertyChanged!=null)PropertyChanged(this,new PropertyChangedEventArgs(property));
}
private const string IsConnectedProperty = "IsConnected";
private bool isConnected;
private object isConnectedLock = new object();
public bool IsConnected
{
get
{
lock (isConnectedLock)
{
return isConnected;
}
}
set
{
lock (isConnectedLock)
{
if(value==isConnected)return;
isConnected = value;
OnPropertyChanged(IsConnectedProperty);
}
}
}
#endregion
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
EDIT:
The conversion method for the failing XAML (it fails on the conversion to bool of values[0]:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var is_connected = (bool) values[0];
var is_loggedin = (bool) values[1];
return is_loggedin
? is_connected
? Colors.YellowGreen
: Colors.Red
: Colors.Gray;
}
for the working XAML:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var is_connected = (bool) values[0];
var is_loggedin = (bool) values[1];
return is_loggedin
? is_connected
? Brushes.YellowGreen
: Brushes.Red
: Brushes.Gray;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
该问题与 MultiBinding 或转换器无关。
DependencyProperty.UnsetValue
表示绑定没有值。事实上,如果您在调试模式下运行,您可以在Output
窗口中看到绑定错误:因此,让我们稍微简化一下标记并应用一些诊断:
应用附加的依赖属性
PresentationTraceSources.TraceLevel
code> 产生更多输出:我们看到绑定没有找到
DataContext
并且绑定失败。当我更改 Windows 的构造函数以便在初始化内容之前设置 DataContext 时,绑定会起作用:这很奇怪,因为对于其他地方的绑定来说这并不重要。不知道为什么它在那里不起作用,所以我只能提供解决方法。例如,有效的方法是将画笔创建为具有绑定的资源(该资源也可以位于 GroupBox 本地):
我建议删除 MultiBinding 并如果您的
UIManager
类是某种MVVM
ViewModel
,请在DataContext
中进行一些预处理。The problem has nothing to do with a
MultiBinding
or your converter.DependencyProperty.UnsetValue
means that the binding got no value. And indeed if you run in debug mode you can see binding errors in theOutput
window:So let's simplify the markup a bit and apply some diagnostics:
Applying the attached dependency property
PresentationTraceSources.TraceLevel
yields some more output:We see that the binding doesn't find a
DataContext
and the binding fails. When I change the windows's constructor so thatDataContext
is set before initializing the content the binding works:Which is weird since for bindings in other places this doesn't matter. Not sure why it doesn't work there so I can only offer workarounds. What works for example is creating the brush as a resource with the bindings (that resource could also be local to the
GroupBox
):I would suggest though to drop the
MultiBinding
and to do some pre-processing in theDataContext
if yourUIManager
class is some sort ofMVVM
ViewModel
.我的理论。 Color 是 struct (不能为 null),因此 SolidColorBrush.Color = null 是错误的。 WPF 无法创建 SolidColorBrush,并且您会遇到异常。
BorderBrush 是对象(可以为 null),因此 GroupBox.BorderBrush = null 即可。
这个 SolidColorBrush 不是一个对象,而是一个工厂。它仅在需要时才实例化,此时您已经附加了 DataContext。
只是我的2分钱。
顺便说一句,如果您需要一些奇怪的绑定或带有奇怪转换器的动画,请阅读我的文章,这可能会很有用。 http://www.codeproject.com/KB/WPF/BindingHub.aspx
My theory. Color is struct (cannot be null), so SolidColorBrush.Color = null is wrong. WPF cannot create SolidColorBrush, and you get exception.
BorderBrush is object (can be null), so GroupBox.BorderBrush = null is OK.
This SolidColorBrush is not an object but a FACTORY. It is instantiated only when needed, and at that point you already have attached DataContext.
Just my 2 cents.
Read my article, btw, could be useful if you need some weird Bindings or Animations with weird Converters. http://www.codeproject.com/KB/WPF/BindingHub.aspx
正是出于这样的原因,您可能想考虑学习 MVVM。此模式可帮助您抽象模型和绑定,以便您不必如此依赖 DP - 您可以简单地绑定到视图模型中的可通知属性。
有几篇关于 MVVM 的优秀文章,所以我建议您首先阅读 Karl Shifflett、Josh Smith、Marlon Grech 和 Sacha Barber 的作品。
It's for reasons like this that you might want to consider learning MVVM. This pattern helps you to abstract the model and bindings so that you don't have to rely so heavily on DPs - you can simply bind to a notifiable property in a view-model instead.
There are several excellent articles on MVVM, so I'd suggest you start by reading the works of Karl Shifflett, Josh Smith, Marlon Grech and Sacha Barber.