Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Dynamically added linkbutton doesn't fire event first time through 2

Status
Not open for further replies.

reharik

Programmer
Feb 12, 2008
7
US
Hi, I have data table that I create dynamically and add linkbuttons to then add the whole thing to a asp:placeholder. now when I get to the page, I click on a cell and it posts back, hits init and load then comes back. the second time i click on it, it posts back hits init and load then hits my onclick event, then hits init and load again and out. I'm creating and binding in the onload every time, but I"ve tried putting it in the oninit and the onload and it doesn't help. This is really quite a basic thing, on load get dataset, create table add controls and event add datatable to placeholder and it's done, I can't figure out why it's only hitting the click event every other time. And sometimes it fires on the first time into the page but not the second and then everyother one. very odd. Anyway, any help would be greatly appreciated.
 
We need to see your code to be able to help. Also, any dynamic controls should always be created in the Page_Init event.
 
ok, here it is, I've truncated and pseudo coded it a bit but the important parts is there I'm not creating in the oninit, this is true all though i have tried it, I'll try again while you view this. thanks for taking a look.

protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// set page up populate some drop downs etc.
}
BindGrid(); -- this just gets a dataset based on the dropdowns mentioned above and then calls the DataTableModel() the dataset returns fine.
}

public void DataTableModel()
{
if (ph.Controls.Count < 1)
{
tblApts = new Table();
tblApts.BackColor = Color.White;
tblApts.Width = Unit.Percentage(100);
tblApts.CellSpacing = 0;
tblApts.ID = "tblApts";
ph.Controls.Add(tblApts);
}
if (tblApts.Rows.Count > 1)
tblApts.Rows.Clear();
bool alternate = false;
if (source.Rows.Count <= 0)
return;
DateTime date = (DateTime)source.Rows[0][0];
TableRow header = new TableRow();
header.BackColor = Color.FromName("#bbbbbb");
CreateHeaderRow(header);
tblApts.Rows.Add(header);
for (int rowIndex = 0; rowIndex < source.Rows.Count; rowIndex++)
{
TableRow tr = new TableRow();
if (alternate)
{
tr.BackColor = Color.WhiteSmoke;
alternate = false;
}
else
alternate = true;
DataRow dr = source.Rows[rowIndex];
for (int cellIndex = 1; cellIndex < dr.ItemArray.Length; cellIndex++)
{
TableCell td = new TableCell();
if (cellIndex > 1)
{
if (rowIndex == 0)
CreateCell(td, dr[cellIndex].ToString(), "", source.Rows[rowIndex + 1][cellIndex].ToString(), "xx~" + dr[1].ToString() + "~" + DateFunctions.GetDayForNumber(cellIndex - 1, Co.FirstDayOfWeek), !alternate);
else if (rowIndex == source.Rows.Count - 1)
CreateCell(td, dr[cellIndex].ToString(), source.Rows[rowIndex - 1][cellIndex].ToString(), "", "xx~" + dr[1].ToString() + "~" + DateFunctions.GetDayForNumber(cellIndex - 1, Co.FirstDayOfWeek), !alternate);
else
CreateCell(td, dr[cellIndex].ToString(), source.Rows[rowIndex - 1][cellIndex].ToString(), source.Rows[rowIndex + 1][cellIndex].ToString(), "xx~" + dr[1].ToString() + "~" + DateFunctions.GetDayForNumber(cellIndex - 1, Co.FirstDayOfWeek), !alternate);
}
else
{
Label time = new Label();
time.Text = dr[1].ToString();
time.CssClass = "Form_Label";
td.Controls.Add(time);
td.Attributes.Add("Style", "border:ridge 2px");
}
if (td.Controls.Count > 0)
tr.Cells.Add(td);
}
// add second time column at the end
TableCell tdEnd = new TableCell();
Label timeEnd = new Label();
timeEnd.Text = dr[1].ToString();
timeEnd.CssClass = "Form_Label";
tdEnd.Controls.Add(timeEnd);
tdEnd.Attributes.Add("Style", "border:ridge 2px");
tr.Cells.Add(tdEnd);
tblApts.Rows.Add(tr);
}
}

