Click here to Skip to main content
15,887,135 members
Please Sign up or sign in to vote.
1.33/5 (2 votes)
See more:
Hello, I am working on one assignment using WPF and MongoDB. I need to generate WPF DataGrid(any other control will be fine if it works) at runtime from List of Dictionary<string,string>. This is simple collection Dictionary where each Dictionary has different set of keypairs.

So far i am able to generate columns at runtime from Dictionary<string,string> having highest number of columns. After generating all columns, task is to put data into DataGrid and display.

List of Dictionary<string,string> contains different set of columns in each dictionary when i tried using DataContext and ItemsSource of DataGrid but it generates empty gird.

Please provide your thoughts
Posted
Updated 29-Jul-17 7:52am
v2

Hello
I would first of all choose the ObservableCollection instead of the List.
That is because that list inherits the ICollectionChanged interface so that changes in the collection will appear in the UI when binding to the collection form the ItemSource property on the datagrid.

Second, I would not use a Dictionary to hold the data. I would create a class with properties for all the "keys" or "columns", that I create the ObservableCollection of.

When all this is done, I would use the MVVM model (my personal choice) but there are many alternatives. And I would use Binding in the XAML from the ItemSource to a property in the model holding the ObservableCollection of my values. That is a quick and easy way of solving the problem, I think.

Hope it helps!
 
Share this answer
 
Well obviously
You might need to replace List<Dictionary<string,string>> to more WPF convenient collection ObservableCollection<IList<KeyValueHolder>> Items ,
where KeyValueHolder is siple entity , see:

C#
public class KeyValueHolder
{
 public string Key {get;set;}
 public string Value {get;set;}
} 
.

But one moment to consider, by it's nature ObservableCollection is not Thraed-safe!!

So , in your scenario i think , would be prafarable to implements master-details approach,(you will have 2 datagrids: - first is general, represent number of your dictionaries, and second one is detailed by list of KeyValueHolder)
 
Share this answer
 
v2
Comments
Vdovenko Andrei 24-Nov-19 6:49am    
Oleksandr Kulchytskyi, half of the day I searched over the inet of how dynamically List<string> to DataGrid. Your solution is f** cool, working and one of most clear I've saw in my life; thanks a lot, Oleksandr Kulchytskyi !!!
Thanks. This is how i resolve it.
table = new DataTable();
            dgSearchResult.Columns.Clear();
            string broker = string.Empty;
            string producttype = string.Empty;
            string currency = string.Empty;
            string productsubtype = string.Empty;

            if (cmbProductTypes.SelectedValue != null)
                producttype = cmbProductTypes.SelectedValue.ToString();
            else
                producttype = "";

            if (cmbBrokers.SelectedValue != null)
                broker = cmbBrokers.SelectedValue.ToString();
            else
                broker = "";

            if (cmbProductSubtype.SelectedValue != null)
                productsubtype = cmbProductSubtype.SelectedValue.ToString();
            else
                productsubtype = "";

            if (cmbCurrency.SelectedValue != null)
                currency = cmbCurrency.SelectedValue.ToString();
            else
                currency = "";
