8

I have a datatemplate which contains a grid and inside the grid I have a combobox.

<DataTemplate x:Key="ShowAsExpanded">
        <Grid>                
            <ComboBox Name ="myCombo" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="5"
                      IsSynchronizedWithCurrentItem="True"
                      ItemsSource="{Binding}"
                      ItemTemplate="{StaticResource MyItems}">
                <ComboBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <VirtualizingStackPanel />
                    </ItemsPanelTemplate>
                </ComboBox.ItemsPanel>
            </ComboBox>

        </Grid>
    </DataTemplate>

I then I have a grid that refers to that template through styling.

<Grid>
    <ContentPresenter Name="_contentPresenter" Style="{DynamicResource StyleWithCollapse}" Content="{Binding}" />
</Grid>

How can I access through code behing the myCombo to basically set its DataContext?

pdiddy
  • 6,217
  • 10
  • 50
  • 111

3 Answers3

31

Three ways which I know of.

1.Use FindName

ComboBox myCombo =
    _contentPresenter.ContentTemplate.FindName("myCombo",
                                               _contentPresenter) as ComboBox;

2.Add the Loaded event to the ComboBox and access it from there

<ComboBox Name ="myCombo" Loaded="myCombo_Loaded" ...

private void myCombo_Loaded(object sender, RoutedEventArgs e)
{
    ComboBox myCombo = sender as ComboBox; 
    // Do things..
}

3.Find it in the Visual Tree

private void SomeMethod()
{
    ComboBox myCombo = GetVisualChild<ComboBox>(_contentPresenter);
}
private T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
    T child = default(T);
    int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
    for (int i = 0; i < numVisuals; i++)
    {
        Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
        child = v as T;
        if (child == null)
        {
            child = GetVisualChild<T>(v);
        }
        if (child != null)
        {
            break;
        }
    }
    return child;
}
Fredrik Hedblad
  • 83,499
  • 23
  • 264
  • 266
  • Thanks, very helpful, but Geert van Horrik answer got me thinking that accessing through code behind is not the correct way ... – pdiddy Jan 03 '11 at 16:44
6

First of all, I can't even find the relation between the Resource (ShowAsExpanded) and the usage inside the ContentPresenter. But for the moment, let's assume that the DynamicResource should point to ShowAsExpanded.

You can't and shouldn't access the combobox via code. You should bind the datacontext to the grid that uses the style. If you don't want to do that, you will have to find the content at runtime and search for the child combobox.

Geert van Horrik
  • 5,689
  • 1
  • 18
  • 32
  • Interesing. I'm fairly new to wpf. Setting the datacontext on the grid, how does this propagate to the combobox in the template? Also, what if I have 2 combobox in my template, how are the datacontext of each combo suppose to be bound if each has a different datacontext? – pdiddy Jan 03 '11 at 16:32
  • 2
    A datacontext is propagated to childs, as long as you don't explicitly set the datacontext of that child. So, if you set a datacontext on the Grid, the ContentPresenter (and all the controls below) will share that datacontext and can bind to it. – Geert van Horrik Jan 03 '11 at 18:51
  • What if I have 2 combobox in my template which needs to be bound through 2 different collections, how would this be done? – pdiddy Jan 03 '11 at 19:00
  • You create a data object that contains both the collections, and then set the datacontext to an instance of that object. Then, you can bind both comboboxes to the right collection. – Geert van Horrik Jan 05 '11 at 07:44
  • You answer makes sense, and binding the `ContentPresenter` to different collections works fine. But what if my data template contains a `ListView` for which I sometimes want to add `GroupDescriptions`? AFAIK this can only be done in code-behind. – Good Night Nerd Pride Oct 18 '20 at 11:49
  • The problem I described above can be solved by [defining a `CollectionViewSource`](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/how-to-group-items-in-a-listview-that-implements-a-gridview) in XAML and binding the `ContentPresenter` that should show a grouped `ListView` to it. Even though the `ListView` in the `DataTemplate` now always defines a `GroupStyle`, the groupings will only be shown for `ListViews` bound to the `CollectionViewSource`. – Good Night Nerd Pride Oct 18 '20 at 12:49
1

you need to use FindName. check out http://msdn.microsoft.com/en-us/library/system.windows.frameworktemplate.findname.aspx

Robert Levy
  • 28,747
  • 6
  • 62
  • 94