如何在没有Name或x:Name的情况下绑定到控件?

时间:2013-08-06 11:50:06

标签: c# wpf binding controls name-attribute

是的,所以我正在关注一个教程。确切地说是This one。它提供的一些代码是:

<TextBlock Margin="2" Foreground="Red" FontWeight="Bold" 
           Text="{Binding ElementName=AddressBox, 
           Path=(Validation.Errors),
           Converter={StaticResource eToMConverter}}" />

正如您所看到的,它会绑定TextBox的验证错误,并且TextBox的{​​{1}}为x:Name。现在,我的问题是:我有一个AddressBox有点像这个。它也只有一个Window。但是,如果可能的话,我宁愿不使用TextBoxName s。是否有可能绑定到另一个x:Name的{​​{1}},而不是该控件被命名,并且该控件是该ControlValidation.Errors的唯一类型ControlWindowTextBox处于同一级别。

3 个答案:

答案 0 :(得分: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;
    }

答案 1 :(得分:0)

如果该控件是带有绑定的控件的父控件,您还可以使用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>

第一个BindingRelativeSource找到视图中的视图模型(DataContext)。当Image属性为true时,第二个将淡出Button.IsEnabled。此外,您可以将第二部分放入样式中,并在Image属性中有任何Button.Content时重复使用它,而无需声明任何名称。

您可以在MSDN的RelativeSource MarkupExtension页面上找到更多信息。

答案 2 :(得分:0)

我相信你唯一的另一个选择是定义一个自定义标记扩展,它可以完成你的请求。例如,您可以定义一个扩展,从名称范围的根元素中查找某个标记类型的第一个元素。在下面的链接中查找扩展部分作为起点。

http://msdn.microsoft.com/en-us/library/ms747254.aspx

另外举一个例子,你可以看看下面链接的底部附近。

http://www.codeproject.com/Articles/140618/WPF-Tutorial-TypeConverter-Markup-Extension

我对我的回复进行了限定,说我从未实施过,因此可能存在特定的限制,这会妨碍您尝试做的事情。