void CreateCell(TableCell td, string item, string previous, string next, string arg, bool alt)
{
LinkButton lb = new LinkButton();
lb.CssClass = "Form_Grid";
lb.Width = Unit.Pixel(80);
lb.Height = Unit.Percentage(100);
lb.Click += new EventHandler(Apt_Click);
lb.CommandArgument = arg;
string style = "border-left:ridge 2px; border-right:ridge 2px;";
int aptNum = 0;
string trName;
string type;

switch (item)
{
case "Full":
style += "background-color:gray; border-top:ridge 2px; border-bottom:ridge 2px; ";
break;
case "Not Full":
style += "background-color:ButtonFace; border-top:ridge 2px; border-bottom:ridge 2px; ";
break;
case "Empty":
lb.Text = "EmptyEmpty";
style += "border-top:ridge 2px; border-bottom:ridge 2px; ";
if (alt)
lb.ForeColor = System.Drawing.Color.WhiteSmoke;
else
lb.ForeColor = System.Drawing.Color.White;

break;
default:
aptNum = Int32.Parse(item.Substring(0, item.IndexOf("~")));
trName = item.Substring(item.IndexOf("~") + 1, item.LastIndexOf("~") - 3);
type = item.Substring(item.LastIndexOf("~") + 1);
lb.CommandArgument = item + "~" + arg.Substring(arg.LastIndexOf("~") - 3); ;
if (AptType.GetIfMultipleByDescription(type, ((ASP.masterpages_masterpage_master)Page.Master).CoId))
lb.Text = trName;
else
lb.Text = GetClientList(aptNum);
lb.Font.Underline = false;
lb.ToolTip = "Client(s): " + GetClientList(aptNum);
style += "border-top:ridge 2px; border-bottom:ridge 2px; background-color:Green; ";
string filter = "apt_id = " + aptNum;
DataRow[] dr = DS.Tables[2].Select(filter);
td.RowSpan = Int32.Parse(dr[0][1].ToString());
td.VerticalAlign = VerticalAlign.Top;
td.HorizontalAlign = HorizontalAlign.Center;
if (previous.Contains("~") && previous.Substring(0, previous.IndexOf("~")) == aptNum.ToString())
{
td = null;
return;
}
else
style += " border-top:ridge 2px; ";
if (!next.Contains("~") || next.Substring(0, next.IndexOf("~")) != aptNum.ToString())
style += " border-bottom:ridge 2px; ";
break;
}


td.Controls.Add(lb);
td.Attributes.Add("Style", style);
}

 
OK! Good news, The problem lies in the code that clears the table rows at the begining of the datatablemodel() method. when I comment that out it fires every time but I get two sets of rows. That is way easier to deal with. I guess just talking it through can help.
Thanks
 
excellent. glad you got it... post back if you have any more questions.
 
funny you should mention it. it's not that easy to figure out after all. the problem is now that when I recreate the controls on the onclick event it doesn't hook up the eventhandlers because it does that somewhere between init and load so that ship has sailed and I get my controls but with no events. IT's a bloody nightmare.
 
without digging to much into your code I would say the problem is your snippet is responsible for too many functions.
in your code your
1. creating a table
2. inputing data into the table
3. applying styles to the table.

instead you could
1. use repeaters/user controls to define the html
2. use properties to set the data values.
3. use css classes to define the styles.

at a minimum it makes it easier to manage the code. plus it would me it much easier to find the problem. right now you have to wade through html and css just to find the actual code/events.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Thank you for your analysis. They are kind of large methods, and I will try to refactor them some more. I can't however use a repeater because essentially I'm creating a scheduler and the some of the cells need to span several rows and appear as one cell. I've been able to do this with the gridview but it really doesn't like it and it is very fragile. What I'm doing now renders everything perfectly and I was very happy until I realized what a nightmare this event business was going to be.
Part of the problem is that I don't know how many rows there are going to be or for that matter how many columns (although I can probalbly nail down the columns) until I get the data from the db. Then when someone changes the request like, only show me the times when I have appointments then posts back, well, now It's getting the data in the event rather than the onload and that's where the problem is. The hooking up of eventhandlers stage has already occured and I missed it cause I didn'thave the parameters to get the data at that point.
uggg.
What I think I'm going to do is create a generic table with as many rows and columns as are possible for the grid and fill them up with linkbuttons in the onload. then once I get the data populate the grid in a sort of homegrown databinding.
I am open and willing to hear ANY thoughts or suggestions.
Thanks again,
Raif
 
