0
votes

Is there a clean, easy way to get a Dictionary<obj,List<obj2>> into a GridView Datasource in ASP.NET? If I just throw the Dictionary as-is into the GridView and am doing an Eval("Key") and Eval("Value"), I'll end up with the object name in the Eval("Value") column. I'm looking for a solution that provides a comma separated value in the Eval("Value") field.

GridView:

<SharePoint:SPGridView
    id="GvItems"
    runat="server"
    AutoGenerateColumns="false"
    width="100%"
    AllowSorting="True" OnRowDataBound="GvItems_OnRowDataBound" >
<AlternatingRowStyle CssClass="ms-alternatingstrong" />
<Columns>
    <asp:TemplateField ItemStyle-CssClass="ms-cbp" HeaderStyle-CssClass="ms-cbp" ItemStyle-VerticalAlign="Top">
        <HeaderTemplate>
            <asp:CheckBox ID="chkBoxSL" 
                runat="server"
                AutoPostBack="true"
                OnCheckedChanged="chkBoxSL_CheckedChanged"
                style="margin-top:-1px; margin-bottom:-1px;" />
        </HeaderTemplate>
        <ItemTemplate>
            <asp:CheckBox ID="chkId" 
                runat="server" 
                class="padding-right: 15px;padding-top: 0px;padding-bottom: 0px" />
        </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="Web Application Zone" HeaderStyle-Width="50%" HeaderStyle-CssClass="ms-vh2-nofilter-perm" SortExpression="Key">
        <ItemTemplate>
            <asp:Label ID="lblZone" runat="server" Text='<%# Eval("Key") %>' />
        </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="Crawl Target" HeaderStyle-Width="50%" HeaderStyle-CssClass="ms-vh2-nofilter-perm" SortExpression="Value">
        <ItemTemplate>
            <asp:Label ID="lblServer" runat="server" Text='<%#Eval("Value") %>' />          
        </ItemTemplate>
    </asp:TemplateField>

</Columns>

Codebehind:

{
//...
var sds = new Dictionary<SPUrlZone, List<Uri>>();

foreach (var t in webApp.SiteDataServers)
{
    var uriList = new List<Uri>();
    foreach (var v in t.Value)
       {
           uriList.Add(v);
       }

   sds.Add(t.Key, uriList);
}
//...
GvItems.DataSource = sds;
GvItems.DataBind();
}
    protected void GvItems_OnRowDataBound(object sender, GridViewRowEventArgs e)
    {
        if (e.Row.DataItem != null)
        {
            Label label = (Label)e.Row.FindControl("lblServer");

            // extract list items

            string[] uris = ((KeyValuePair<SPUrlZone, List<Uri>>)e.Row.DataItem).Value.Select(x => x.ToString()).ToArray();
            label.Text = string.Join(",", uris);
        }
    }
1
In the grid, the list items should be comma-separated, e.g. "Item1,Item2". - Trevor Seward
Can you please provide minimal code showing your scenario and causing exception mentioned below? - Tomas Voracek
Cannot be KeyValuePair<string, List<Uri> in GvItems_OnRowDataBound event, it must match the datasource, ie must be KeyValuePair<SPUrlZone, List<Uri>. That's why you see InvalidCastException. Debugger is your friend ;) - Tomas Voracek
Yep, thanks for that (oops). However, now all of the Rows have the same Value, but the Value doesn't match the key. E.g. Row1 should be Key1 with "Value1", Row2 with Key2, "Value2", Row3 with Key3, "Value3, Value4". Instead, each row has "Value1,Value2,Value3,Value4". - Trevor Seward
Disregard, that is due to a scoping issue with the uriList object. - Trevor Seward

1 Answers

3
votes

Check the MSDN http://msdn.microsoft.com/en-us/library/4hx47hfe%28v=vs.110%29.aspx. Currently you are accessing List only, that's the reason why it is not evaluated as you want. Optionally you can write a method in codebehind and do it manually - personally I prefer this way.

I made small example for you:

Markup:

 <asp:GridView runat="server" ID="grid1">
            <Columns>
                <asp:BoundField HeaderText="Key" DataField="Key"></asp:BoundField>
                <asp:TemplateField HeaderText="Value">
                    <ItemTemplate>
                        <asp:Label ID="Label1" runat="server" />
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>

Code behind:

protected void Page_Load(object sender, EventArgs e)
    {
        Dictionary<string, List<Person>> data = new Dictionary<string, List<Person>>();
        data.Add("Managers", new List<Person>() { new Person() { Age = 38, Name = "Bob" }, new Person() { Age = 45, Name = "Stephen" } });
        data.Add("Developers", new List<Person>() { new Person() { Age = 25, Name = "Jake" }, new Person() { Age = 31, Name = "John" }, new Person() { Age = 27, Name = "Matthew" } });

        grid1.RowDataBound += grid1_RowDataBound;
        grid1.DataSource = data;
        grid1.DataBind();
    }

    void grid1_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        if (e.Row.DataItem != null)
        {
            Label label = (Label)e.Row.FindControl("Label1");

            // extract list items
            string[] names = ((KeyValuePair<string, List<Person>>) e.Row.DataItem).Value.Select(x => x.Name).ToArray();
            label.Text = string.Join(",", names);
        }
    }

    protected class Person
    {
        public int Age { get; set; }
        public string Name { get; set; }
    }

Or you can bind the values comma separated right from the beginning...Depends on your scenario.