WPF MVVM将DataTable绑定到DataGrid不显示数据

5
我有一个简单的控件,其中包含一个 DataGrid,其 ItemsSource 绑定到一个 DataTable。当我填充 DataTable 时,可以看到 DataGrid 中添加了行,但没有显示任何数据。我没有使用任何特殊样式来设置这个 DataGrid(使用默认样式),唯一的设置是 AutoGenerateColumn 设置为 True。
在 XAML 中:
<DataGrid AutoGenerateColumns="True" ItemsSource="{Binding TableResult}"/>

在视图模型中

private DataTable tableResult = new DataTable();
public DataTable TableResult
{
   get { return tableResult; }
   set { tableResult = value; OnPropertyChanged("TableResult"); }
}

private void FillTable()
{
   DataColumn c = new DataColumn();
   c.ColumnName = "Col1";
   this.TableResult.Columns.Add(c);

   c = new DataColumn();
   c.ColumnName = "Col2";
   this.TableResult.Columns.Add(c);

   DataRow row1 = this.TableResult.NewRow();
   row1["Col1"] = "Blue";
   row1["Col2"] = "12";;
   this.TableResult.Rows.Add(row1);

   DataRow row2 = this.TableResult.NewRow();
   row2["Col1"] = "Red";
   row2["Col2"] = "18";
   this.TableResult.Rows.Add(row2);

   DataRow row3 = this.TableResult.NewRow();
   row3["Col1"] = "Yellow";
   row3["Col2"] = "27";
   this.TableResult.Rows.Add(row3);
}

你在任何地方分配了DataContext吗? - Lei Yang
是的,它是在app.xaml中合并的资源字典中完成的。 - GuillaumeA
看一下我的回答,你必须以正确的方式设置DataContext。 - blindmeis
DataContext不是问题,每个其他控件都绑定得很好。此外,行已添加到数据网格中,但没有显示任何内容。 - GuillaumeA
6个回答

2

数据表格填充数据的工作示例:

    public MyViewModel()//constructor of MyViewModel
    {
        FillMyDataGrid();            
    }

    private DataTable employeeDataTable;

    public DataTable EmployeeDataTable
    {
        get { return employeeDataTable; }
        set
        {
            employeeDataTable = value;
            OnPropertyChanged("EmployeeDataTable");
        }
    }

    public FillMyDataGrid()
    {
            var _ds = new DataSet("Test");
            employeeDataTable = new DataTable();
            employeeDataTable = _ds.Tables.Add("DT");
            for (int i = 0; i < 50; i++)
            {
                //employeeDataTable.Columns.Add(i.ToString() + ".");
                employeeDataTable.Columns.Add(i.ToString());
            }
            for (int i = 0; i < 2; i++)
            {
                var theRow = employeeDataTable.NewRow();
                for (int j = 0; j < 50; j++)
                {                       
                        theRow[j] = "a";

                }
                employeeDataTable.Rows.Add(theRow);
            }
   }

XAML 代码:

<DataGrid ItemsSource="{Binding EmployeeDataTable}"/>

更新:

我已经测试了您的示例,并且它可以正常工作,但是您应该在构造函数中调用您的方法 FillTable()来填充 DataTable 。例如:

public class YourViewModel
{
    public YourViewModel()
    {  
        FillTable();
    }
}

0

我刚刚解决了这个问题,并找到了另一个答案,它非常可靠,包含了其他人贡献的一些但不是全部内容。 Xaml(假设数据上下文在页面级别上设置):

<DataGrid ItemsSource="{Binding Findings}" />

ViewModel 中的代码(假设已创建带有数据的 DataTable):

Findings = null;
Findings = dataTable.DefaultView

关键是首先明确将绑定属性设置为 null。我发现这在 WinForms 中也是如此。


0
你还需要定义你的列,例如:
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Source=TableResult}" >
            <DataGrid.Columns>
                <DataGridTextColumn Header="Name" Binding="{Binding col1}"/>
                <DataGridTextColumn Header="Surname" Binding="{Binding col2}"/>
                <DataGridTextColumn Header="Phone" Binding="{Binding col3}" />
            </DataGrid.Columns>
 </DataGrid>

请确保您已在视图模型中实现了INotifyPropertyChanged


不需要添加AutoGenerateColumns="False" - Sajeetharan
1
确实,不需要定义列,我也不想这样做,因为我的数据表是动态生成的。 - GuillaumeA

0

