C#和wpf-ListBox-Label-ComboBox之间的(OneWay-Mode)链绑定的意外行为 [英] C# & wpf - Unexpected behavior of (OneWay-Mode) chain-binding between ListBox-Label-ComboBox

查看:53
本文介绍了C#和wpf-ListBox-Label-ComboBox之间的(OneWay-Mode)链绑定的意外行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下奇怪的情况(对我来说)
ListBox通过OneWay模式绑定(作为源)到Label,即ListBox是只读的.然后,将标签绑定到具有TwoWay绑定的ComboBox

I have the following strange (for me) situation
A ListBox is bound (as Source) to a Label with OneWay Mode, i.e. ListBox is read-only. The Label is then bound to a ComboBox with TwoWay binding

ListBox --> Label <--> ComboBox - arrows denote binding mode

奇怪的是,当程序启动并且用户在列表框的列表中进行选择时,所有3个控件的行为均与预期的一样.但是,一旦从Combobox中选择了一个索引,Label就会继续正常工作(由Combo更新),但是与ListBox的OneWay绑定会消失(为null),并且ListBox无法再更新Label.

The strange thing is that when the program starts and the user selects through the list in the ListBox, all 3 controls behave as expected. But as soon as one index is chosen from Combobox, the Label continues to work properly (is updated by the Combo), but the OneWay binding to ListBox disappears (is null) and the ListBox cannot update the Label any more.

在我看来,当通过OneWay绑定(如此处的Combo更新或使用ValueConverter)以外的其他方式设置标签内容"时,此绑定将由WPF清除.

It seems to me that when Label Content is set by other means besides the OneWay binding (as here with the Combo updating or maybe with a ValueConverter), this binding is cleared by WPF.

另一个奇怪的行为是,如果将ListBox和Label之间的OneWay绑定转换为TwoWay,则一切正常.

The other strange behavior is that if this OneWay binding between ListBox and Label is turned into a TwoWay one, then everything works perfectly.

问题是我在做什么错了,或者这是正常现象,在哪里可以找到相关文档.

The question is what am I doing wrong, or if this is the normal behavior, where could I find relevant documentation.

请在下面找到简化的代码和XAML演示案例.我的解决方法是在ListBox_SelectionChanged中使用代码设置标签内容.

Please find below simplified code and XAML demonstrating the case. My workaround is to set the Label Content with code in ListBox_SelectionChanged.

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace Test_Chained_controls
{
   public partial class MainWindow : Window
   {
      public class ComboItems
      {
         public int iDX { get; set; }
         public string sDesc { get; set; }

         public ComboItems(int a, string b)
         {
            iDX = a;
            sDesc = b;
         }
      }

      public class ListItems
      {
         public int iLDX { get; set; }
         public ListItems(int a)
         {
            iLDX = a;
         }
      }

      public List<ListItems> intList = new List<ListItems>();
      public List<ComboItems> idx_StrList = new List<ComboItems>();

      public MainWindow()
      {
         InitializeComponent();

         intList.Add(new ListItems(0));
         intList.Add(new ListItems(1));
         intList.Add(new ListItems(2));
         intList.Add(new ListItems(3));

         idx_StrList.Add(new ComboItems(0, "Zero"));
         idx_StrList.Add(new ComboItems(1, "One"));
         idx_StrList.Add(new ComboItems(2, "Two"));
         idx_StrList.Add(new ComboItems(3, "Three"));
      }

      private void Window_Loaded(object sender, RoutedEventArgs e)
      {
         listBox.ItemsSource = intList;
         comboBox.ItemsSource = idx_StrList;
      }

      private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
      {
         //// Set Label Content in case of OneWay
         // var binding = BindingOperations.GetBinding(label, Label.ContentProperty);
         // if (binding != null)
         // {
         //    if (binding.Mode == BindingMode.OneWay)
         //       {}  // Binding set - do nothing
         // }
         // else label.Content = listBox.SelectedItem;
      }
   }
}

XAML

<Window ... normal stuff
        xmlns:local="clr-namespace:Test_Chained_controls"
        mc:Ignorable="d"
        Title="MainWindow" Height="182" Width="500" Loaded="Window_Loaded">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="120"/>
            <ColumnDefinition Width="140"/>
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="40"/>
            <RowDefinition Height="100"/>
        </Grid.RowDefinitions>

        <Label Content="ListBox"    Grid.Row="0" Grid.Column="0" Margin="20,10,0,0" />
        <Label Content="Label"      Grid.Row="0" Grid.Column="1" Margin="20,10,0,0" />
        <Label Content="ComboBox"   Grid.Row="0" Grid.Column="2" Margin="20,10,0,0" />

        <ListBox x:Name="listBox"   Grid.Row="1" Grid.Column="0" Margin="0"  
                 DisplayMemberPath="iLDX" 
                 SelectedIndex="0"
                 IsSynchronizedWithCurrentItem="True" 
                 SelectionChanged="ListBox_SelectionChanged"/>

        <Border BorderThickness="1" Grid.Row="1" Grid.Column="1" Height="30" 
                Margin="20,20,0,0" BorderBrush="#FFACACAC"  >

            <!-- *********** Label with Mode=OneWay or TwoWay *********** -->
            <Label x:Name="label" Width="100" Height="25"
                   Content="{Binding ElementName=listBox, 
                             Path=SelectedItem.iLDX, Mode=OneWay }" />
        </Border>

        <ComboBox x:Name="comboBox" Grid.Row="1" Grid.Column="2" 
                                   Height="30" Margin="20,20,0,0"  

                  DisplayMemberPath="sDesc" 
                  SelectedValue="{Binding ElementName=label, Path=Content, 
                  TargetNullValue=0, FallbackValue=0, Mode=TwoWay}"
                  SelectedValuePath="iDX"  />
    </Grid>
