У меня следующая странная (для меня) ситуация
ListBox привязан (как источник) к метке в режиме OneWay, т.е. ListBox доступен только для чтения.
Метка затем привязывается к ComboBox с привязкой TwoWay
ListBox --> Label <--> ComboBox - arrows denote binding mode
Странно, что при запуске программы и выборе пользователем списка в ListBox все 3 элемента управления работают так, как и ожидалось.
Но как только в Combobox выбран один индекс, Label продолжает работать должным образом (обновляется Combo), но привязка OneWay к ListBox исчезает (имеет значение null) и ListBox больше не может обновлять Label.
Мне кажется, что когда содержимое метки устанавливается другими способами, кроме привязки OneWay (как здесь, с обновлением Combo или, возможно, с помощью ValueConverter), эта привязка очищается WPF.
Другое странное поведение состоит в том, что если эта привязка OneWay между ListBox и Label превращается в двустороннюю, то все работает отлично.
Вопрос что я делаю неправильно или, если это нормальное поведение, где я могу найти соответствующую документацию.
Ниже приведен упрощенный код и 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;
}
}
}
1018 * 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 Исправление локальных значений Control Control 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 , и поэтому привязка для (label) стирается при использовании my (combobox).
Чтобы преодолеть это, я изменил привязку TwoWay (comboBox) на 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.