访问扩展器控件的子级

时间:2014-10-17 10:15:17

标签: c# wpf xaml c#-4.0

我在Expander的ContentTemplate中有一个文本块。我想在我的代码隐藏文件中访问该文本块。这是我到目前为止所尝试的

<Expander x:Name="myExp" Header="Whatever ...">
            <Expander.ContentTemplate>
                <DataTemplate>
                    <TextBlock x:Name="txtWhatever"/>
                </DataTemplate>
            </Expander.ContentTemplate>
</Expander>  

我尝试找到扩展器的子元素,但是以下方法返回null,因为它没有为扩展器找到任何子节点。

ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(myExp);


private childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
        if (child != null && child is childItem)
            return (childItem)child;
        else
        {
            childItem childOfChild = FindVisualChild<childItem>(child);
            if (childOfChild != null)
                return childOfChild;
        }
    }
    return null;
} 

这样做的正确方法是什么?此外,Expander还应用了一个控件模板。

<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="{x:Type Expander}">
            <Border SnapsToDevicePixels="true" BorderThickness="1,1,1,1" Margin="0,0,0,-2"  BorderBrush="{DynamicResource DisabledBorderBrush}" >
                <DockPanel>
                    <ToggleButton x:Name="HeaderSite"   MinHeight="0" MinWidth="0" Style="{DynamicResource ToggleButtonGraphicsStyleLRUHeader}"
                                Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}" 
                                ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}" FontFamily="{TemplateBinding FontFamily}" 
                                FontSize="{TemplateBinding FontSize}" FontStretch="{TemplateBinding FontStretch}" 
                                FontStyle="{TemplateBinding FontStyle}" FontWeight="{TemplateBinding FontWeight}" 
                                Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" 
                                IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" DockPanel.Dock="Top" 
                                Height="24"/>
                    <ContentPresenter x:Name="ExpandSite"  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Focusable="false" Visibility="Collapsed" DockPanel.Dock="Bottom"/>
                </DockPanel>
            </Border>
            <ControlTemplate.Triggers>
                <Trigger Property="IsExpanded" Value="true">
                    <Setter Property="Visibility" TargetName="ExpandSite" Value="Visible"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </Setter.Value>
</Setter>

2 个答案:

答案 0 :(得分:2)

我已经尝试过你的代码了,它几乎没问题。我已经尝试过首先测试它,比如按钮,它可以正常工作。但是对于Expander,它更复杂。这里有2条通知:

  • 确保展开扩展器(IsExpanded = true)。
  • 确保更新布局(您可以明确调用UpdateLayout

所以代码应该是:

yourExpander.IsExpanded = true;
yourExpander.UpdateLayout();
//now use your method
var textBlock = FindVisualChild<TextBlock>(yourExpander);

您的代码可以缩短为:

private childItem FindVisualChild<childItem>(DependencyObject obj) 
                               where childItem : DependencyObject
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
        if(child is childItem) return (childItem)child;            
        childItem childOfChild = FindVisualChild<childItem>(child);
        if (childOfChild != null) return childOfChild;            
    }
    return null;
}

请注意,child永远不会是null。由于GetChildrenCount()已经限制了 现有 子项的范围,因此child应该存在于指定的索引i

答案 1 :(得分:0)

以下是我的一些尝试:

未能成功

msdn提到的这两种方式,在尝试之后我才得到null:

How to: Find DataTemplate-Generated Elements

How to: Find ControlTemplate-Generated Elements

下面提到的链接也只是空了

Access a control from within a DataTemplate with its identifying name

同样简单的this.FindName("name")只得到了null:

https://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.findname(v=vs.110).aspx

成功尝试

最后King King的方法有效,

在我的情况下,我想在ContentTemplate的DataTemplate中找到一个RichTextBox,所以我写了

yourExpander.IsExpanded = true;
yourExpander.UpdateLayout();
//now use your method
var richTextBox = FindVisualChild<RichTextBox>(yourExpander);

(但我发现它只在窗口初始化并显示后才有效,当用户调用鼠标点击等事件时,如果我把它放到构造函数中它会变为null,也许我会稍后再尝试一下

20170328更新:我发现在构造函数中使用FindVisualChild可能找不到孩子,但是如果你在Window_OnLoaded中使用它似乎可以找到,也许是因为在Loaded阶段控件变得具体了)< / p>

此外,我测试ControlTemplate的DataTemplate中是否有两个RichTextBox,这种方式只能得到第一个,

因此,我认为可以将FindVisualChild修改为FindRichTextBox,以便您可以找到具有特定名称的RichTextBox,在此之前您必须为您找到的控件命名:

(您可以类似于您的查找控件类型进行修改):

    public static RichTextBox FindRichTextBox(DependencyObject obj, string name)
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(obj, i);
            //                                            add the name condition
            if (child != null && child is RichTextBox && ((RichTextBox)child).Name == name)
                return (RichTextBox)child;
            else
            {
                RichTextBox childOfChild = FindRichTextBox(child, name);
                if (childOfChild != null)
                    return childOfChild;
            }
        }
        return null;
    }