Что происходит с именами столбцов DataTable с точками, что делает их непригодными для управления WPF DataGrid?

Запустите это и смутитесь:

<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;
        }
    }
}

Я расскажу вам, что произойдет. Верхний datagrid заполняется заголовками столбцов и данными. Нижний datagrid имеет заголовки столбцов, но все строки пустые.

Ответ 1

Символ полной остановки в именах столбцов второй таблицы некорректно интерпретируется парсером пути привязки. Посмотрите на вывод отладки, пока этот пример запущен, и вы можете видеть, что автогенерированные столбцы привязаны к "a" и "b", а не "a.name" и "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.

Существует несколько различных символов, которые имеют особый смысл в пути привязки, включая полную остановку ('.'), слэш ('/'), квадратные скобки ('[', ']') и круглые скобки ('( ',') '), скобка приведет к сбою вашего приложения. Эти специальные символы можно избежать, окружив путь привязки квадратными скобками. Более подробную информацию о путях и экранировании символов можно найти в Обзор объявлений привязок

Чтобы исправить это, вам нужно будет установить AutoGenerateColumns = "False" и указать привязки столбцов в 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>

или программно в коде

        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;

Ответ 2

Вы можете сохранить значение AutoGenerateColumns равным true и добавить обработчик событий для обработки любых периодов (или других специальных символов):

    <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 + "]");
    }
}

Это работало лучше для меня в сценарии MVVM.

Ответ 3

Символ FULL STOP (период/точка) не работает.
Даже экранирование с помощью x\002E не сработало.

Вот компромисс с использованием символа MIDDLE DOT:

dt1.Columns.Add("a\x00B7name", typeof(string));  
dt1.Columns.Add("b\x00B7name", typeof(string));

Ответ 4

В итоге я использовал замену ONE DOT LEADER, и он отлично работал. Я импортировал данные из XML файла и вместо использования string.replace(".","\x2024"); везде в моем коде было легче изменить файл импорта.

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