</Window>

编辑

相关文档:依赖性属性概述

本地值:可以通过属性包装器的便利性来设置本地值,这也等同于在XAML中设置为属性或属性元素,或者通过调用使用特定实例的属性的SetValue 方法.如果您通过使用绑定或静态资源来设置本地值,则它们都将优先执行,就像设置了本地值一样,,并且如果设置了新的本地值,则绑定或资源引用将被删除.

Local value: A local value might be set through the convenience of the property wrapper, which also equates to setting as an attribute or property element in XAML, or by a call to the SetValue method using a property of a specific instance. If you set a local value by using a binding or a static resource, these each act in the precedence as if a local value was set, and bindings or resource references are erased if a new local value is set.

再往下

如果为最初保留绑定值的属性设置另一个本地值,您将完全覆盖绑定,而不仅仅是绑定的运行时值.

据我了解,这种情况下存在某种错误,已通过引入DependencyObject进行了修复. SetCurrentValue

As I understand, there was some kind of bug related to this case, fixed with the introduction of DependencyObject.SetCurrentValue The Control Local Values Bug Solution

public void SetCurrentValue (System.Windows.DependencyProperty dp, object value);
// Sets the value of a dependency property without changing its value source.

在我看来,Combobox TwoWay绑定仍在使用 SetValue ,这就是为什么在使用我的(组合框)时会删除(标签)的绑定的原因.

It seems to me that Combobox TwoWay binding is still using SetValue, and that's why the binding for (label) gets erased when my (combobox) is used.

为克服此问题,我将(comboBox)的TwoWay绑定更改为OneWay,并在comboBox_DropDownClosed事件(显示当前选中的项)中输入了以下内容,以便通过代码更新(标签)而不删除现有绑定

To overcome this, I changed the TwoWay binding of (comboBox) to OneWay, and entered the following in the comboBox_DropDownClosed event (showing the currently selected Item), in order to update (label) by code without erasing the existing binding

  private void comboBox_DropDownClosed(object sender, System.EventArgs e)
  {
     Binding binding = BindingOperations.GetBinding(label, Label.ContentProperty);
     if (binding != null)
     {
        ComboItems ComboItem = comboBox.SelectedItem as ComboItems;
        int iDX = ComboItem.iDX;

        // Set label value without affecting existing binding
        label.SetCurrentValue(Label.ContentProperty, iDX);
     }
  }

通过使用 SetCurrentValue ,现在可以通过模拟" TwoWay模式来按原计划工作代码.

With the use of SetCurrentValue, code works now as originally intended by "simulating" the TwoWay mode.

推荐答案

一点也不奇怪.数据绑定被设计为以这种方式工作.当您将绑定分配给依赖项属性时,这意味着您可以将此依赖项属性的本地值更改为绑定表达式.绑定源提供的任何更新将是此依赖项属性的有效值.如果绑定以单向方式工作,则从其他绑定源开始对此依赖项属性进行的任何更新都将覆盖本地值,从而导致丢失绑定.另一方面,因为假设两种方式都将更新绑定源,所以依赖对象会将任何非表达式值都视为有效值,绑定将一直起作用,直到您替换或清除它为止.

There's nothing strange at all. Data binding is designed to work this way. When you assign a binding to a dependency property, it means you change the local value of this dependency property to a binding expression. And any update provide by the binding source will be the effective value of this dependency property. If the binding is working in one way mode, any update to this dependency property from other then binding source, will overwrite the local value, result in losing the binding. On the other side, becuase two mode is suppose to update the binding source, dependency object will count any non-expression value as effective value, binding will keep working until you replace or clear it.

  • DependencyObject.GetValue 获取有效值.
  • DependencyObject.ReadLocalValue 获取本地值.
  • DependencyObject.SetValue 设置本地值.
  • DependencyObject.SetCurrentValue 设置有效值.
  • DependencyObject.ClearValue 清除本地值.
  • DependencyObject.GetValue gets the effective value.
  • DependencyObject.ReadLocalValue gets the local value.
  • DependencyObject.SetValue sets the local value.
  • DependencyObject.SetCurrentValue sets the effective value.
  • DependencyObject.ClearValue clears the local value.

这篇关于C#和wpf-ListBox-Label-ComboBox之间的(OneWay-Mode)链绑定的意外行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