Что происходит с именами столбцов 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․5" Kv="0.54" />