你需要调用DefaultView,在FillTable()方法中进行相应修改:

 private void FillTable()
        {
            DataColumn c = new DataColumn();
            c.ColumnName = "Col1";
            this.TableResult.Columns.Add(c);
            c = new DataColumn();
            c.ColumnName = "Col2";
            this.TableResult.Columns.Add(c);
            DataRow row1 = this.TableResult.NewRow();
            row1["Col1"] = "Blue";
            row1["Col2"] = "12"; ;
            this.TableResult.Rows.Add(row1);
            DataRow row2 = this.TableResult.NewRow();
            row2["Col1"] = "Red";
            row2["Col2"] = "18";
            this.TableResult.Rows.Add(row2);
            DataRow row3 = this.TableResult.NewRow();
            row3["Col1"] = "Yellow";
            row3["Col2"] = "27";
            this.TableResult.Rows.Add(row3);
            dataGrid1.ItemsSource = this.tableResult.DefaultView;
        }

在 MVVM 上下文中,我无法访问 GUI 元素。但是,我们可以看到与 ItemsSource 的绑定在某种程度上正在工作,因为行被添加到 DataGrid 中。 - GuillaumeA

0

你的 ItemsSource 需要是对象列表,而不是 DataRow/Column。无论如何,在 ViewModel 中引用这些类型的内容都会破坏 MVVM 设计模式!

因此:

public List<Result> TableResult {get;set;} // raise property change on set

并且

public class Result 
{
public string Color {get;set;}
public int Number {get;set;}
}

编辑:由于您事先不了解表本身的任何信息,建议您使用简单的字符串列表列表来生成行和列。

像这样:

    public List<List<string>> TableResult { get; set; } // raise property changed

并且

        List<string> columns = new List<string>() { "Col1", "Col2", "Col3", "Col4" };

        List<string> row1 = new List<string>() { "a", "b", "c", "d" };
        List<string> row2 = new List<string>() { "w", "x", "y", "z" };
        List<List<string>> table = new List<List<string>>(){columns,row1,row2};
        TableResult = table;

你需要创建一个转换器将你的行转换成 DataTable:
public class ListToTables : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var list =  value as List<List<string>>;
        var headers = list.First();
        DataTable dt = new DataTable();

        foreach (string header in headers)
        {
            dt.Columns.Add(header);
        }
        foreach (List<string> row in list.Skip(1))
        {
            int index = 0;
            DataRow r = dt.NewRow();
            foreach (string col in row)
            {
                r[index++] = col;
            }
            dt.Rows.Add(r);
        }
        return dt;

    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

在你的 XAML 中,将以下内容添加到资源中(例如 UserControl.Resources):
        <local:ListToTables x:Key="listToTables"></local:ListToTables>

其中"local"是您的转换器添加的命名空间,且:

    <DataGrid ItemsSource="{Binding TableResult,Converter={StaticResource listToTables}}" >

适用于您的数据网格。

enter image description here

最终编辑:

如果你认为MVVM允许,你可以保留你的DataTable,只需将代码的后半部分替换为以下内容:

   DataTable tempDataTable = new DataTable();
   DataColumn c = new DataColumn();
   c.ColumnName = "Col1";
   tempDataTable.Columns.Add(c);

   c = new DataColumn();
   c.ColumnName = "Col2";
   tempDataTable.Columns.Add(c);

   DataRow row1 = tempDataTable.NewRow();
   row1["Col1"] = "Blue";
   row1["Col2"] = "12";;
   tempDataTable.Rows.Add(row1);

   DataRow row2 = tempDataTable.NewRow();
   row2["Col1"] = "Red";
   row2["Col2"] = "18";
   tempDataTable.Rows.Add(row2);

   DataRow row3 = tempDataTable.NewRow();
   row3["Col1"] = "Yellow";
   row3["Col2"] = "27";
   tempDataTable.Rows.Add(row3);
   this.DataTable = tempDataTable;

你必须设置 DataTable 来引发属性更改。添加到其中不会产生任何作用 ;)

这是我在其他帖子中看到的,但如何处理动态数据?我不知道运行时列名或列数是什么... - GuillaumeA
动态数据是什么意思呢?如果您确定有多少列,可以将列名绑定到ViewModel中的变量中。其余的值可以通过这种方式处理。 - Tyress
我的意思是数据集可以有任意名称和数量的列。我无法将我的数据表示为定义好的模型,因为我事先不知道它是什么。 - GuillaumeA
更新了我的回答,@GuillaumeA - Tyress
1
如果你相信MVVM允许,你可以保留你的DataTable。事实上我并不知道,我只把DataTable看作为一个数据容器,所以我不明白为什么它的填充应该在视图中进行...但是我有何资格说呢 ;) 不过,我认为你找到了我的问题,因为我更新DataTable时未触发属性更改事件可能是解决方法,我会尝试一下。谢谢! - GuillaumeA
显示剩余2条评论

0

如果我添加以下内容,您的代码对我有效

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        FillTable();
        DataContext = this;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接