Avoid Page_Load to effect my array, using AJAX - c#

I have a textbox that the user suppose to place numbers on it,
and after he push the button, the number should pass to an array.
and each number and button pushed, should be saved in this array in order (for example : 3 , 4, 5, ....)
The problem is, that each time I push the button, then page_load occurs. I have this code :
protected string[] CurrentArr;
protected void Page_Load(object sender, EventArgs e)
{
if (CurrentArr != null)
{
CurrentArr = (string[])Session["CurrentArr"];
}
else
CurrentArr = new string[length];
which CurrentArr is the array that change over time.
I tried to solved it with AJAX as well :
<asp:ScriptManager runat="server" ID="sm">
</asp:ScriptManager>
<asp:UpdatePanel runat="server" ID="update">
<ContentTemplate>
<input type="text" class="response" id="how_many" name="guess" placeholder="Enter your guess..." />
<asp:Button runat="server" class="button red" id="generate" name="generate" value="Generate!" OnClick="guess_Click" />
<asp:Button runat="server" class="button red" id="win" name="win" value="you won" />
</ContentTemplate>
</asp:UpdatePanel>
but it has no effect over my array (although my page doesnt post back...)
anyone has a solution ?
Thanks!

You are using an UpdatePanel, it is inherently AJAXified. Just add a trigger section to catch the button click:
<asp:UpdatePanel runat="server" ID="update">
<ContentTemplate>
<input type="text" class="response" id="how_many" name="guess" placeholder="Enter your guess..." />
<asp:Button runat="server" class="button red" id="generate" name="generate" value="Generate!" OnClick="guess_Click" />
<asp:Button runat="server" class="button red" id="win" name="win" value="you won" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="generate" EventName="Click" />
</Triggers>
</asp:UpdatePanel>
The problem that you're probably running into is that your array isn't persisting through the postbacks. Try what you and ed were getting at, getting and setting from the session. I prefer not to use old school arrays. Consider a list, they are more versatile. And if you specifically need an array at the end then just use myList.ToArray();
private List<int> CurrentArr = new List<string>();
protected void Page_Load(object sender, EventArgs e)
{
if(Session["CurrentArr"] != null)
{
//if there is something stored in the session variable then grab that as a working array
CurrentArr = (List<string>)Session["CurrentArr"];
}
else
{
//if not, then initialize one
CurrentArr = new List<string>();
Session["CurrentArr"] = CurrentArr;
}
}
protected void guess_Click(object sender, EventArgs e)
{
//get reference to the button clicked like you said in your question
var btnMyButton = sender as Button;
btnMyButton.Enabled = false; //example of using the reference, disable the button that was clicked
//add the value of your text box to the array/list, sort, then update the session
this.CurrentArr.Add(txtYourTextBox.Text);
this.CurrentArr.Sort();
Session["CurrentArr"] = this.CurrentArr;
}

Of course, Load is part of an ASP.Net's Page life cycle.
See Page.IsPostBack to have more control on what methods you want, or don't want to run depending on the request type (is it a postback or not).
In the code above, this is probably what you meant:
if (Session["CurrentArr"] != null)
{
CurrentArr = (string[])Session["CurrentArr"];
}
else
{
CurrentArr = new string[length]; //where is length defined?
Session["CurrentArr"] = CurrentArr;
}

Related

Modal Panel Doesn’t Appear When Called by Button Inside a Grid View’s Cell

I've struggled a lot with how to show a modal panel on click on a button inside a grid view.
To context: I have a data row with a string field that can contain a simple text or a base 64 encoded image, so I'm using a custom template to define when to show the raw content or a button "View Image". This image will be opened on a modal panel that should rise up on button click.
This is the Panel I've created as a control (ascx):
<asp:Panel ID="pnlModalOverlay" runat="server" Visible="true" CssClass="Overlay">
<asp:Panel ID="pnlModalMainContent" runat="server" Visible="true" CssClass="ModalWindow">
<div class="WindowTitle">
<asp:Label ID="lbTitle" runat="server" />
</div>
<div class="WindowBody">
<asp:Panel ID="pnlContent" runat="server" Visible="true">
<asp:Image ID="imgContent" runat="server" CssClass="ImageView" />
</asp:Panel>
<div class="Button">
<asp:Button ID="btnOk" runat="server" class="btn btn-default " Text="Close" OnClientClick="loadingPanel.Show();" />
</div>
</div>
</asp:Panel>
</asp:Panel>
And this is the page and ASPxGridView where I wanna use it:
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="true">
<ContentTemplate>
<div style="margin-top: 12px;">
<asp:Button type="button" ID="btnShowImage" AutoPostBack="true" class="btn btn-default navbar-right" Text="Show Image"
runat="server" Style="margin-left: 5px;" OnClientClick="loadingGridPanel.Show();" />
</div>
<!-- Some data filter controls -->
<MyWorkspace:AlertModal ID="alertModal" runat="server" Visible="false" />
<MyWorkspace:ImageModal ID="imageModal" runat="server" Visible="false" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="mainGrid" />
</Triggers>
</asp:UpdatePanel>
<MyWorkspace:GridViewWrapper ID="mainGrid" runat="server" Visible="true" />
Codebihind:
public partial class MyPage : System.Web.UI.Page
{
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
btnShowImage.Click += new EventHandler(ShowImage); // This call works fine
}
protected void Page_Load(object sender, EventArgs e)
{
try
{
if (!IsPostBack)
{
mainGrid.CanEditItems = true;
mainGrid.CustomTemplates.Add(new CustomColumnTemplate { columnName = "Id", template = new LinkColumn(CreateParentLink, "Go to parent") });
mainGrid.CustomTemplates.Add(new CustomColumnTemplate { columnName = "Value", template = new ButtonColumn(ShowImage, "View Image") }); // This one doesn't works
}
}
catch (Exception ex)
{
modalAlerta.Show("Page_Load", ex.Message, false, false, "");
}
}
void ShowImage()
{
modalImagem.Show(); // Set Modal's Visible property to True
// UpdatePanel1.Update(); <-- Tryin' force it to work with no success
}
}
The ButtonColumn template creation:
public class ButtonColumn : System.Web.UI.ITemplate
{
private Action action;
private string controlId;
private string tooltip;
public ButtonColumn(Action onClick, string toolTip)
{
this.action = onClick;
this.controlId= "btnShowImage";
this.tooltip = toolTip;
}
public void InstantiateIn(System.Web.UI.Control container)
{
GridViewDataItemTemplateContainer gridContainer = (GridViewDataItemTemplateContainer)container;
if (System.Text.RegularExpressions.Regex.IsMatch(gridContainer.Text, "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$"))
{
ImageButton button = new ImageButton();
button.ID = idControle;
button.ImageUrl = "/Images/html5_badge_64.png";
button.Width = 20;
button.Height = 20;
button.ToolTip = tooltip;
button.Click += (s, a) =>
{
if (onClick != null)
onClick();
};
container.Controls.Add(button);
}
else
{
Label label = new Label()
{
Text = gridContainer.Text,
ToolTip = tooltip
};
container.Controls.Add(label);
}
}
}
The method's call at the click of btnShowImage button works fine. But when I do the same call by one ImageButton (or button) inside the gridview it doesn't work. Both calls reach the ShowImage method.
Any help would be appreciated. Thank you all.
EDIT 1:
The GridView is encapsulated in GridViewWrapper (there I build the columns dynamically using a combination of class's properties gotten by reflection and stored metadata), this class have too much code to share here and I do not think it's the reason. Also, I've executed in debug mode and passed thru it step by step every relevant method inside this one.
The column add method:
CustomColumnTemplate customTemplate = CustomTemplates.FirstOrDefault(f => f.columnName == metadata.ColumnIdName);
gridView.Columns.Add(new GridViewDataColumn()
{
FieldName = metadata.ColumnIdName,
VisibleIndex = GetVisibleIndexByColumnIdName(metadata.ColumnIdName),
Caption = metadata.Caption,
Width = new Unit(DefaultColumnWidth, UnitType.Pixel),
DataItemTemplate = customTemplate == null ? null : customTemplate.template
});
I've made sure the ShowImage method is being hitten, but it behaves like the UpdatePanel1 isn't have been updated
The ASPxGridView stores information about columns in ViewState, but does not save information about column templates. This is made on purpose since templates can be very complex and their serialization makes ViewState very huge.
So, if you create columns with templates at runtime, disable ViewState:
ASPxGridView.EnableViewState="false"
and create columns on every callback:
//if (!IsPostBack)
//{
mainGrid.CanEditItems = true;
mainGrid.CustomTemplates.Add(new CustomColumnTemplate { columnName = "Id", template = new LinkColumn(CreateParentLink, "Go to parent") });
mainGrid.CustomTemplates.Add(new CustomColumnTemplate { columnName = "Value", template = new ButtonColumn(ShowImage, "View Image") }); // This one doesn't works
//}
You used code below:
<Triggers>
<asp:AsyncPostBackTrigger ControlID="mainGrid" />
</Triggers>
According to this article, in asp:AsyncPostBackTrigger If the EventName property is not specified, the DefaultEventAttribute attribute of the control is used to determine the default event. For example, the default event for the Button control is the Click event.
mainGrid control created by GridViewWrapper that it doesn't connected to controls that are in mainGrid.
Updatepanel tries to register async trigger for the mainGrid control which is outside the panel but it can't do it.
solution:
I think solution of this problem is update Updatepanel in ShowImage() method.

Updating CSS of Controls in different files via CodeBehind

I have this AJAX button that I want to update some CSS. The problem is that the controls I want it to update are in a different file and can't be moved out of that file.
This is the panel whose CSS I want to update, which is located in the Site.master file:
<asp:ScriptManager ID="ScriptManager" runat="server" />
<asp:UpdatePanel ID="Panel2" runat="server" updatemode="Always">
<ContentTemplate>
<div id="updateThis" runat="server"><p>Test text</p></div>
</ContentTemplate>
</asp:UpdatePanel>
This is the button, located in the Items.ascx file:
<asp:UpdateProgress runat="server" ID="PageUpdateProgress">
<ProgressTemplate>
<img class="ajax-loader" src="/Images/ajax-loader.gif" alt="Loading..." />
</ProgressTemplate>
</asp:UpdateProgress>
<asp:UpdatePanel ID="Panel3" runat="server" updatemode="Conditional">
<Triggers>
<asp:AsyncPostBackTrigger controlid="UpdateButton" eventname="Click" />
</Triggers>
<ContentTemplate>
<asp:Button runat="server" OnClick="UpdateButton"
OnClientClick="hideButton()" Text="Update"
class="update" ID="UpdateButton" name="UpdateButton"
type="submit" ></asp:Button>
</ContentTemplate>
</asp:UpdatePanel>
This is the Items.ascx.cs method that is UpdateButton
protected void UpdateButton(object sender, EventArgs e)
{
var masterPage = this.Page.Master;
var updatePanel = masterPage.FindControl("Panel2");
var div = (HtmlGenericControl)updatePanel.Controls[0].FindControl("updateThis");
div.Style.Add("color", "#ff0000");
}
When I click the button it doesn't end up working correctly. As of right now, with the AJAX UpdateProgress template, it shows the loading GIF and then the GIF disappears and the text never changes color.
EDIT
Hopefully this will give a better idea of where things might be going wrong:
THIS WORKS
protected void UpdateButton(object sender, EventArgs e)
{
var masterPage = this.Page.Master;
var updatePanel = masterPage.FindControl("Panel2");
var div = (HtmlGenericControl)updatePanel.FindControl("updateThis");
div.Style.Add("color", "#ff0000");
UpdateButton.Text = "Done!";
}
I had #updateThis in the FindControl() method. Don't be like me!
You should add the runat="server" attribute to the div control, to make it visible in code-behind:
<div id="updateThis" runat="server"><p>Test text</p></div>
You can also remove Controls[0] in the event handler (but it works if you keep it, according to my tests):
protected void UpdateButton(object sender, EventArgs e)
{
var masterPage = this.Page.Master;
var updatePanel = masterPage.FindControl("Panel2");
var div = (HtmlGenericControl)updatePanel.FindControl("updateThis");
div.Style.Add("color", "#ff0000");
}

not able to redirect users to another page with hyper link

I have requirement where users are redirected youtube.com by using hyperlink control using below
I want to change the URL dynamically based on drop down list selected item by using below code.
protected void ddlPType_SelectedIndexChanged(object sender, EventArgs e)
{
int x = ddlPType.SelectedIndex;
if (x == 0)
{
activateCerts.NavigateUrl = "http://www.youtube.com/watch?v=3AYoipyqOkQ";
activateCerts.Text = "activateCerts";
activateCerts.Target = "_blank";
//activateCerts.HRef = "http://www.youtube.com/watch?v=3AYoipyqOkQ";
}
else if (x == 1)
{
//activateCerts.Target = "_blank";
//activateCerts.HRef = "http://www.youtube.com/watch?v=hk3hxUuwg0w";
activateCerts.Text = "activateCerts";
activateCerts.NavigateUrl = "http://www.youtube.com/watch?v=3AYoipyqOkQ";
}
and this is the one aspx code
<asp:Label runat="server" style="padding-left:23rem;" Text="pls watch this video on How to"></asp:Label>
<asp:HyperLink ID="activateCerts" runat="server"></asp:HyperLink>
but when I click on link I am not able to open a youtube video
This is working for me by setting AutoPostBack=true for dropdpwn ddlPType :
<form id="form1" runat="server">
<div>
<asp:DropDownList runat="server" ID="ddlPType" AutoPostBack="true" OnSelectedIndexChanged="ddlPType_SelectedIndexChanged">
<asp:ListItem Text="Option 1" Selected="True" />
<asp:ListItem Text="Option 2" />
</asp:DropDownList>
<br />
<asp:Label ID="Label1" runat="server" style="padding-left:23rem;" Text="pls watch this video on How to"></asp:Label>
<asp:HyperLink ID="activateCerts" runat="server"></asp:HyperLink>
</div>
</form>
.cs Page :
protected void ddlPType_SelectedIndexChanged(object sender, EventArgs e)
{
int x = ddlPType.SelectedIndex;
if (x == 0)
{
activateCerts.NavigateUrl = "http://www.youtube.com/watch?v=3AYoipyqOkQ";
activateCerts.Text = "activateCerts";
activateCerts.Target = "_blank";
//activateCerts.HRef = "http://www.youtube.com/watch?v=3AYoipyqOkQ";
}
else if (x == 1)
{
//activateCerts.Target = "_blank";
//activateCerts.HRef = "http://www.youtube.com/watch?v=hk3hxUuwg0w";
activateCerts.Text = "activateCerts";
activateCerts.NavigateUrl = "http://www.youtube.com/watch?v=3AYoipyqOkQ";
}
}
For your Dropdownlist named ddlPType, you need to make sure its AutoPostBack is true. You can set it in the Attribute Panel, or using the code:
<asp:DropDownList runat="server" ID="ddlPType" AutoPostBack="true" OnSelectedIndexChanged="ddlPType_SelectedIndexChanged">
By this step you should achieve your goal, but sometimes this is not that simple. You may need to make sure you put your data-bind (if there is) in if (!Page.IsPostBack) in Page_Load.
Also, Dropdownlist will only send data when the data is changed. That is to say, if you get two options sharing the same value, Dropdownlist may not respond you. For example:
if(!IsPostBack)
{
for(int i=0;i<10;i++)this.DropDownList1.Items.Add(new ListItem(i.ToString(),"same_value"));
}
Here comes the most strange situation: you have done all above but it still does not work. Sometimes it happens in IE8. If you use window.showModalDialog() to show DropDownList, submitting will leads you to a new page. You need to add between head tag:
<base target=_self></base>
Hope my experience will do help.

How to create asp.net app combining ListBox, TextBox and Button

I'm designing small instant messenger app on .NET platform.
I have a ListBox, TextBox and Button (called Send).
When user click send button, Text of TextBox will be appeared on ListBox but user should not send 3 messages in 1 minute(message restriction) and also his/her size of message should consist min 20 max 140 strings.
How can I do this?
The example below uses the timer control, if you would like to learn more about using timers in ASP.NET have a look at this video tutorial by Joe Stagner.
Basically I'm storing the number of messages in ViewState and when that number reaches 3 I start the timer which will reset the ViewState["Messages"] back to 0 after 1 minute (60 000 milliseconds) and the user is once again able to send more messages.
ASPX:
<asp:ScriptManager ID="Scriptmanager" runat="server" />
<asp:Timer ID="timer" runat="server" Enabled="false" Interval="60000" OnTick="Tick" />
<asp:UpdatePanel ID="updatePanel" runat="server">
<ContentTemplate>
<asp:TextBox MaxLength="140" ID="txtMessage" runat="server" />
<asp:Button ID="btnSend" runat="server" Text="Send" OnClick="Send" /> <span
id="error" runat="server" style="color: Red;" />
<br />
<asp:ListBox ID="lbMessages" runat="server" Width="240" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="timer" />
</Triggers>
</asp:UpdatePanel>
Code behind:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
ViewState["Messages"] = 0;
}
public void Send(object sender, EventArgs e)
{
error.InnerHtml = string.Empty;
string message = txtMessage.Text;
if (message.Length < 20)
{
error.InnerHtml = "The message should be at least 20 characters long";
return;
}
int messageNumber = (int)ViewState["Messages"];
if (messageNumber < 3)
{
lbMessages.Items.Add(message);
ViewState["Messages"] = ++messageNumber;
if (messageNumber.Equals(3))
timer.Enabled = true;
}
}
protected void Tick(object sender, EventArgs e)
{
ViewState["Messages"] = 0;
timer.Enabled = false;
}
Also you don't need to check for maximum length in code, there is a property for that on the textbox - MaxLength
Maybe you can set an hidden field in your page load to store serialised three last requests time and another for last minute message count.
in the click button event get text of the textbox apply your size restriction and verify message count.

Viewstate not preserved when handling exception

I'm using a detailsview for a dialog to the user, and it seems that the viewstate is not preserved when there is an error inserting the data.
I'm using a OnInserted handler on the datasource to check if there was an exception like so:
protected void areaInsertHandler(Object sender, SqlDataSourceStatusEventArgs e)
{
if (e.Exception != null)
{
// There was an error in submitting, show the error dialog
ScriptManager.RegisterClientScriptBlock(Page, GetType(), "DialogHandler", "showError('#overlayAreas');", true);
e.ExceptionHandled = true;
}
}
Which simply calls the client side JS function:
function showError(overlayName) {
$(".msgError").css('visibility', 'visible');
$(overlayName).css('visibility', 'visible');
}
My detailsview looks something like this:
<asp:UpdatePanel ID="AreaUP" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<div id="overlayAreas" class="overlay">
<asp:DetailsView
ID="DetailsView_Areas"
runat="server"
Visible="True"
AutoGenerateInsertButton="False"
AutoGenerateRows="False"
caption="<a style='font-weight: bold;'>Bold</a> = Required field"
CaptionAlign="Bottom"
headertext="Create new area"
EnableViewState="true"
DataKeyNames="Area_Name"
DataSourceID="AreasSource"
DefaultMode="Insert">
<Fields>
...
</Fields>
</asp:DetailsView>
</div>
<br />
<asp:Button width="200" height="30" ID="Button_CreateArea" runat="server" OnClientClick="return btnToggle('#overlayAreas')" Text="Create new area" />
</ContentTemplate>
</asp:UpdatePanel>
It all works fine, but for some reason the ASP viewstate is not preserved. Meaning that if I fill out incorrect information in the form and submit I will get the appropriate error and the dialog will still be displayed. But the fields are not filled out with my old values.
If someone could give me some pointers or help me out I'd greatly appreciate it
EDIT 10-08: Still haven't been able to solve it, any ideas at all?
basically:
avoid DetailsView_Areas.DataBind()
if (DetailsView_Areas.CurrentMode != DetailsViewMode.Insert) DetailsView_Areas.DataBind();
create the ItemInserted event for your DetailsView_Areas and put
if (e.AffectedRows < 0) e.KeepInInsertMode = true;
see http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.detailsviewinsertedeventargs.affectedrows(v=vs.110).aspx

Categories

Resources