Как я могу привязать к элементу управления без Имени или x:Name?
Хорошо, так что я следую за учебником. Этот, чтобы быть точным. Некоторый код, который он предоставляет, таков:
<TextBlock Margin="2" Foreground="Red" FontWeight="Bold"
Text="{Binding ElementName=AddressBox,
Path=(Validation.Errors),
Converter={StaticResource eToMConverter}}" />
Как видите, он привязан к TextBox
ошибки валидации, и это TextBox
"s x:Name
является AddressBox
, Теперь моя проблема: у меня есть Window
немного как этот. Он также имеет только один TextBox
, Тем не менее, я бы предпочел не использовать Name
с или x:Name
Если возможно. Можно ли привязать к другому Control
"s Validation.Errors
не будучи названным этим элементом управления, и этот элемент управления является единственным из этого типа Control
на том же Window
? TextBox
находится на том же уровне, что и ListBox
,
3 ответа
Другой способ, кроме связывания с ElementName
, использует x:Reference, но ему также нужен целевой элемент, чтобы иметь x:Name
определено на нем. Так что это выходит за рамки здесь.
Другой подход, о котором я могу подумать, не определяя имя, - это привязать что-то вроде ниже (привязка к родительскому элементу и индексатору для получения целевого дочернего элемента).
Но это тесно связано с вашей логической древовидной структурой -
<StackPanel>
<TextBlock Text="Test"/>
<TextBlock Text="{Binding Parent.Children[0].Text,
RelativeSource={RelativeSource Mode=Self}}"/>
</StackPanel>
Кроме того, это может быть достигнуто с помощью IValueConverter. Поскольку вы упомянули, что в вашем родительском контейнере есть только один элемент такого типа, вы можете передать родительский элемент в конвертер, который будет проходить по дочернему элементу, используя класс VisualTreeHelper.
<StackPanel>
<TextBlock Text="Test"/>
<TextBlock Text="{Binding Parent, RelativeSource={RelativeSource Self},
Converter={StaticResource MyConverter}}"/>
</StackPanel>
Вот ваш код конвертера -
public class MyConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (value is DependencyObject)
{
var textBlock = FindChild<TextBlock>((DependencyObject)value, null);
return (textBlock == null)?string.Empty:textBlock.Text;
}
else
return String.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return Binding.DoNothing;
}
}
Вот метод для обхода с использованием VisualTreeHelper. Я поместил этот метод в свой класс Utility, он пригодится во многих ситуациях -
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
// Confirm parent is valid.
if (parent == null) return null;
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not
// overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null
&& frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
Вы также можете использовать RelativeSource
подключиться к другому неназванному элементу управления, если этот элемент управления является родительским для элемента управления с привязкой:
<Button Command="{Binding DataContext.ACommand, RelativeSource={RelativeSource
FindAncestor, AncestorType={x:Type Views:AView}}}" Margin="0,2,0,2">
<Image Source="{Binding AnImage}">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Width" Value="16" />
<Setter Property="Height" Value="16" />
<Setter Property="Opacity" Value="1.0" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsEnabled, RelativeSource={
RelativeSource FindAncestor, AncestorType={x:Type Button}},
FallbackValue=False}" Value="False">
<Setter Property="Opacity" Value="0.5" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Button>
Первый Binding
с RelativeSource
находит модель вида (DataContext
) с точки зрения. Второй увядает Image
когда Button.IsEnabled
свойство это правда. Кроме того, вы можете поместить эту вторую часть в стиль и использовать ее всякий раз, когда у вас есть какие-либо Image
в пределах Button.Content
собственность без необходимости объявлять какие-либо имена.
Вы можете узнать больше на странице RelativeSource MarkupExtension в MSDN.
Я полагаю, что единственным вариантом для вас будет определить пользовательское расширение разметки, которое выполняет то, что вы запрашиваете. В качестве примера вы можете определить расширение, которое находит первый элемент определенного типа тега из корневого элемента области имен. Найдите раздел расширений в ссылке ниже в качестве отправной точки.
http://msdn.microsoft.com/en-us/library/ms747254.aspx
Также для примера вы можете посмотреть в нижней части следующей ссылки.
http://www.codeproject.com/Articles/140618/WPF-Tutorial-TypeConverter-Markup-Extension
Я квалифицирую свой ответ тем, что я никогда не реализовывал его, поэтому могут быть определенные ограничения, мешающие тому, что вы пытаетесь сделать.