Порядок операций в отношении DataContext, жестко закодированных значений, выражений привязки, шаблонов и вложенных элементов управления - PullRequest
6 голосов
/ 26 апреля 2011

Это беспокоило меня некоторое время, и я устал работать над этой проблемой.Что такое «порядок операций» в WPF:

  • Настройка DataContext
  • Наследование DataContext
  • Оценка значения «жестко закодированного» свойства
  • Оценка значения свойства {Binding}

Все это с учетом вложенных элементов управления и шаблонов (когда применяются шаблоны).

У меня был номерпроблемных сценариев, но вот только один пример:

Пользовательский пользовательский элемент управления

<UserControl x:Class="UserControls.TestUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             >
    <StackPanel>
        <Label Content="{Binding Label1}" />
        <Label Content="{Binding Label2}" />
    </StackPanel>
</UserControl>

Код пользовательского элемента управления

using System;
using System.Windows;
using System.Windows.Controls;

namespace UserControls
{
    public partial class TestUserControl : UserControl
    {
        public static readonly DependencyProperty Label1Property = DependencyProperty.Register("Label1", typeof(String), typeof(TestUserControl), new FrameworkPropertyMetadata(OnLabel1PropertyChanged));
        public String Label1
        {
            get { return (String)GetValue(Label1Property); }
            set { SetValue(Label1Property, value); }
        }

        public static readonly DependencyProperty Label2Property = DependencyProperty.Register("Label2", typeof(String), typeof(TestUserControl), new FrameworkPropertyMetadata(OnLabel2PropertyChanged));
        public String Label2
        {
            get { return (String)GetValue(Label2Property); }
            set { SetValue(Label2Property, value); }
        }

        public TestUserControl()
        {
            DataContext = this;

            InitializeComponent();
        }

        private static void OnLabel1PropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
        {
            //used for breakpoint
        }

        private static void OnLabel2PropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
        {
            //used for breakpoint
        }
    }
}

Окно для использования пользовательского элемента управления

<Window x:Class="Windows.TestWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:UC="clr-namespace:UserControls"
        >
    <StackPanel>
        <Label Content="Non user control label" />

        <UC:TestUserControl x:Name="uc" Label1="User control label 1" Label2="{Binding Label2FromWindow}" />
    </StackPanel>
</Window>

И код для окна

using System;
using System.Windows;

namespace Windows
{
    public partial class TestWindow : Window
    {
        public String Label2FromWindow
        {
            get { return "User control label 2"; }
        }

        public TestWindow()
        {
            DataContext = this;

            InitializeComponent();
        }
    }
}

Итак, в этом сценарии, почему «Label2» в пользовательском элементе управления никогда не получает значение из «Label2FromWindow» изокно?Я чувствую, что это проблема синхронизации, когда пользовательский элемент управления сначала оценивает все его выражения, а затем окно оценивает его выражения позже, и пользовательский элемент управления никогда не «уведомляется» об оцененных значениях окна.

надеюсь, что пример полезен для иллюстрации одной проблемы, но мой реальный вопрос:

Каков порядок операций с DataContext, жестко заданными значениями свойств, выражений связывания, шаблонов и вложенных элементов управления?

РЕДАКТИРОВАТЬ:

HB помог мне прийти к этой реализации.Когда DataContext окна установлен на себя, пользовательский элемент управления «наследует» DataContext.Это позволяет Binding работать со свойством пользовательского элемента управления, но тогда в пользовательском элементе управления Binding к его локальным свойствам работать не будет.Когда DataContext устанавливается непосредственно в пользовательском элементе управления, привязка окна к свойству пользовательского элемента управления больше не работает, но пользовательский элемент управления может затем привязать к своим собственным локальным свойствам.Ниже приведен обновленный пример кода, который работает.

Контроль пользователя:

<UserControl x:Class="UserControls.TestUserControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                Name="uc">
    <StackPanel>
        <Label Content="{Binding ElementName=uc, Path=Label1}" />
        <Label Content="{Binding ElementName=uc, Path=Label2}" />
    </StackPanel>
</UserControl>

Код контроля пользователя:

using System;
using System.Windows;
using System.Windows.Controls;

namespace UserControls
{
    public partial class TestUserControl : UserControl
    {
        public static readonly DependencyProperty Label1Property = DependencyProperty.Register("Label1", typeof(String), typeof(TestUserControl));
        public String Label1
        {
            get { return (String)GetValue(Label1Property); }
            set { SetValue(Label1Property, value); }
        }

        public static readonly DependencyProperty Label2Property = DependencyProperty.Register("Label2", typeof(String), typeof(TestUserControl));
        public String Label2
        {
            get { return (String)GetValue(Label2Property); }
            set { SetValue(Label2Property, value); }
        }

        public TestUserControl()
        {
            InitializeComponent();
        }
    }
}

Окно тестирования:

<Window x:Class="Windows.TestWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:UC="clr-namespace:UserControls"
        >
    <StackPanel>
        <Label Content="Non user control label" />

        <UC:TestUserControl Label1="User control label 1" Label2="{Binding Label2FromWindow}" />
    </StackPanel>
</Window>

Код окна проверки кода:

using System;
using System.Windows;

namespace Windows
{
    public partial class TestWindow : Window
    {
        public String Label2FromWindow
        {
            get { return "User control label 2"; }
        }

        public TestWindow()
        {
            DataContext = this;
            InitializeComponent();
        }
    }
}

Ответы [ 2 ]

3 голосов
/ 26 апреля 2011

Я думаю, что дело не в порядке, а в приоритете (возможно, я здесь расщепляю волосы). Вы явно устанавливаете DataContext для UserControl - это означает, что оно не будет унаследовано , поэтому ваша привязка ищет свойство Label2FromWindow внутри UserControl. Очевидно, он не находит его.

Просто никогда не устанавливайте DataContext из UserControl экземпляров, и вы не должны сталкиваться с такими проблемами. (Назовите свой UserControl и используйте ElementName для внутренних привязок)

Полный список приоритетов см. В MSDN .

0 голосов
/ 16 декабря 2013

Другой способ, который работает хорошо, - это дать первому дочернему элементу вашего UserControl x: Name = "LayoutRoot".Затем в конструкторе UserControl используйте LayoutRoot.DataContext = this.

Этот метод позволяет устанавливать значения DependencyProperties, определенных для UserControl, из окружающего окна, а также по-прежнему использовать стандартные привязки внутри разметки UserControl безиспользование привязок к ElementName или RelativeSource.

Колин Эберерхардт объяснил это здесь: http://www.scottlogic.com/blog/2012/02/06/a-simple-pattern-for-creating-re-useable-usercontrols-in-wpf-silverlight.html

...