簡體   English   中英

C#和WPF-ListBox-Label-ComboBox之間的(OneWay-Mode)鏈綁定的意外行為

[英]C# & wpf - Unexpected behavior of (OneWay-Mode) chain-binding between ListBox-Label-ComboBox

我有以下奇怪的情況(對我來說)
ListBox通過OneWay模式綁定(作為源)到Label,即ListBox是只讀的。 然后使用TwoWay綁定將Label綁定到ComboBox

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

奇怪的是,當程序啟動並且用戶在列表框的列表中進行選擇時,所有3個控件的行為均與預期的一樣。 但是,一旦從Combobox中選擇了一個索引,Label就會繼續正常工作(由Combo更新),但是與ListBox的OneWay綁定會消失(為null),並且ListBox無法再更新Label。

在我看來,當通過OneWay綁定(如此處的Combo更新或使用ValueConverter)以外的其他方式設置Label Content時,WPF會清除此綁定。

另一個奇怪的行為是,如果將ListBox和Label之間的OneWay綁定轉換為TwoWay,則一切正常。

問題是我在做什么錯,或者這是正常現象,在哪里可以找到相關文檔。

請在下面找到簡化的代碼和XAML演示案例。 我的解決方法是使用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方法。 如果您通過使用綁定或靜態資源來設置本地值,則它們各自的優先級就好像設置了本地值一樣, 並且如果設置了新的本地值,則綁定或資源引用將被刪除。

再往下走

如果為原來保留有Binding值的屬性設置了另一個本地值, 則將完全覆蓋綁定,而不僅是綁定的運行時值。

據我了解,這種情況下存在某種錯誤,並通過引入DependencyObject進行了修復。 SetCurrentValue 控件局部值錯誤解決方案

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

在我看來,Combobox TwoWay綁定仍在使用SetValue ,這就是為什么使用我的(combobox)時會刪除(label)的綁定的原因。

為了克服這個問題,我將(comboBox)的TwoWay綁定更改為OneWay,並在comboBox_DropDownClosed事件(顯示當前選擇的項)中輸入了以下內容,以便在不刪除現有綁定的情況下通過代碼更新(標簽)

  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模式原本預期的工作。

一點也不奇怪。 數據綁定旨在以這種方式工作。 當您將綁定分配給依賴項屬性時,這意味着您將此依賴項屬性的本地值更改為綁定表達式。 綁定源提供的任何更新將是此依賴項屬性的有效值。 如果綁定以單向方式工作,則從其他綁定源開始對此依賴項屬性進行的任何更新都將覆蓋本地值,從而導致丟失綁定。 另一方面,由於假設兩種方式都將更新綁定源,所以依賴對象會將任何非表達式值都視為有效值,綁定將一直起作用,直到您替換或清除它為止。

  • DependencyObject.GetValue獲取有效值。
  • DependencyObject.ReadLocalValue獲取本地值。
  • DependencyObject.SetValue設置本地值。
  • DependencyObject.SetCurrentValue設置有效值。
  • DependencyObject.ClearValue清除本地值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM