WPF 多重绑定失败。为什么?

发布于 2024-08-05 08:26:25 字数 3966 浏览 7 评论 0原文

我有这个标记:

   <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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

月下伊人醉 2024-08-12 08:26:25

该问题与 MultiBinding 或转换器无关。 DependencyProperty.UnsetValue 表示绑定没有值。事实上,如果您在调试模式下运行,您可以在 Output 窗口中看到绑定错误:

System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=IsConnected; DataItem=null; target element is 'SolidColorBrush' (HashCode=17654054); target property is 'Color' (type 'Color')
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=IsLoggedIn; DataItem=null; target element is 'SolidColorBrush' (HashCode=17654054); target property is 'Color' (type 'Color')

因此,让我们稍微简化一下标记并应用一些诊断:

<GroupBox>
    <GroupBox.BorderBrush>
        <SolidColorBrush>
            <SolidColorBrush.Color>
                <Binding Path="GroupColor" PresentationTraceSources.TraceLevel="High"/>
            </SolidColorBrush.Color>
        </SolidColorBrush>
    </GroupBox.BorderBrush>
</GroupBox>

应用附加的依赖属性 PresentationTraceSources.TraceLevel code> 产生更多输出:

System.Windows.Data Warning: 52 : Created BindingExpression (hash=17654054) for Binding (hash=44624228)
System.Windows.Data Warning: 54 :   Path: 'GroupColor'
System.Windows.Data Warning: 56 : BindingExpression (hash=17654054): Default mode resolved to OneWay
System.Windows.Data Warning: 57 : BindingExpression (hash=17654054): Default update trigger resolved to PropertyChanged
System.Windows.Data Warning: 58 : BindingExpression (hash=17654054): Attach to System.Windows.Media.SolidColorBrush.Color (hash=52727599)
System.Windows.Data Warning: 60 : BindingExpression (hash=17654054): Use Framework mentor <null>
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source 
System.Windows.Data Warning: 65 : BindingExpression (hash=17654054): Framework mentor not found
System.Windows.Data Warning: 61 : BindingExpression (hash=17654054): Resolve source deferred
System.Windows.Data Warning: 91 : BindingExpression (hash=17654054): Got InheritanceContextChanged event from SolidColorBrush (hash=52727599)
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source 
System.Windows.Data Warning: 66 : BindingExpression (hash=17654054): Found data context element: GroupBox (hash=51393439) (OK)
System.Windows.Data Warning: 67 : BindingExpression (hash=17654054): DataContext is null
System.Windows.Data Warning: 91 : BindingExpression (hash=17654054): Got InheritanceContextChanged event from SolidColorBrush (hash=52727599)
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source 
System.Windows.Data Warning: 65 : BindingExpression (hash=17654054): Framework mentor not found
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source 
System.Windows.Data Warning: 65 : BindingExpression (hash=17654054): Framework mentor not found
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source  (last chance)
System.Windows.Data Warning: 65 : BindingExpression (hash=17654054): Framework mentor not found
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=GroupColor; DataItem=null; target element is 'SolidColorBrush' (HashCode=52727599); target property is 'Color' (type 'Color')

我们看到绑定没有找到 DataContext 并且绑定失败。当我更改 Windows 的构造函数以便在初始化内容之前设置 DataContext 时,绑定会起作用:

public Window1()
{
    DataContext = ...;
    InitializeComponent();
}

这很奇怪,因为对于其他地方的绑定来说这并不重要。不知道为什么它在那里不起作用,所以我只能提供解决方法。例如,有效的方法是将画笔创建为具有绑定的资源(该资源也可以位于 GroupBox 本地):

<GroupBox BorderBrush="{DynamicResource resbrush}">
    <GroupBox.Resources>
        <SolidColorBrush x:Key="resbrush">
            <SolidColorBrush.Color>
                <MultiBinding Converter="{StaticResource ConnectionAndLoggedInToBorderBrush}">
                    <Binding Path="IsConnected"/>
                    <Binding Path="IsLoggedIn"/>
                </MultiBinding>
            </SolidColorBrush.Color>
        </SolidColorBrush>
    </GroupBox.Resources>
</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 the Output window:

System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=IsConnected; DataItem=null; target element is 'SolidColorBrush' (HashCode=17654054); target property is 'Color' (type 'Color')
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=IsLoggedIn; DataItem=null; target element is 'SolidColorBrush' (HashCode=17654054); target property is 'Color' (type 'Color')

So let's simplify the markup a bit and apply some diagnostics:

<GroupBox>
    <GroupBox.BorderBrush>
        <SolidColorBrush>
            <SolidColorBrush.Color>
                <Binding Path="GroupColor" PresentationTraceSources.TraceLevel="High"/>
            </SolidColorBrush.Color>
        </SolidColorBrush>
    </GroupBox.BorderBrush>
</GroupBox>

Applying the attached dependency property PresentationTraceSources.TraceLevel yields some more output:

