15

Run this, and be confused:

<Window x:Class="Data_Grids.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <DataGrid
        Name="r1"
              ItemsSource="{Binding Path=.}">
        </DataGrid>
        <DataGrid
        Name="r2"
              ItemsSource="{Binding Path=.}">
        </DataGrid>
    </StackPanel>
</Window>

Codebehind:

using System.Data;
using System.Windows;

namespace Data_Grids
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            DataTable dt1, dt2;
            dt1 = new DataTable();
            dt2 = new DataTable();
            dt1.Columns.Add("a-name", typeof(string));
            dt1.Columns.Add("b-name", typeof(string));
            dt1.Rows.Add(new object[] { 1, "Hi" });
            dt1.Rows.Add(new object[] { 2, "Hi" });
            dt1.Rows.Add(new object[] { 3, "Hi" });
            dt1.Rows.Add(new object[] { 4, "Hi" });
            dt1.Rows.Add(new object[] { 5, "Hi" });
            dt1.Rows.Add(new object[] { 6, "Hi" });
            dt1.Rows.Add(new object[] { 7, "Hi" });
            dt2.Columns.Add("a.name", typeof(string));
            dt2.Columns.Add("b.name", typeof(string));
            dt2.Rows.Add(new object[] { 1, "Hi" });
            dt2.Rows.Add(new object[] { 2, "Hi" });
            dt2.Rows.Add(new object[] { 3, "Hi" });
            dt2.Rows.Add(new object[] { 4, "Hi" });
            dt2.Rows.Add(new object[] { 5, "Hi" });
            dt2.Rows.Add(new object[] { 6, "Hi" });
            dt2.Rows.Add(new object[] { 7, "Hi" });
            r1.DataContext = dt1;
            r2.DataContext = dt2;
        }
    }
}

I'll tell you what happens. The top datagrid is populated with column headers and data. The bottom datagrid has column headers but all the rows are blank.

Tom Ritter
  • 99,986
  • 30
  • 138
  • 174

4 Answers4

19

You can keep AutoGenerateColumns set to true and add an eventhandler to deal with any periods (or other special characters):

    <DataGrid
    Name="r2"
          ItemsSource="{Binding Path=.}"
          AutoGeneratingColumn="r2_AutoGeneratingColumn">
    </DataGrid>

Codebehind:

private void r2_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
    if (e.PropertyName.Contains('.') && e.Column is DataGridBoundColumn)
    {
        DataGridBoundColumn dataGridBoundColumn = e.Column as DataGridBoundColumn;
        dataGridBoundColumn.Binding = new Binding("[" + e.PropertyName + "]");
    }
}

This worked better for me in a MVVM scenario.

TRX
  • 189
  • 1
  • 3
  • I had to used same solution for my datagrid in wpf but what I am dealing with is the problem when cell vallue has been updated (editing mode) and I press Enter (commit value), app crashes with general exc. "Object reference not set to an instance of an object", stack trace: " at System.Windows.Data.BindingGroup.UpdateValues() at System.Windows.Data.BindingGroup.UpdateAndValidate(ValidationStep validationStep) at System.Windows.Data.BindingGroup.CommitEdit()". Have u had similar problem? – st35ly Nov 12 '14 at 21:41
  • and just one comment to my previous one, I did change of binding path from "_-./#" to "[_-^.^/#]" - these special characters should be allowed in column header. Thanks for help – st35ly Nov 12 '14 at 21:55
  • 2
    As I found [here](http://stackoverflow.com/questions/8010614/datagrid-header-sort-error-bound-to-dataview), to not screw up sorting also add: dataGridBoundColumn.SortMemberPath = e.PropertyName; – anakic Dec 03 '15 at 17:55
17

The full stop character in the column names of the second table are incorrectly interpreted by the binding path parser. Look at the debug output while this example is running and you can see that the auto-generated columns have been bound to 'a' and 'b' rather than 'a.name' and 'b.name'

System.Windows.Data Error: 40 : BindingExpression path error: 'a' property not found on 'object' ''DataRowView' ... etc.
System.Windows.Data Error: 40 : BindingExpression path error: 'b' property not found on 'object' ''DataRowView' ... etc.

There are a number of different characters which have special meaning in a binding path including full stop ('.'), slash ('/'), square brackets ('[',']') and parenthesis ('(',')'), the parenthesis will cause your app to crash. These special characters can be escaped by surrounding the binding path with square brackets. More information about paths and character escaping can be found in the Binding Declarations Overview

To fix this you will have to set AutoGenerateColumns="False" and specify the column bindings in the xaml:

    <DataGrid
    x:Name="r2"
          ItemsSource="{Binding .}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="a.name" Binding="{Binding Path=[a.name]}" />
            <DataGridTextColumn Header="b.name" Binding="{Binding Path=[b.name]}" />
        </DataGrid.Columns>
    </DataGrid>

or programmatically in the code behind

        r2.AutoGenerateColumns = false;
        foreach( DataColumn column in dt2.Columns )
        {
            var gridColumn = new DataGridTextColumn()
            {
                Header = column.ColumnName,
                Binding = new Binding( "[" + column.ColumnName + "]" )
            };

            r2.Columns.Add( gridColumn );
        }

        r2.DataContext = dt2;
bstoney
  • 6,594
  • 5
  • 44
  • 51
  • 1
    But what if there's a bracket in the column name? Won't that mess up this approach? (I'm aware this is very old but I have this problem right now:)) – anakic Dec 03 '15 at 18:00
4

The FULL STOP (period/dot) character does not work.
Even escaping with x\002E did not work.

Here is a compromise using the MIDDLE DOT character:

dt1.Columns.Add("a\x00B7name", typeof(string));  
dt1.Columns.Add("b\x00B7name", typeof(string));
Zamboni
  • 7,897
  • 5
  • 43
  • 52
1

I ended up using the substituting the ONE DOT LEADER and it worked great. I imported my data from an XML file and instead of using string.replace(".","\x2024"); everywhere in my code it was easier to change the import file.

Before
<Components Description="Thru Tee" Size="0.5" Kv="0.54" />
After
<Components Description="Thru Tee" Size="0&#x2024;5" Kv="0.54" />
Icy
  • 21
  • 2