Иногда происходит сбой привязки, в зависимости от положения {Binding} в файле XAML - PullRequest
1 голос
/ 05 марта 2012

Моя программа содержит несколько экземпляров этой строки:

<local:MyClass Data="{Binding}"/>

т.е. свойство Data привязано к контексту данных окружающего окна. Когда значение окна DataContext изменяется, привязка иногда обновляется, иногда нет; это зависит от положения <local:MyClass...> в файле XAML.

Вот пример ( РЕДАКТИРОВАТЬ: Я изменил {Binding} на {Binding Path=DataContext, ElementName=myWindow}, чтобы подчеркнуть, что проблема не связана с наследованием DataContext):

XAML код:

<Window x:Class="BindTest.MainWindow"
        x:Name="myWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:BindTest"
        Title="Binding Test" Height="101" Width="328">
    <Window.Tag>
        <local:MyClass x:Name="bindfails" Data="{Binding Path=DataContext, ElementName=myWindow}"/>
    </Window.Tag>

    <StackPanel Orientation="Horizontal">
        <Button Margin="5" Padding="5" Click="SetButtonClicked">Set DataContext</Button>
        <Button Margin="5" Padding="5" Click="ReadButtonClicked">Read Bound Property</Button>
        <local:MyClass x:Name="bindworks" Data="{Binding Path=DataContext, ElementName=myWindow}"/>
    </StackPanel>
</Window>

C # код:

using System.Windows;

namespace BindTest
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void SetButtonClicked(object sender, RoutedEventArgs e)
        {
            DataContext = 1234;
        }

        private void ReadButtonClicked(object sender, RoutedEventArgs e)
        {
            string txtA = (bindfails.Data == null ? "null" : bindfails.Data.ToString());
            string txtB = (bindworks.Data == null ? "null" : bindworks.Data.ToString());
            MessageBox.Show(string.Format("bindfails.Data={0}\r\nbindworks.Data={1}", txtA, txtB));
        }
    }

    public class MyClass : FrameworkElement
    {
        #region Dependency Property "Data"

        public object Data
        {
            get { return (object)GetValue(DataProperty); }
            set { SetValue(DataProperty, value); }
        }

        public static readonly DependencyProperty DataProperty =
            DependencyProperty.Register("Data", typeof(object), typeof(MyClass), new UIPropertyMetadata(null));

        #endregion
    }
}

Сначала нажмите кнопку «Установить DataContext», чтобы изменить контекст данных. Затем нажмите кнопку «Read Bound Property», чтобы отобразить это сообщение:

bindfails.Data = нуль

bindworks.Data = 1234

Очевидно, что привязка данных была обновлена ​​только для элемента MyClass, который является дочерним для StackPanel; но привязка данных не была обновлена ​​для элемента MyClass, на который ссылается Window.Tag.

EDIT2: Я также обнаружил, что привязка работает при программном добавлении привязки внутри конструктора MainWindow:

Binding binding = new Binding("DataContext") {Source = this};
bindfails.SetBinding(MyClass.DataProperty, binding);

Привязка не выполняется, только если она объявлена ​​в XAML. Кроме того, проблема не относится к DataContext; это также происходит, когда я использую другие свойства Window, такие как Title.

Может кто-нибудь объяснить это поведение и предложить, как разрешить использование {Binding} в XAML в обоих случаях?

EDIT3: Приведенный выше код не полностью эквивалентен расширению разметки {Binding}. 100% эквивалентный код:

Binding binding = new Binding("DataContext") {ElementName = "myWindow"};
bindfails.SetBinding(MyClass.DataProperty, binding);

Когда я использую этот код, привязка также завершается неудачно (как при связывании в XAML), и в отладочный вывод записывается следующее диагностическое сообщение:

System.Windows.Data Ошибка: 4: не удается найти источник для привязки со ссылкой «ElementName = myWindow».

Очевидно, что свойство ElementName ищет только визуальное дерево или логическое дерево, даже если это не задокументировано в онлайн-документации WPF. Возможно, в XAML нет простого способа установить такую ​​привязку.

Ответы [ 2 ]

1 голос
/ 06 марта 2012

dataContext передается только через дерево объектов.Тег свойства не находится в визуальном дереве и не будет реагировать на событие DataContext, измененное, и привязка не обновляется без этого события.

См. Поле идентификатора свойства зависимости: DataContextProperty из FrameworlElement

Контекст данных - это концепция, которая позволяет объектам наследовать информацию, определяющую привязку, от своих родителей в дереве объектов

http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.datacontext(v=vs.95).aspx

1 голос
/ 06 марта 2012

Полагаю, это не работает, потому что когда вы пишете {Binding}, контекст данных, которые должны быть связаны, наследуется от родителя.В случае bindworks это StackPanel, который наследует DataContext от Window, однако свойство bindfails.Parent равно null.

Интересно, почему вы поместили элемент управления в элемент тега Window в Window?Если по какой-то причине вы должны оставить его объявленным в узле Tag, вы можете напрямую обновить его DataContext, поэтому просто измените метод SetButtonClicked на:

private void SetButtonClicked(object sender, RoutedEventArgs e)
{
    DataContext = 1234;
    bindfails.DataContext = DataContext;
}

Еще один простой способ заставить его работать:просто взяв bindfails из Window.Tag и поместив его где-нибудь в Window, то есть в StackPanel.Далее в конструкторе окна напишите this.Tag = bindfails.Если вы не хотите, чтобы TextBox отображался в форме, вы можете установить для Visibility значение Collapsed (или поместить его в свернутый контейнерный элемент управления).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...