Ошибка привязки данных 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>