//Sample is a list of dict of string string
            List<dictionary><string,string>> sample = Models.RateCards.GetCard(producttype,broker,productsubtype,currency);

            foreach (Dictionary<string,> d in sample)
            {
                foreach (string key in d.Keys)
                {
                    Addcolumn(key);
                }

            }

            foreach (Dictionary<string,> d in sample)
            {
                //Addrow(d);

                row = table.NewRow();
                foreach (KeyValuePair<string,> keyValue in d)
                {
                    if (table.Columns.Contains(keyValue.Key))
                    {
                        if (keyValue.Key != "XMLDefinition")
                        {
                            if (keyValue.Value.Contains(","))
                            {
                                //if (keyValue.Key == "Volume Band Rates" || keyValue.Key == "Premium Band Rates")
                                    row[keyValue.Key] = RemoveSpecialCharacters(keyValue.Value.Replace("}, ", "}" + System.Environment.NewLine).Replace(",", ";").Replace("{", "").Replace("}", "").Replace("[", "").Replace("]", "").Replace("''", ""));
                                //else
                                //    row[keyValue.Key] = RemoveSpecialCharacters(keyValue.Value.Replace(",", ";").Replace("{", "").Replace("}", "").Replace("[", "").Replace("]", "").Replace("''", ""));
                            }
                            else
                                row[keyValue.Key] = RemoveSpecialCharacters(keyValue.Value);
                        }
                        else
                            row[keyValue.Key] = keyValue.Value;
                    }
                }

                table.Rows.Add(row);

            }
            
            dgSearchResult.ItemsSource= table.DefaultView;
            
       }

       private static string RemoveSpecialCharacters(string str) 
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in str) 
            {
                if (c != '"') 
                {
                    sb.Append(c);
                }
            }
            return sb.ToString();
        }

        private void Addcolumn(string columnname)
        {
            if (!table.Columns.Contains(columnname))
            {

                DataGridTextColumn dgColumn = new DataGridTextColumn();
                dgColumn.Header = columnname;
                dgColumn.Binding = new Binding(string.Format("[{0}]", columnname));
                dgColumn.SortMemberPath = columnname;
                dgColumn.IsReadOnly = true;
                
                dgSearchResult.Columns.Add(dgColumn);
                
                DataColumn dtcolumn = new DataColumn();
                dtcolumn.Caption = columnname;
                dtcolumn.ColumnName = columnname;
                
                table.Columns.Add(dtcolumn);
                if (columnname == "_id" || columnname == "XMLDefinition")
                    dgColumn.Visibility = Visibility.Hidden;
                    
            }
        }</dictionary>
 
Share this answer
 
v2
This is what i use to convert a dictionary-list to a datatable(credit to someone on StackOverflow) to which a datagrid can bind.
N.B if T is not a value-type then you will need to specify the property (of interest).

public static DataTable ToDataTable<T>(string tableName, Dictionary<string, IEnumerable<T>> dictionary, string property="")
     {
         Type myType = null;
         DataTable dataTable = new DataTable(tableName);
         DataRow row;
         System.Reflection.PropertyInfo myPropInfo = null;
         bool isValueType = typeof(T).IsValueType;
         if (!isValueType)
         {
             myType = typeof(T);
             myPropInfo = myType.GetProperty(property);

         }

         int length = 0;
         int i;
         List<System.Collections.IEnumerator> Enumerators = new List<System.Collections.IEnumerator>();

         var t = isValueType ? typeof(T) : myPropInfo.PropertyType;

         foreach (KeyValuePair<string, IEnumerable<T>> pair in dictionary)
         {

             var x = new DataColumn(pair.Key, t);

             dataTable.Columns.Add(x);


             Enumerators.Add(pair.Value.GetEnumerator());
             length++;

         }



         while (length > 0)
         {
             row = dataTable.NewRow();

             i = 0;
             foreach (KeyValuePair<string, IEnumerable<T>> pair in dictionary)
             {

                 if (Enumerators[i].MoveNext())
                     row[i] = isValueType ? Enumerators[i].Current : myPropInfo.GetValue(Enumerators[i].Current, null);

                 else
                     length--;

                 i++;

             }
             dataTable.Rows.Add(row);
         }

         return dataTable;
     }
 
Share this answer
 
v2
Comments
Graeme_Grant 29-Jul-17 16:01pm    
This is a 5-year-old question. Do you really think that he is still waiting for an answer? Please don't answer tombstoned questions, only current outstanding ones.
Member 12805636 9-Oct-17 5:56am    
No I don't think so; but wpf hasn't change too much in 5 years. And this question is still relevant today. So it was never meant for the person who asked the question but for anyone else who like me stumbled into this corner of the web. The problem with it is that it misses out some details/ is wrong:

This converts a dictionary to a datatable to which you can bind a datagrid. Directly binding a dictionary isn't feasible in this scenario.






This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900