<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 Error: 4: Не удается найти источник для привязки с ссылка 'ElementName = myGrid'. BindingExpression: Path = ActualWidth; DataItem = NULL; целевым элементом является" Метка "(Name= ''); целевое свойство" Содержимое "(тип" Объект ")"
Я использую версию сообщества Visual Studio 2015 с .Net Framework 4.5.2. Есть идеи? Заранее благодарю вас.
Элемент (свойство которого привязан) должен быть частью визуального дерева, чтобы можно было выполнить визуальный поиск дерева. При использовании привязки с ElementName
или RelativeSource
он выполняет некоторый внутренний поиск визуального дерева. Но в вашем коде Label
отключен от визуального дерева с помощью Tag
. Label
- это просто объект в памяти, на который ссылается свойство Tag.
Начиная с .NET 4.0 вы можете использовать разметку {x:Reference}
, например:
<Grid.Tag>
<Label Content="{Binding ActualWidth, Source={x:Reference myGrid}}" />
</Grid.Tag>
Edit
Использование {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
, поэтому довольно интересно, что все Bindings, установленные для любого DependencyProperty объекта Freezable
, хорошо работают независимо от того, используете ли вы ElementName
или RelativeSource
. Поэтому мы привязываем его свойство Value
к Grid
(с именем myGrid
). Позже мы привязываем свойство Content
объекта Label
к объекту Freezable
, но с Path
, установленным в Value.ActualWidth
(Value
указывает на Grid
), поэтому нам нужно добавить ActualWidth
, чтобы связать его до Grid.ActualWidth
).
Фактически вы можете использовать любой объект Freezable
, но для удобства мы используем DiscreteObjectKeyFrame
, его Value
принимает все типы object
.
Существует еще один способ установки привязки в такой ситуации (отключенный контекст), но для этого требуется создать пользовательский 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>