off the top of my head.
1 repeater for the header columns.
1 repeater for the body rows with nested repeader for the columns.
2 repeater for the footer.
something like this might work
Code:
<table>
  <asp:Repeater id="header">
     <HeaderTemplate><thead><tr></HeaderTemplate>
     <FooterTemplate></tr></thead></FooterTemplate>
     <ItemTemplate><th><%#Eval("ColumnHeaderTextFromDataSource")%></th></ItemTemplate>
  </asp:Reader>
  <asp:Repeater id="footer">
     <HeaderTemplate><tfoot><tr></HeaderTemplate>
     <FooterTemplate></tr></tfoot></FooterTemplate>
     <ItemTemplate><td><%#Eval("ColumnFooterTextFromDataSource")%></td></ItemTemplate>
  </asp:Reader>
  <asp:Repeater id="body">
     <HeaderTemplate><tbody></HeaderTemplate>
     <FooterTemplate></tbody></FooterTemplate>
     <ItemTemplate>
        <tr>
        <asp:Repeater id="columnsForBody">
           <HeaderTemplate><td class='<%#Eval("classname")%>'></HeaderTemplate>
           <FooterTemplate></td></FooterTemplate>
           <ItemTemplate><%#Eval("fieldvalue")%></ItemTemplate>
        </asp:Repeater>
        </tr>
     </ItemTemplate>
  </asp:Reader>
</table>
this doesn't account for multispan fields/columns but it would build html like this
Code:
<table>
   <thead>
      <tr><th>a</th><th>b</th></tr>
   </thead>
   <tbody>
      <tr><td>c</td><td>d</td></tr>
      <tr><td>e</td><td>f</td></tr>
   </tbody>
   <tfoot>
      <tr><td>g</td><td>h</td></tr>
   </tfoot>
</table>
this would complicate how the data is fetched in the code behind. you would need a list of columns for the header, footer and columnsForBody repeaters. this could be compiled from the original datasource. then the original datasource will be used on the body repeater.

since you have to deal with spaned columns you could build the templates using web user controls and inject them into the repeater where appropiate.

to hook up events to control within the repeater use the ItemCreated event for the repeater. to set the data for those controls use the ItemDataBound event.

if your using buttons set the commandargument/commandname properties and utilize the repeater.ItemCommand event. then you don't have to worry about wiring up events to buttons and the like. the repeater will automatically do that because of the ItemCommand.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
I had a similar problem some time ago but unfortunately wasn't able to work out the cause of the problem within the time I had available, so had to use a workaround instead.

I'm going from memory here so this may not be accurate but I think my work around was something like:

Code:
    protected void Page_Load(object sender, EventArgs e)
    {
        if (Page.IsPostBack)
        {
            if (sender.GetType() == typeof(LinkButton))
            {
                LinkButton lnk = (LinkButton)sender;
                if (lnk.ID == "MY LINK ID")
                {
                    //Manually call the event
                    LinkButtonEvent();
                }
            }
        }
    }

If you ever find the real cause of the problem I would love to hear the solution.

HTH

Smeat
 
hi jmeckley,
This is good stuff and I appreciate it. But the real problem is that I need row spans. So one cell may span 3 rows while the next cell is just the usual one row. let me see if I can diagram this.

-7:00|--joe--|--sam--|--jon--
-7:30|-blank-|-blank-|-blank-
-8:00|--sam--|-blank-|--jon--
-8:30| |--jon--|--joe--
-9:00| | |-blank-
-9:30|--joe--| |-blank-
10:00|-blank-|--sam--|--joe--

so you can see that sam has an apt from 7:30 till 9:00 now I could just put sam, sam, sam but it wont look nice so I rowspan of 3 and then I have to remove the cells that the row span is covering or it will push them out to the side. I don't seem to have the control over the individual cells in the repeater the way I do in a gridview or in my home made grid.
 
yes, this does complicate the example above, but it doens't make it impossible. the trick is to find a way to inform the nested repeater of the number of cells and there spans. this should be defined by the datasource.
example
Code:
public class Block
{
   public Block(string text, int span)
   public int Span {get;}
   public string Text{get;}
}

repeater.DataSource = List<Block>();
repeater.DataBind();
Code:
<asp:Repeater>
   <HeaderTemplate><tr></HeaderTemplate>
   <FooterTemplate></tr></FooterTemplate>
   <ItemTemplate>
      <td span='<%#Eval("Span")%>'>'<%#Eval("Text")%>'</td>
   </ItemTemplate>
</asp:Repeater>

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Thanks Guys,
Jason I will give this a try. I may also give Smeat's work around a try as well because it allows me to use the grid which is working fine except for the events. I know that this is a total work around and that there may be a proper way to do this but At this point I'm willing to for go purity for results.
Thanks again. I appreciate all the responses.
R
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top