Ошибка привязки данных WPF в свойстве Tag

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:local="clr-namespace:son"
         x:Class="son.SonWindow">
    <Grid x:Name="myGrid">
        <Grid.Tag>
            <Label Content="{Binding ActualWidth, ElementName=myGrid}" />
        </Grid.Tag>
    </Grid>
</UserControl>

Как простой код выше, но привязка не может найти элемент myGrid. Во время выполнения ошибка отображается в окне вывода.

"Ошибка System.Windows.Data: 4: не удается найти источник для привязки со ссылкой" ElementName=myGrid ". BindingExpression:Path=ActualWidth; DataItem=null; целевым элементом является" Label "(Name=''); целевым свойством является ' Контент ' (тип' Объект ')"

Я использую версию сообщества Visual Studio 2015 с.Net Framework 4.5.2. Есть идеи? Заранее спасибо.

3 ответа

Решение

Элемент (свойство которого связано) должен быть частью визуального дерева, чтобы можно было выполнить поиск по визуальному дереву. При использовании Binding с ElementName или же RelativeSource, он выполняет некоторый внутренний визуальный поиск дерева. Но в вашем коде Label отключен от визуального дерева Tag, Label это просто объект в памяти, на который ссылается свойство Tag.

Начиная с.NET 4.0 вы можете использовать {x:Reference} вместо разметки, вот так:

<Grid.Tag>
    <Label Content="{Binding ActualWidth, Source={x:Reference myGrid}}" />
</Grid.Tag>

Редактировать:

С помощью {x:Reference} может вызвать проблему циклической зависимости, если имя ссылки указывает на некоторый элемент, содержащий {x:Reference}, В вашем случае это myGrid (содержащий {x:Reference}). Так что это не может быть использовано в вашем случае. Вместо этого вам нужно использовать прокси. Эта техника кажется немного хакерской, но на самом деле она очень красивая. Он также наверняка работает в любой версии.NET (с поддержкой WPF):

<Grid x:Name="myGrid">
    <Grid.Resources>
        <DiscreteObjectKeyFrame x:Key="proxy" Value="{Binding ElementName=myGrid}"/>
    </Grid.Resources>
    <Grid.Tag>
        <Label Content="{Binding Value.ActualWidth, Source={StaticResource proxy}}" />
    </Grid.Tag>
</Grid>

Как вы видите, Binding работает со своими Source установлен в StaticResource указывая на DiscreteObjectKeyFrame, Это Freezable объект, так что довольно интересно, что все привязки установлены для любого DependencyProperty Freezable объект хорошо работает, независимо от того, вы используете ElementName или же RelativeSource, Поэтому мы связываем его Value собственность на Grid (с именем myGrid). Позже мы связываем Content собственностью Label к Freezable объект, но с Path установлен в Value.ActualWidth (Value указывает на Gridтак что нам нужно добавить ActualWidth привязать это к Grid.ActualWidth).

На самом деле вы можете использовать любой Freezable объект, но мы используем DiscreteObjectKeyFrame для удобства его Value принимает все виды object,

Существует другая техника для установки Binding в такой ситуации (отключенный контекст), но она требует создания пользовательского MarkupExtension, Это, конечно, сложнее (но все же достаточно просто, когда вы знакомы с WPF).

Я также пытался повторить проблему, но ниже код работает нормально в VS 2010.

<Window x:Class="CustomEventHandlerClick.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:CustomEventHandlerClick"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid x:Name="myGrid">
        <Grid.Tag>
            <Label Content="{Binding ActualWidth, ElementName=myGrid}"/>
        </Grid.Tag>
    </Grid>
   </Grid>

Я попробовал приведенный ниже фрагмент кода в WPF, и он работает нормально.

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid x:Name="myGrid">
    <Grid.Tag>
        <Label Content="{Binding ActualWidth, ElementName=myGrid}" />
    </Grid.Tag>
</Grid>

Другие вопросы по тегам