views:

595

answers:

2

I haven't used repeaters for much more than showing data from a datatable.

I am building a grid that that shows a list of users and columns of roles that the user has been assigned, shown with checkboxes (shown with true/false below but pretent they are checkboxes).

ex.

|Rep Name|Caller|Closer|Manager|SuperUser|
|Bob           |True  |true   | false      | false |
|Tom          |false  |false  |True       | True |

Basically using it for roles management.

However the roles may change later on so I want to load the roles(headers and items) dynamically into the repeater.

I am not sure how to do this or if it is even possible.

I figure you grab a list of current role possibilities and load them into the headertemplate but I am not sure how to match those with the itemtemplate and how to create checkboxes and place them in the itemtemplate.

Sorry if it is a rudementary question.... I appreciate any advice!

Datatable example of data that I am going to get... though I will also return ids for roles and users that are not shown here.

DataTable dt = new DataTable(); DataColumn dc = new DataColumn();

      dc.DataType = Type.GetType("System.String");
      dc.ColumnName = "RepName";
      dt.Columns.Add(dc);

      dc = new DataColumn(); 
      dc.DataType = Type.GetType("System.Boolean");
      dc.ColumnName = "Caller";
      dt.Columns.Add(dc);

      dc = new DataColumn();
      dc.DataType = Type.GetType("System.Boolean");
      dc.ColumnName = "closer";
      dt.Columns.Add(dc);

      dc = new DataColumn();
      dc.DataType = Type.GetType("System.Boolean");
      dc.ColumnName = "Admin";
      dt.Columns.Add(dc);

      dc = new DataColumn();
      dc.DataType = Type.GetType("System.Boolean");
      dc.ColumnName = "SuperUser";
      dt.Columns.Add(dc);


      DataRow row;

      row = dt.NewRow();

      row["RepName"] = "Joe";
      row["Caller"] = true;
      row["closer"] = false;
      row["Admin"] = true;
      row["SuperUser"] = false;
      dt.Rows.Add(row);

      row = dt.NewRow();

      row["RepName"] = "Bob";
      row["Caller"] = true;
      row["closer"] = false;
      row["Admin"] = true;
      row["SuperUser"] = false;
      dt.Rows.Add(row);


      row = dt.NewRow();

      row["RepName"] = "Tom";
      row["Caller"] = true;
      row["closer"] = false;
      row["Admin"] = true;
      row["SuperUser"] = false;
      dt.Rows.Add(row);
A: 

I'll try something like this:

<asp:GridView ID="OrdersGV" runat="server" AutoGenerateColumns="False">
<Columns>
  <asp:BoundField DataField="UserName" />
  <asp:TemplateField>
    <ItemTemplate>
      <asp:ListView ID="NewsLV" runat="server">
        <ItemTemplate>
          <td id="Td1" runat="server" valign="top" style="width: 30px;">
            <asp:Label ID="Label1" runat="server" Text='<%# Eval("RoleName") %>' Visible='<%# Container.DataItemIndex == 0%>'></asp:Label>
            <div style="padding: 2px;">
              <asp:CheckBox ID="CheckBox1" runat="server" Checked='<%# Eval("IsRoleSelected") %>' />
            </div>
          </td>
        </ItemTemplate>
        <LayoutTemplate>
          <table id="Table1" runat="server" border="0" style="">
            <tr id="itemPlaceholderContainer" runat="server">
              <td id="itemPlaceholder" runat="server">
              </td>
            </tr>
          </table>
        </LayoutTemplate>
      </asp:ListView>
    </ItemTemplate>
  </asp:TemplateField>
</Columns>

alejandrobog
Unfortunately my manager doesn't want us to use gridviews. It sucks because I do have more experience with them.
sdmiller
+1  A: 

Use nested Repeaters: the outer Repeater is for rows and has a HeaderTemplate and ItemTemplate which contain inner Repeaters for header and checkbox columns respectively. Something like this:

<asp:Repeater runat="server" ID="rowRepeater" OnItemDataBound="rowRepeater_ItemBound">
    <HeaderTemplate>
        <table>                                
        <tr>
            <asp:Repeater runat="server" ID="headerRepeater">
                <ItemTemplate>
                    <th>
                        <%# Container.DataItem %>
                    </th>
                </ItemTemplate>
            </asp:Repeater>
        </tr>
    </HeaderTemplate>
    <ItemTemplate>
        <tr>
            <td><asp:Label runat="server" ID="lblUserName" Text='<%# Eval("Key") %>' /></td>
            <asp:Repeater runat="server" ID="columnRepeater">
                <ItemTemplate>
                    <td>
                        <asp:HiddenField runat="server" ID="hfRoleIndex" Value='<%# Container.ItemIndex %>' />
                        <asp:CheckBox runat="server" ID="cbColumnValue" Checked='<%# Container.DataItem %>' OnCheckedChanged="cbColumnValue_CheckedChanged" AutoPostBack="true" />
                    </td>
                </ItemTemplate>
            </asp:Repeater>
        </tr>
    </ItemTemplate>
    <FooterTemplate>
        </table>
    </FooterTemplate>
</asp:Repeater>

and in your code behind:

Dictionary<string, bool[]> userRoles = new Dictionary<string, bool[]>(){
    {"Bob", new bool[]{true,true,false,false}},
    {"Tim",new bool[]{false,false,true,true}},
    {"John",new bool[]{false,true,false,true}}
};

string[] headings = { "Rep Name", "Caller", "Closer", "Manager", "SuperUser" };

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        rowRepeater.DataSource = userRoles;
        rowRepeater.DataBind();
    }
}

protected void rowRepeater_ItemBound(object sender, RepeaterItemEventArgs e)
{
    if (e.Item.ItemType == ListItemType.Header)
    {
        Repeater headerRepeater = e.Item.FindControl("headerRepeater") as Repeater;
        headerRepeater.DataSource = headings;
        headerRepeater.DataBind();
    }
    else if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
    {
        Repeater columnRepeater = e.Item.FindControl("columnRepeater") as Repeater;
        columnRepeater.DataSource = ((KeyValuePair<string, bool[]>)e.Item.DataItem).Value;
        columnRepeater.DataBind();
    }
}

protected void cbColumnValue_CheckedChanged(object sender, EventArgs e)
{
    CheckBox cb = sender as CheckBox;
    HiddenField hf = cb.Parent.FindControl("hfRoleIndex") as HiddenField;
    int roleIndex = int.Parse(hf.Value); // now you have the column identifier

    Label lbl = cb.Parent.Parent.Parent.FindControl("lblUserName") as Label;
    string userName = lbl.Text; //now you have the row identifier

    //now you can update your data source
    userRoles[userName][roleIndex] = cb.Checked;
}
Stephen Swensen
Example would be great!
sdmiller
Thanks a lot, I will give that a shot!
sdmiller
Quick question... I need add an oncheck event so call a method in code behind. When I do this all of the checkboxes look the same... Is there a good way to tell which was checked... may binding the id of the header role to the checkbox somehow.Caller id =1closer id =2ect... they come from a table of course but you get the idea.Thanks for you help... this has been great so far!
sdmiller
I added a mock datatable to my original post to show the type of datatable I will be binding. As stated above... the real one will also contain fields for role id and user id.
sdmiller
See my latest edit: the hfRoleIndex and lblUserName Controls can be used to capture the coordinates for each CheckBox, and in the CheckedChanged event you can retrieve their values by traveling the server DOM. I'm using a Dictionary to mock the data, but this strategy can be adopted to any data source. Glad this has been helpful! I had to figure this stuff out on my own over two years of experience and could never find a good resource for something which should be a common use case.
Stephen Swensen
You have been awesome... Thanks a lot! I have been programming for just over a year... so I bang my head a lot trying to understand some of these things!
sdmiller