5

I need to find the element inside the content control :

<ContentControl Content="{Binding YourChoices}" Grid.ColumnSpan="3" x:Name="ccBloodGroup">
                <ContentControl.ContentTemplate>
                    <DataTemplate>
                        <Grid>
                            <ComboBox x:Name="cbBloodGroup" ItemsSource="{Binding}" HorizontalAlignment="Left" Margin="10,160,0,0" VerticalAlignment="Top" Width="331" Height="45">
                                <ComboBoxItem>A+</ComboBoxItem>
                                <ComboBoxItem>A-</ComboBoxItem>
                                <ComboBoxItem>B+</ComboBoxItem>
                                <ComboBoxItem>B-</ComboBoxItem>
                                <ComboBoxItem>O+</ComboBoxItem>
                                <ComboBoxItem>O-</ComboBoxItem>
                                <ComboBoxItem>AB+</ComboBoxItem>
                                <ComboBoxItem>AB-</ComboBoxItem>
                            </ComboBox>
                            <TextBlock x:Name="tb" Text=" Blood Type" IsHitTestVisible="False" Visibility="Hidden" HorizontalAlignment="Left" Margin="10,176,0,0" VerticalAlignment="Top"/>
                        </Grid>
                        <DataTemplate.Triggers>
                            <Trigger SourceName="cbBloodGroup" Property="SelectedItem" Value="{x:Null}">
                                <Setter TargetName="tb" Property="Visibility" Value="Visible"/>
                            </Trigger>
                        </DataTemplate.Triggers>
                    </DataTemplate>
                </ContentControl.ContentTemplate>
            </ContentControl>

I found an answer on Internet as

 ComboBox cb = ccBloodGroup.ContentTemplate.FindName("cbBloodGroup", ccBloodGroup) as ComboBox;

But this is giving me an run time exception saying 'This operation is valid only on elements that have this template applied.'

Please help..

  • You can only use that after `OnApplyTemplate` of the control itself is called. If you want to be able to refer `ccBloodGroup` in your code behind, you can add a loaded event for it in xaml, however it would be better to tell us exactly why you want that and what your actual goal is, because this is not the `MVVM` way. – Silvermind Nov 11 '13 at 11:26
  • Similiar to [link](http://stackoverflow.com/questions/5679648/why-would-this-contenttemplate-findname-throw-an-invalidoperationexception-on) - call this.ApplyTemplate(); before calling FindName – auburg Nov 11 '13 at 11:27
  • possible duplicate of [Xaml inside ContentControl and binding to DependencyProperty](http://stackoverflow.com/questions/19809073/xaml-inside-contentcontrol-and-binding-to-dependencyproperty) – Fritjof Berggren Nov 11 '13 at 14:34

2 Answers2

11

This method will help you:

public T FindElementByName<T>(FrameworkElement element, string sChildName) where T : FrameworkElement
    {
            T childElement = null;
            var nChildCount = VisualTreeHelper.GetChildrenCount(element);
            for (int i = 0; i < nChildCount; i++)
            {
                FrameworkElement child = VisualTreeHelper.GetChild(element, i) as FrameworkElement;

                if (child == null)
                    continue;

                if (child is T && child.Name.Equals(sChildName))
                {
                    childElement = (T)child;
                    break;
                }

                childElement = FindElementByName<T>(child, sChildName);

                if (childElement != null)
                    break;
            } 
            return childElement;
    }

And, how I use it, just add button, and on button Click:

 private void Button_OnClick(object sender, RoutedEventArgs e)
    {
        var element = FindElementByName<ComboBox>(ccBloodGroup, "cbBloodGroup");
    }
Aleksey
  • 1,299
  • 9
  • 13
2

Basically, you need to provide an element that (as the error says) has the Template applied. Your ccBloodGroup control is inside the DataTemplate and so clearly, does not have this Template applied to it.

For example, an element that might have this Template applied to it would be the ContentPresenters of the items in the YourChoices collection that are using this DataTemplate to define what they look like in the UI.

You can find out full details as usual on MSDN, with a detailed example on the FrameworkTemplate.FindName Method page, but it goes something like this... from the example on the linked page:

// Getting the currently selected ListBoxItem 
// Note that the ListBox must have 
// IsSynchronizedWithCurrentItem set to True for this to work
ListBoxItem myListBoxItem = (ListBoxItem)(myListBox.ItemContainerGenerator.
    ContainerFromItem(myListBox.Items.CurrentItem));

// Getting the ContentPresenter of myListBoxItem
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(myListBoxItem);

// Finding textBlock from the DataTemplate that is set on that ContentPresenter
DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
TextBlock myTextBlock = (TextBlock)myDataTemplate.FindName("textBlock", 
    myContentPresenter);

// Do something to the DataTemplate-generated TextBlock
MessageBox.Show("The text of the TextBlock of the selected list item: "
+ myTextBlock.Text);

The FindVisualChild method is shown on the linked page.

Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • Sorry but I dint get how those two separate controls are communicating. :( I have comboBox inside the DataTemplate & need to access it. Also If I do something like this, ComboBox myListBoxItem = (ComboBox)(cbBloodGroup.ItemContainerGenerator. ContainerFromItem(cbBloodGroup.Items.CurrentItem)); It does not recognize cbBloodGroup. –  Nov 11 '13 at 12:32
  • Please read my answer and the linked MSDN page again. You get the `ContentPresenter` *of the item that has the `DataTemplate` applied*, not any element inside the `DataTemplate`. – Sheridan Nov 11 '13 at 13:35