Как я могу привязать к элементу управления без Имени или 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

Я квалифицирую свой ответ тем, что я никогда не реализовывал его, поэтому могут быть определенные ограничения, мешающие тому, что вы пытаетесь сделать.

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