简体   繁体   English

如何将Dapper查询结果绑定到WPF DataGrid

[英]How do you bind Dapper query result to WPF DataGrid

Here's my code. 这是我的代码。 It produces a bound grid with the correct number of rows, however the cells are empty . 它生成具有正确行数的绑定网格,但单元格为空

XAML XAML

<DataGrid
  Name="grid" 
  ItemsSource="{Binding}"
  AutoGenerateColumns="True" />

Code behind 代码背后

grid.DataContext = cn.Query("select * from SomeTable");

From the docs , your syntax there -- cn.Query("sql") -- returns a list of dynamic-typed objects ( IEnumerable<dynamic> ). 文档中 ,你的语法cn.Query("sql") - 返回一个动态类型对象列表( IEnumerable<dynamic> )。 That won't work for the DataGrid auto-columns, which looks for concrete members to generate its columns. 这对于DataGrid自动列不起作用,后者查找具体成员生成其列。 I'd suggest creating a simple entity class for SomeTable to map the properties and then using cn.Query<SomeTableEntity>("select * from SomeTable"); 我建议为SomeTable创建一个简单的实体类来映射属性,然后使用cn.Query<SomeTableEntity>("select * from SomeTable"); .

So I guess the answer is: it's not possible. 所以我猜答案是:这是不可能的。 Here's a hacky workaround. 这是一个hacky解决方法。

        ...
        var items = cn.Query("select * from SomeTable");
        grid.DataContext = ConvertToDataTable(items);
    }

    public DataTable ConvertToDataTable(IEnumerable<dynamic> items) {
        var t = new DataTable();
        var first = (IDictionary<string, object>)items.First();
        foreach (var k in first.Keys)
        {
            var c = t.Columns.Add(k);
            var val = first[k];
            if (val != null) c.DataType = val.GetType();
        }

        foreach (var item in items)
        {
            var r = t.NewRow();
            var i = (IDictionary<string, object>)item;
            foreach (var k in i.Keys)
            {
                var val = i[k];
                if (val == null) val = DBNull.Value;
                r[k] = val;
            }
            t.Rows.Add(r);
        }
        return t;
    }

If you use the non-generic version of Query, it returns a dynamic representation of the data. 如果使用非泛型版本的Query,则返回数据的动态表示。 The dynamic API is not suitable for most UI data-bindings. 动态API不适合大多数UI数据绑定。 It would be preferable to use the generic Query<T> API to load the data into types that have defined properties. 最好使用通用Query<T> API将数据加载到具有已定义属性的类型中。

At a complete push , it would also theoretically be possible to implement ITypedList on the data and expose the properties accordingly. 完全推动 ,理论上也可以在数据上实现ITypedList并相应地公开属性。 But that is quite a lot of work for not so much gain. 但这并没有太大的收获。

It is not possible with pure Linq ! 纯粹的Linq是不可能的! It is possible reusing OLD dataset and convert back to linq using System.Data.DataSetExtensions. 可以重用OLD数据集并使用System.Data.DataSetExtensions转换回linq。 (Not Elegant but it works) (不优雅,但它的工作原理)

// Steal concrete connection from Linq
ModelDEmoWPFContainer l_ctx = new ModelDEmoWPFContainer();
var l_connection = (System.Data.EntityClient.EntityConnection)l_ctx.Connection)
                                   .StoreConnection;

System.Data.SqlClient.SqlCommand l_cmd = 
                  new System.Data.SqlClient.SqlCommand(query_arg);
l_cmd.Connection = (System.Data.SqlClient.SqlConnection) l_connection;
System.Data.SqlClient.SqlDataAdapter l_da = 
                 new    System.Data.SqlClient.SqlDataAdapter(l_cmd);
System.Data.DataSet l_ds = new System.Data.DataSet();     
l_da.Fill(l_ds);

CLONE metadata CLONE元数据

System.Data.DataTable l_dt = l_ds.Tables[0].Clone();    

back to linq via System.Data.DataSetExtensions 通过System.Data.DataSetExtensions返回linq

var dt = (from data in l_ds.Tables[0].AsEnumerable()
                  select data).ToList();

   foreach (DataColumn column in l_dt.Columns)
   {
      var binding = new Binding(string.Format("[{0}]", column.Ordinal));
      datagrid.Columns.Add(new DataGridTextColumn() 
                     { Header = column.ColumnName, Binding = binding });
   }

        datagrid.ItemsSource = dt;

If you are using MVVM pattern, I think this is better solution: 如果您使用的是MVVM模式,我认为这是更好的解决方案:

Create ObservableCollecion in your ViewModel, which will be binded to ItemSource in DataGrid : 在ViewModel中创建ObservableCollecion,它将绑定到DataGrid ItemSource

public ObservableCollection<T> bindableCollection;

Then, fetch data from your database, example: 然后,从数据库中获取数据,例如:

public void RefreshDataGrid(){
    using (var conn = new SqlConnection(ConfigurationManager.ConnectionStrings[
        "RecipeManager.Properties.Settings.RecipeManagerConnectionString"].ConnectionString)){
       var fetchedData = conn.Query<Flavour>("select * from [Flavour]");
        ConvertToDataTable(fetchedData);

    }
}

Last step, and the most important, create function, which will add IEnumerable to your ObservableCollection: 最后一步,也是最重要的创建函数,它将IEnumerable添加到你的ObservableCollection中:

private void ConvertToObservableCollection(IEnumerable<Flavour> items){
    ObservableCollection<Flavour> flavours = new ObservableCollection<Flavour>();
    foreach (var item in items){
        Flavour flavour = item as Flavour;
        flavours.Add(new Flavour(flavour.Name,flavour.Company,flavour.Shop,flavour.Amount));
    }
    Flavours = flavours;
}

I think, that this is good approach for mvvm. 我认为,这是mvvm的好方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM