none
Access a control placed inside header template of a datagrid in WPF

    Question

  • Hi,

    I have a requirement to add filters to a datagrid. I have placed the filter controls in the headertemplate of a datagrid. How can I access these controls in code behind? I need to set itemsource for the listbox placed in header template through code behind on the basis of certain selections by user. Following is my data template(resource template) for header column.

    <DataTemplate x:Key="buttonResourceTemplate">
          <StackPanel Name="spResource" FlowDirection="LeftToRight" Orientation="Horizontal">
            <TextBlock Text="Resource Name"></TextBlock>
            <ToggleButton Name="btnResourceFilter" IsChecked="False" Height="20px" Width="30px">
              <Image Source="image\filter.ico"></Image>
            </ToggleButton>
            <Popup Name="popupResource" IsOpen="{Binding ElementName=btnResourceFilter,Path=IsChecked}" PlacementTarget="{Binding ElementName=btnResourceFilter}" PopupAnimation="Slide" Height="300px" Width="200px" Closed="popupResource_Closed">
              <StackPanel Background="White">
                <CheckBox x:Name="ChkResourceAll" IsChecked="True" Content="Select All" Foreground="Black" Height="20px" FontWeight="Bold" Checked="ChkResourceAll_Checked" Unchecked="ChkResourceAll_Unchecked" ></CheckBox>
                <ListBox Name="lstResource" ItemsSource="{Binding Path=Resource}" Initialized="lstResource_Initialized" Height="280px" >
                  <ListBox.ItemTemplate>
                    <DataTemplate>
                      <CheckBox x:Name="chkResource" 
                           IsChecked="{Binding ElementName=ChkResourceAll,Path=IsChecked,Mode=OneWay,NotifyOnTargetUpdated=True}" 
                           Content="{Binding ResourceName}" 
                           Width="100" 
                           Checked="chkResource_Checked" 
                           Unchecked="chkResource_Unchecked" 
                           TargetUpdated="chkResource_TargetUpdated" 
                           Initialized="chkResource_Initialized" />
                    </DataTemplate>
                  </ListBox.ItemTemplate>
                  <ListBox.Resources>
                    <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}">
                      SkyBlue
                    </SolidColorBrush>
                  </ListBox.Resources>
                </ListBox>
              </StackPanel>
            </Popup>
          </StackPanel>
        </DataTemplate>
    

    Following is my datagrid column with the above template.

    <my:DataGrid Grid.Row="1" AutoGenerateColumns="False" Name="dgDailyTimesheet" xmlns:my="http://schemas.microsoft.com/wpf/2008/toolkit" 
                    ItemsSource="{Binding Path=RecordsView}" AlternatingRowBackground="LightGray" FontSize="13">            
                  <my:DataGrid.Columns>
                    <my:DataGridTextColumn Header="Date Time" Binding="{Binding EntryDateTime , Converter={StaticResource DateConverter}}" HeaderTemplate="{StaticResource buttonEntryDateTimeTemplate}"></my:DataGridTextColumn>
                    <my:DataGridTextColumn Header="Status/Location" Binding="{Binding Location}" HeaderTemplate="{StaticResource buttonLocationTemplate}"></my:DataGridTextColumn>
                    <my:DataGridTextColumn x:Name="ResourceCol" Header="Resource" Binding="{Binding ResourceName}" HeaderTemplate="{StaticResource buttonResourceTemplate}"></my:DataGridTextColumn>
                    <my:DataGridTextColumn Header="SubProject" Binding="{Binding SubProject}" HeaderTemplate="{StaticResource buttonSubProjectTemplate}"></my:DataGridTextColumn>
                    <my:DataGridTextColumn Header="Billable Hours" Binding="{Binding TimeBillable}"></my:DataGridTextColumn>
                    <my:DataGridTextColumn Header="Actual Day" Binding="{Binding TimeActual}"></my:DataGridTextColumn>
                    <my:DataGridTextColumn Header="Adjusted Day" Binding="{Binding TimeAdjusted, Mode=TwoWay}" IsReadOnly="False"></my:DataGridTextColumn>
                    <my:DataGridTextColumn Header="Comments" Binding="{Binding Comments}"></my:DataGridTextColumn>                
                  </my:DataGrid.Columns>
                  
                </my:DataGrid>
    

    I want to access the listbox control lstResource through code behind.  I have tried to work around this as below.

    Project selectedProject =(Project) cboProjects.SelectedValue;
            
            DataGridColumnHeadersPresenter presenter = null;
    
            Control sv = dgDailyTimesheet.Template.FindName("DG_ScrollViewer", dgDailyTimesheet) as Control;
            if (sv != null)
            {
              presenter = sv.Template.FindName("PART_ColumnHeadersPresenter", sv) as DataGridColumnHeadersPresenter;
            }
    
            DataGridColumnHeader header=null;
            if (presenter != null)
            {
              header=(DataGridColumnHeader)presenter.ItemContainerGenerator.ContainerFromIndex(2);
            }
    
    
            if (header != null)                                      
            {
    
              ControlTemplate template = (ControlTemplate)header.Template;
              object obj= template.FindName("lstResource", header) as Object;
    
              ListBox listBox = (ListBox)header.Template.FindName("lstResource", header) as ListBox;
    
              listBox.Items.Clear();
              listBox.ItemsSource = null;
              listBox.ItemsSource = lstResource;
            }
    
    The FindName method above is returning null. I tried to find the stackpanel also but it returns null. What is wrong in above code?
    Monday, October 04, 2010 5:27 AM

