Как сослаться на значение пользовательского свойства в пользовательском элементе управления WPF через Binding? - PullRequest
0 голосов
/ 04 марта 2020

Я создаю приложение WPF с пользовательскими элементами управления UserControls и пытаюсь понять, как должны работать привязки свойств. Я не могу получить даже самую базовую c привязку к работе, и ее достаточно просто превратить в крошечный пример, поэтому я подумал, что кто-то с большим опытом работы с WPF сможет поставить меня на правильный путь.

Я определил пользовательский UserControl с именем TestControl , который предоставляет свойство Foo , которое предназначено для установки в XAML при каждом размещении UserControl.

TestControl.xaml.cs

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

namespace BindingTest
{
    public partial class TestControl : UserControl
    {
        public static readonly DependencyProperty FooProperty = DependencyProperty.Register("Foo", typeof(string), typeof(TestControl));
        public string Foo
        {
            get { return (string)GetValue(FooProperty); }
            set { SetValue(FooProperty, value); }
        }

        public TestControl()
        {
            InitializeComponent();
        }
    }
}

Разметка для TestControl просто определяет его как элемент управления с помощью одной кнопки, текст метки которой отображает текущее значение свойства Foo:

TestControl.xaml

<UserControl x:Class="BindingTest.TestControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:BindingTest"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Button Content="{Binding Foo}" />
    </Grid>
</UserControl>

В моем классе MainWindow я просто помещаю один экземпляр TestControl со свойством Foo, установленным в «Hello».

MainWindow.xaml

<Window x:Class="BindingTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BindingTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <local:TestControl Foo="Hello" />
    </Grid>
</Window>

Я ожидаю, что при сборке и запуске этого приложения я увижу окно с одной кнопкой с надписью «Hello» , Однако кнопка пуста: привязка не работает.

Если я добавлю обработчик щелчка к кнопке TestControl, я могу убедиться, что значение обновляется за кулисами:

// Added to TestControl.xaml.cs:
private void Button_Click(object sender, RoutedEventArgs e)
{
    Console.WriteLine("Button clicked; Foo is '{0}'", Foo);
}

// Updated in TestControl.xaml:
// <Button Content="{Binding Foo}" Click="Button_Click" />

Когда я нажимаю кнопку, я получаю Button clicked; Foo is 'Hello', но GUI никогда не обновляется. Я попытался использовать Path=Foo, XPath=Foo, et c., А также установить UpdateSourceTrigger=PropertyChanged и проверять обновления с помощью NotifyOnTargetUpdated=True ... кажется, ничего не приводит к обновлению текста в пользовательском интерфейсе для соответствия базовое значение свойства, хотя значение свойства, похоже, обновляется очень хорошо.

Что я делаю не так? Я чувствую, что есть просто простое и фундаментальное недоразумение в том, как я к этому подхожу.

edit:

Возни немного больше и читая похожие вопросы привел меня к потенциальному исправлению: добавление имени к элементу root UserControl в TestControl.xaml (x:Name="control") и изменение привязки для явного указания этого элемента управления ({Binding Foo, ElementName=control}).

Я предполагаю, что по умолчанию {Binding Foo} в элементе Button просто означает "найти свойство с именем 'Foo' в этом элементе управления Button" , тогда как я предполагал, что это будет означать "найти свойство с именем 'Foo' в контексте, в котором эта кнопка объявляется, т.е. в TestControl" .

Является ли указание явного ElementName лучшим решением здесь?

1 Ответ

1 голос
/ 04 марта 2020

Вы должны установить исходный объект Binding для экземпляра UserControl, например, так:

<Button Content="{Binding Foo, RelativeSource={RelativeSource AncestorType=UserControl}}"/>

или

<UserControl ... x:Name="theControl">
...
<Button Content="{Binding Foo, ElementName=theControl}"/>

Если у вас много таких привязок вы также можете установить DataContext элемента верхнего уровня в XAML UserControl в экземпляр UserControl:

<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
    <Button Content="{Binding Foo}" />
    <Button Content="{Binding Bar}" />
</Grid>

Однако вы должны избегать установки DataContext из UserControl (который часто рекомендуется «экспертные» блоггеры), потому что это нарушит привязки на основе DataContext свойств UserControl, например

<local:TestControl Foo="{Binding SomeFoo}" />
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...