System.Windows.Data Warning: 52 : Created BindingExpression (hash=17654054) for Binding (hash=44624228)
System.Windows.Data Warning: 54 :   Path: 'GroupColor'
System.Windows.Data Warning: 56 : BindingExpression (hash=17654054): Default mode resolved to OneWay
System.Windows.Data Warning: 57 : BindingExpression (hash=17654054): Default update trigger resolved to PropertyChanged
System.Windows.Data Warning: 58 : BindingExpression (hash=17654054): Attach to System.Windows.Media.SolidColorBrush.Color (hash=52727599)
System.Windows.Data Warning: 60 : BindingExpression (hash=17654054): Use Framework mentor <null>
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source 
System.Windows.Data Warning: 65 : BindingExpression (hash=17654054): Framework mentor not found
System.Windows.Data Warning: 61 : BindingExpression (hash=17654054): Resolve source deferred
System.Windows.Data Warning: 91 : BindingExpression (hash=17654054): Got InheritanceContextChanged event from SolidColorBrush (hash=52727599)
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source 
System.Windows.Data Warning: 66 : BindingExpression (hash=17654054): Found data context element: GroupBox (hash=51393439) (OK)
System.Windows.Data Warning: 67 : BindingExpression (hash=17654054): DataContext is null
System.Windows.Data Warning: 91 : BindingExpression (hash=17654054): Got InheritanceContextChanged event from SolidColorBrush (hash=52727599)
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source 
System.Windows.Data Warning: 65 : BindingExpression (hash=17654054): Framework mentor not found
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source 
System.Windows.Data Warning: 65 : BindingExpression (hash=17654054): Framework mentor not found
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source  (last chance)
System.Windows.Data Warning: 65 : BindingExpression (hash=17654054): Framework mentor not found
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=GroupColor; DataItem=null; target element is 'SolidColorBrush' (HashCode=52727599); target property is 'Color' (type 'Color')

We see that the binding doesn't find a DataContext and the binding fails. When I change the windows's constructor so that DataContext is set before initializing the content the binding works:

public Window1()
{
    DataContext = ...;
    InitializeComponent();
}

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):

<GroupBox BorderBrush="{DynamicResource resbrush}">
    <GroupBox.Resources>
        <SolidColorBrush x:Key="resbrush">
            <SolidColorBrush.Color>
                <MultiBinding Converter="{StaticResource ConnectionAndLoggedInToBorderBrush}">
                    <Binding Path="IsConnected"/>
                    <Binding Path="IsLoggedIn"/>
                </MultiBinding>
            </SolidColorBrush.Color>
        </SolidColorBrush>
    </GroupBox.Resources>
</GroupBox>

I would suggest though to drop the MultiBinding and to do some pre-processing in the DataContext if your UIManager class is some sort of MVVM ViewModel.

花伊自在美 2024-08-12 08:26:25

我的理论。 Color 是 struct (不能为 null),因此 SolidColorBrush.Color = null 是错误的。 WPF 无法创建 SolidColorBrush,并且您会遇到异常。

 <GroupBox.BorderBrush>
     <SolidColorBrush x:Name="Border">
         <SolidColorBrush.Color>
             <MultiBinding Converter="{StaticResource 
                           ConnectionAndLoggedInToBorderBrush}">
                 <Binding Path="IsConnected"/>
                 <Binding Path="IsLoggedIn"/>
             </MultiBinding>
         </SolidColorBrush.Color>
     </SolidColorBrush>
 </GroupBox.BorderBrush>

BorderBrush 是对象(可以为 null),因此 GroupBox.BorderBrush = null 即可。

 <GroupBox.BorderBrush>
      <MultiBinding Converter="{StaticResource 
                    ConnectionAndLoggedInToBorderBrush}">
           <Binding Path="IsConnected"/>
           <Binding Path="IsLoggedIn"/>
      </MultiBinding>
 </GroupBox.BorderBrush>

这个 SolidColorBrush 不是一个对象,而是一个工厂。它仅在需要时才实例化,此时您已经附加了 DataContext。

 <GroupBox.Resources>
      <SolidColorBrush x:Key="resbrush">
           <SolidColorBrush.Color>
                <MultiBinding Converter="{StaticResource 
                              ConnectionAndLoggedInToBorderBrush}">
                     <Binding Path="IsConnected"/>
                     <Binding Path="IsLoggedIn"/>
                </MultiBinding>
           </SolidColorBrush.Color>
      </SolidColorBrush>
 </GroupBox.Resources>

只是我的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.

 <GroupBox.BorderBrush>
     <SolidColorBrush x:Name="Border">
         <SolidColorBrush.Color>
             <MultiBinding Converter="{StaticResource 
                           ConnectionAndLoggedInToBorderBrush}">
                 <Binding Path="IsConnected"/>
                 <Binding Path="IsLoggedIn"/>
             </MultiBinding>
         </SolidColorBrush.Color>
     </SolidColorBrush>
 </GroupBox.BorderBrush>

BorderBrush is object (can be null), so GroupBox.BorderBrush = null is OK.

 <GroupBox.BorderBrush>
      <MultiBinding Converter="{StaticResource 
                    ConnectionAndLoggedInToBorderBrush}">
           <Binding Path="IsConnected"/>
           <Binding Path="IsLoggedIn"/>
      </MultiBinding>
 </GroupBox.BorderBrush>

This SolidColorBrush is not an object but a FACTORY. It is instantiated only when needed, and at that point you already have attached DataContext.

 <GroupBox.Resources>
      <SolidColorBrush x:Key="resbrush">
           <SolidColorBrush.Color>
                <MultiBinding Converter="{StaticResource 
                              ConnectionAndLoggedInToBorderBrush}">
                     <Binding Path="IsConnected"/>
                     <Binding Path="IsLoggedIn"/>
                </MultiBinding>
           </SolidColorBrush.Color>
      </SolidColorBrush>
 </GroupBox.Resources>

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

一枫情书 2024-08-12 08:26:25

正是出于这样的原因,您可能想考虑学习 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文