wpfwpfdatagriddatagridcolumn

cannot find Name of DataGridColumn programmatically


I found the Columns collection in my datagrid, and was hoping to iterate through it to find a certain column Name. However, I can't figure out how to address the x:Name attribute of the column. This xaml illustrates my problem with a DataGridTextColumn and a DataGridTemplateColumn:

<t:DataGrid x:Name="dgEmployees" ItemsSource="{Binding Employees}" 
    AutoGenerateColumns="false" Height="300" >
    <t:DataGrid.Columns>
        <t:DataGridTextColumn x:Name="FirstName" Header="FirstName"
Binding="{Binding FirstName}" />
        <t:DataGridTemplateColumn x:Name="LastName" Header="LastName" >
            <t:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding LastName}" />
                </DataTemplate>
            </t:DataGridTemplateColumn.CellTemplate>
        </t:DataGridTemplateColumn>
    </t:DataGrid.Columns>
</t:DataGrid>

And here is my code:

    DataGrid dg = this.dgEmployees;
    foreach (var column in dg.Columns) 
    {
        System.Console.WriteLine("name: " + (string)column.GetValue(NameProperty));
    }

At runtime, no value is present; column.GetValue doesn't return anything. Using Snoop, I confirmed that there is no Name property on either DataGridTextColumn or DataGridTemplateColumn.

What am I missing?


Solution

  • WPF has two different, yet similar concepts, x:Name, which is used to create a field which references an element defined in XAML, i.e. connecting your code-behind to your XAML, and FrameworkElement.Name, which uniquely names an element within a namescope.

    If an element has a FrameworkElement.Name property, x:Name will set this property to the value given in XAML. However, there are instances where it is useful to link non FrameworkElement elements to fields in code-behind, such as in your example.

    See this related question:

    In WPF, what are the differences between the x:Name and Name attributes?

    As an alternative, you could define your own attached property which can be used to name the columns. The attached property is defined as follows:

    public class DataGridUtil
    {
    
        public static string GetName(DependencyObject obj)
        {
            return (string)obj.GetValue(NameProperty);
        }
    
        public static void SetName(DependencyObject obj, string value)
        {
            obj.SetValue(NameProperty, value);
        }
    
        public static readonly DependencyProperty NameProperty =
            DependencyProperty.RegisterAttached("Name", typeof(string), typeof(DataGridUtil), new UIPropertyMetadata(""));
    
    }
    

    You can then assign a name to each column ...

    xmlns:util="clr-namespace:WPFDataGridExamples"
    
    <t:DataGrid x:Name="dgEmployees" ItemsSource="{Binding Employees}" 
        AutoGenerateColumns="false" Height="300" >
        <t:DataGrid.Columns>
            <t:DataGridTextColumn util:DataGridUtil.Name="FirstName" Header="FirstName"
    Binding="{Binding FirstName}" />
            <t:DataGridTemplateColumn util:DataGridUtil.Name="LastName" Header="LastName" >
                <t:DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding LastName}" />
                    </DataTemplate>
                </t:DataGridTemplateColumn.CellTemplate>
            </t:DataGridTemplateColumn>
        </t:DataGrid.Columns>
    </t:DataGrid>
    

    Then access this name in code as follows:

    DataGrid dg = this.dgEmployees;
    foreach (var column in dg.Columns) 
    {
        System.Console.WriteLine("name: " + DataGridUtil.GetName(column));
    }
    

    Hope that helps