2

I have a textblock inside the ContentTemplate of an Expander. I want to access that textblock in my code behind file. This is what I have tried so far

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

I try to find the child elements of the expander but the following method returns null as it does not find any children for the expander.

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;
} 

What is the correct way to do this.? Also, the Expander has a control template applied to it.

<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>
daniele3004
  • 13,072
  • 12
  • 67
  • 75
pankaj
  • 185
  • 1
  • 2
  • 10

2 Answers2

2

I've tried your code, it's almost fine. I've tried testing it on such as a Button first, it works OK. However for the Expander, it's more complex. There are 2 notices here:

  • Be sure the Expander is expanded (IsExpanded = true).
  • Be sure the layout is updated (you can call UpdateLayout explicitly)

So the code should be:

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

Your code can be shorten more like this:

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;
}

Note that child will never be null. Because the GetChildrenCount() already limits the range of existing children, so the child should exist at the specified index i.

King King
  • 61,710
  • 16
  • 105
  • 130
  • I tried using your code, but i still get null when i try to access the textblock. Also, the textblock is inside the ContentTemplate of the expander so i think trying to access it directly like this var textBlock = FindVisualChild(yourExpander); is not right i think. – pankaj Oct 17 '14 at 13:44
  • @user3668639 I tried a simple demo and yes my Expander also has the `ContentTemplate` inside which the TextBlock is (I copied exactly your XAML code). In your question you ***confirmed*** that there was not any child, but in fact ***there are many***. You can try using `System.Diagnostics.Debug.Print` to print the `child.GetType().ToString()` in each for loop and see the result in the `Output` window. That means there is something wrong on your side, at least related to ***how*** you test it. – King King Oct 17 '14 at 13:58
  • @user3668639 you said your Expander also has a `ControlTemplate`? So when running the code, ***is your `TextBlock` shown***? if it's not shown, that's the problem. – King King Oct 17 '14 at 14:01
  • In case your `TextBlock` is not shown, that means your `ControlTemplate` is wrong, so please post the code of your `ControlTemplate` to see if I can help. – King King Oct 17 '14 at 19:20
  • I have added the control template that I am using..also the textblock is shown when i run my application. – pankaj Oct 20 '14 at 05:32
  • @user3668639 I've just included the `ControlTemplate`, but it is still working on my side, if you want, maybe I'll send you this simple demo. I hope your tried the code when the window has already been loaded. – King King Oct 20 '14 at 05:42
0

Here is some of my try :

FAILED TRY

These two ways which msdn mentioned, after trying I just got null :

How to: Find DataTemplate-Generated Elements

How to: Find ControlTemplate-Generated Elements

.

And the way link below mention also just got null

Access a control from within a DataTemplate with its identifying name

.

Also simply this.FindName("name") just got null:

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

.

SUCCESS TRY

Finally King King's method works,

In my case I want to find a RichTextBox in the DataTemplate of ContentTemplate, so I write

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

(but I find it works only after the window is initialized and show and when user invoke the event such as mouse click, if I put it into constructor it get null, maybe I'll give it some other try later

20170328updated: I find use FindVisualChild in the constructor may not find the child, but if you use in the Window_OnLoaded it seems can find, maybe it's because that in the stage of Loaded the controls are become concrete)

Besides, I test if there're two RichTextBoxes in the DataTemplate of ControlTemplate, this way can only get the first one,

So, I think a way to modify FindVisualChild to FindRichTextBox so that you can find RichTextBox with specific Name, and before that you have to give name to the control you're finding:

(you can modify similarly to your finding control type):

    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;
    }
Community
  • 1
  • 1
yu yang Jian
  • 6,680
  • 7
  • 55
  • 80