How to access datagrid template column textbox text WPF C#

19,252

To find a control in a DataGrid template column, you should use FindChild():

    public static T FindChild<T>(DependencyObject parent, string childName) where T : DependencyObject
    {
        if (parent == null)
        {
            return null;
        }

        T foundChild = null;

        int childrenCount = VisualTreeHelper.GetChildrenCount(parent);

        for (int i = 0; i < childrenCount; i++)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
            T childType = child as T;

            if (childType == null)
            {
                foundChild = FindChild<T>(child, childName);

                if (foundChild != null) break;
            }
            else
                if (!string.IsNullOrEmpty(childName))
                {
                    var frameworkElement = child as FrameworkElement;

                    if (frameworkElement != null && frameworkElement.Name == childName)
                    {
                        foundChild = (T)child;
                        break;
                    }
                    else
                    {
                        foundChild = FindChild<T>(child, childName);

                        if (foundChild != null)
                        {
                            break;
                        }
                    }
                }
                else
                {
                    foundChild = (T)child;
                    break;
                }
        }

        return foundChild;
    }

For example I have this template column in MyDataGrid:

<DataGridTemplateColumn Width="1.5*" IsReadOnly="False">
    <DataGridTemplateColumn.Header>
        <TextBlock Text="Sample" ToolTip="{Binding Path=Text, RelativeSource={RelativeSource Self}}" FontSize="14" />
     </DataGridTemplateColumn.Header>

     <DataGridTemplateColumn.CellTemplate>
         <DataTemplate>
             <TextBlock x:Name="MyTextBlock" Text="Hello!" />
         </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Access it from the code, you can:

TextBlock MyTextBlock = FindChild<TextBlock>(MyDataGrid, "MyTextBlock");

MessageBox.Show(MyTextBlock.Text);

Note: Always use FindChild only when the control will be fully loaded, otherwise it will not find it and give null. In this case, I put this code in the event ContentRendered (Window) which says that all the contents of the window successfully load (even the event MyDataGrid_Loaded not have access to MyTextBlock, because it is not yet loaded):

    private void Window_ContentRendered(object sender, EventArgs e)
    {
        TextBlock MyTextBlock = FindChild<TextBlock>(MyDataGrid, "MyTextBlock");

        MessageBox.Show(MyTextBlock.Text);
    }

EDIT1:

To access the control of selected row to add event SelectionChanged to DataGrid in which to function, which will give a selected row:

    private void MyDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        try
        {
            var row_list = GetDataGridRows(MyDataGrid);

            foreach (DataGridRow single_row in row_list)
            {
                if (single_row.IsSelected == true)
                {
                    TextBlock MyTextBlock = FindChild<TextBlock>(single_row, "MyTextBlock");

                    MessageBox.Show(MyTextBlock.Text);
                }
            }
        }

        catch 
        {
            throw new Exception("Can't get access to DataGridRow");
        }
    }

Listing of GetDataGridRows():

    public IEnumerable<DataGridRow> GetDataGridRows(DataGrid grid)
    {
        var itemsSource = grid.ItemsSource as IEnumerable;

        if (null == itemsSource)
        {
            yield return null; 
        }

        foreach (var item in itemsSource)
        {
            var row = grid.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;

            if (null != row)
            {
                yield return row; 
            }
        }
    }

EDIT2:

To get ALL the items I rewrote the function FindChild():

    public static void FindChildGroup<T>(DependencyObject parent, string childName, ref List<T> list) where T : DependencyObject
    {
        // Checks should be made, but preferably one time before calling.
        // And here it is assumed that the programmer has taken into
        // account all of these conditions and checks are not needed.
        //if ((parent == null) || (childName == null) || (<Type T is not inheritable from FrameworkElement>))
        //{
        //    return;
        //}

        int childrenCount = VisualTreeHelper.GetChildrenCount(parent);

        for (int i = 0; i < childrenCount; i++)
        {
            // Get the child
            var child = VisualTreeHelper.GetChild(parent, i);

            // Compare on conformity the type
            T child_Test = child as T;

            // Not compare - go next
            if (child_Test == null)
            {
                // Go the deep
                FindChildGroup<T>(child, childName, ref list);
            }
            else
            {
                // If match, then check the name of the item
                FrameworkElement child_Element = child_Test as FrameworkElement;

                if (child_Element.Name == childName)
                {
                    // Found
                    list.Add(child_Test);
                }

                // We are looking for further, perhaps there are
                // children with the same name
                FindChildGroup<T>(child, childName, ref list);
            }
        }

        return;
    }

Calling this new function:

   private void Window_ContentRendered(object sender, EventArgs e)
   {
        // Create the List
        List<TextBlock> list = new List<TextBlock>();

        // Find all elements
        FindChildGroup<TextBlock>(MyDataGrid, "MyTextBlock", ref list);
        string text = "";

        // Print
        foreach (TextBlock elem in list)
        {
            text += elem.Text + "\n";
        }

        MessageBox.Show(text, "Text in TextBlock");
   }

Generally, this practice is not the best ... to get the items (such as all or selected), you can contact directly to the list which stores your data (like ObservableCollection). Also, it is useful events such as PropertyChanged.

Share:
19,252
acosta
Author by

acosta

Updated on June 28, 2022

Comments

  • acosta
    acosta almost 2 years

    I need to access the text in a DataGrid's template column from code behind, but I don't know how. I need to change the text to whatever string I pass to it on the SelectionChanged event. Can someone please tell me how to achieve this? I found a similar question here but it had no answers.

  • acosta
    acosta almost 11 years
    I am facing the same problem the person in the thread I linked to. I can't find a way for this to work on every Row. Only one. How to make it work on selected row?
  • acosta
    acosta almost 11 years
    Hmm.. the code for getting all of them works, but the one from the first edit just throws the exception that it can't access the DataGridRow
  • Anatoliy Nikolaev
    Anatoliy Nikolaev almost 11 years
    I have this exception is triggered when the wrong name of the control, for example: TextBlock MyTextBlock = FindChild<TextBlock>(single_row, "WrongNameOfTextBlock"). And when passed the wrong DataGrid, example NullDataGrid: DataGrid NullDataGrid = null; var row_list = GetDataGridRows(NullDataGrid). Check these cases.
  • nam
    nam over 3 years
    @AnatoliyNikolaev But if you are using Page instead of Window? Page does not seem to have ContentRendered event.