Answers

  • Hi S_K_C,

    To get a control placed inside template you may search the VisualTree down along the current item which hosts the template.

    Here is a code snippet for you,

    public static T FindVisualChildByName<T>(DependencyObject parent, string name) where T : DependencyObject
    {
      for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
      {
        var child = VisualTreeHelper.GetChild(parent, i);
        string controlName = child.GetValue(Control.NameProperty) as string;
        if (controlName == name)
        {
          return child as T;
        }
        else
        {
          T result = FindVisualChildByName<T>(child, name);
          if (result != null)
            return result;
        }
      }
      return null;
    }
    

    Use it like

    ListBox listBox = FindVisualChildByName(header, "lstResource")<ListBox>;
    
    

    Hope it helps!

     

    Best regards,

    Yves


    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.
    Tuesday, October 05, 2010 11:39 AM
    Moderator

All replies

  • Monday, October 04, 2010 5:32 AM
  • Hi Rajnikant,

    I checked the link you sent. The code on the page is about how to get a cell template. My requirement is to get control placed in header template.

    Through my coding i am getting the template object. I checked its properties and its returning the correct template object. But when i try to find out a control placed in template, I am not able to find it.

    Monday, October 04, 2010 6:25 AM
  • Hi Rajnikant,

    In the link you have sent, the code explains how to get template for listviewitems. I think you have misunderstood my query. I want to get a control placed inside datatemplate which is applied to a headercolumn in datagrid control. I am getting the headertemplate but when i try to find control using FindName its returning null.

    Thanks

     

    Monday, October 04, 2010 7:57 AM
  • Hi S_K_C,

    To get a control placed inside template you may search the VisualTree down along the current item which hosts the template.

    Here is a code snippet for you,

    public static T FindVisualChildByName<T>(DependencyObject parent, string name) where T : DependencyObject
    {
      for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
      {
        var child = VisualTreeHelper.GetChild(parent, i);
        string controlName = child.GetValue(Control.NameProperty) as string;
        if (controlName == name)
        {
          return child as T;
        }
        else
        {
          T result = FindVisualChildByName<T>(child, name);
          if (result != null)
            return result;
        }
      }
      return null;
    }
    

    Use it like

    ListBox listBox = FindVisualChildByName(header, "lstResource")<ListBox>;
    
    

    Hope it helps!

     

    Best regards,

    Yves


    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.
    Tuesday, October 05, 2010 11:39 AM
    Moderator
  • HI I am facing a problem with this functionality


    I want to access the button which is inside the WPF datagrid header. We tried some methods mentioned by you. But no effect..

    The XAML part of the datagrid is shown below

    <my:DataGridTextColumn Binding="{Binding Path=drugName}" Width="400"  Header="Medication">
                                    <my:DataGridTextColumn.HeaderTemplate>
                                        <DataTemplate>
                                            <StackPanel>
                                                <TextBlock Text="Medication"></TextBlock>
                                                <Button Name="btnPopUpMedication" Height="22" Width="Auto" Content="Add Medication"></Button>
                                            </StackPanel>
                                        </DataTemplate>
                                    </my:DataGridTextColumn.HeaderTemplate>
                                </my:DataGridTextColumn>

     

     

    Here i want to access the button named 'btnPopUpMedication' which is inside the datagrid header. Please clarify this thing with proper solution..Waiting for that

     

    • Proposed as answer by zaytuhin Thursday, August 02, 2012 8:22 AM
    Friday, December 17, 2010 2:43 PM
  • <my:DataGrid AutoGenerateColumns="False"  CellEditEnding="dgvProductInfo_CellEditEnding" ItemsSource="{Binding Eproduct, Mode=TwoWay}" Margin="19,3,6,35" SelectionChanged="dgvProductInfo_SelectionChanged" Name="dgvProductInfo" >
                        <my:DataGrid.Columns>
                            <my:DataGridTextColumn Header="Product" Width="150" IsReadOnly="True" Binding="{Binding productName}"/>
                            <my:DataGridTextColumn Header="Unit" IsReadOnly="True" Width="60" Binding="{Binding unit}"/>
                            <my:DataGridTextColumn Header="sales Qty" IsReadOnly="True"  Width="62" Binding="{Binding quantity,NotifyOnValidationError=True, ValidatesOnExceptions=True }"/>
                            <my:DataGridTemplateColumn Header="Bin" Width="*">
                                <my:DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <ComboBox Margin="2" Name="CmbDGV" ItemsSource="{Binding BinInfo }" DisplayMemberPath="BinCode" SelectedItem="Binding dgvProductInfo,Path=SelectedItem.BinCode,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay" SelectedValuePath="BinCode"  SelectionChanged="CmbDGV_SelectionChanged"/>
                                    </DataTemplate>
                                </my:DataGridTemplateColumn.CellTemplate>
                            </my:DataGridTemplateColumn>

                        </my:DataGrid.Columns>
                    </my:DataGrid>

    Inside a datagrid template colum i used a combobox control and access that using below code:

    private void CmbDGV_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                
                ComboBox s = (ComboBox)e.Source;
                EbinInfo ss = (EbinInfo)s.SelectedItem;
            }

    You try on this  way

                            
    Thursday, August 02, 2012 8:25 AM