ASP.NET Dynamic User Control with JQuery - asp.net

Alright...this is a bit of a complex problem so hopefully I can explain it properly.
I have a user control which I am dynamically loading in my page_load event via a method (see below). The user control contains a gridview and a label. A key piece of information has to do with how to get around the convenient feature of gridviews not rendering when their datasource is empty. In my user control I add some hidden rows so that the grids will render and the user can see just the headers (if the situation calls for it).
The nomControl is an asp:Panel on the parent page which will hold the user controls. The dsRefinedProductsNomInfo is a strongly typed dataset.
private void LoadCycleControls()
{
var dsRefinedProductsNomInfo = Session[REFINED_PRODUCT_NOMINATION_INFO] as RefinedProductsNomInfo;
if (dsRefinedProductsNomInfo == null)
{
return;
}
int permittedCycles = dsRefinedProductsNomInfo.NOS_MONTH.Count == 0 ? 0: dsRefinedProductsNomInfo.NOS_MONTH[0].PERMITTED_CYCLES;
for (int cycle = 1; cycle <= permittedCycles; cycle++ )
{
var control = LoadControl("~/CustomControl/RefinedProductNominationCycleControl.ascx");
nomContent.Controls.Add(control);
var nomControl = control as RefinedProductNominationCycleControl;
if (nomControl == null)
{
return;
}
nomControl.CycleNumber = cycle;
nomControl.ID = "control_" + cycle;
nomControl.CycleTitle = "Cycle " + cycle;
nomControl.GridDataSource = dsRefinedProductsNomInfo.REFINED_PRODUCT_NOS_CYCLE_INFO;
}
}
Now when a user adds a row to the grid they click a plus button. In the button javascript click event I make an ajax call to a page method.
var options = {
type: "POST",
url: "RefinedProductNomination.aspx/AddNomCycleLineItem",
data: "{'id':'" + tableRow.attr("id") + "',\
'isPlug':'" + isPlug + "',\
'sequenceNbr':'" + sequenceNbr + "',\
'receiptLocationId':'" + receiptLocationId + "',\
'materialTypeId':'" + materialId + "',\
'shipperId':'" + shipperId + "',\
'tankId':'" + tankId + "',\
'deliveryLocationId':'" + deliveryLocationId + "',\
'volume':'" + volume + "',\
'cycle':'" + cycle + "'}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(response) {
if (response.d != "") {
if (response.d == "-1000") {
var messageContainer = tableRow.parents("div").siblings("div #messageSummary");
messageContainer.empty()
var message = $("<span></span>").append("There has been an issue accepting the row please try again.");
messageContainer.append(message);
message.fadeOut(10000, "linear");
}
else {
ConvertToReadOnlyRow(tableRow, response.d);
}
}
}
};
//Call the PageMethods
$.ajax(options);
The page method then retrieves the dataset from the cache and adds the new row the user created. This all works fine and I can save to the database via an asp:button and handling it's click event.
My issue is after the the user clicks the save button and everything is rendered my grid shows the rows I added to make sure just the headers show up. In my save button click event I am removing these added rows so they don't get persisted and then saving and then added again once we load the user control. Initially this seems like the right place to handle this but after figuring out the order of events it would appear I was wrong.
So my question. Can anyone suggest how I should be handling my events so that my rendered grid is up to date and not showing these hidden rows. I have a feeling I am just doing things in the wrong place/order but this is my first real swim in the deep end of the asp.net pool. If any more information would be helpful let me know.
Thank you in advance for any help.

Well I think I have solved my own problem. Essentially what I have done is this:
1. On parent page_load I reload controls so that they show up on the screen.
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
LoadCycleControls();
return;
}
RetrieveParameters();
LoadRefinedNominationInfo();
LoadLookupLists();
LoadCycleControls();
}
2. On control page_load I check IsPostBack. If it's true I just return.
protected void Page_Load(object sender, EventArgs e)
{
if(IsPostBack)
{
return;
}
BindData();
cycleNumber.Value = _cycleNumber.ToString();
}
3. On parent save button click, which caused the post back, I do my normal save logic. After that is complete I iterate over all the controls I created set their data source to the updated dataset. In my control I exposed a method called BindData() which I then call.
protected void BtnSaveClick(object sender, EventArgs e)
{
SaveRefinedProductNosInfo();
var dataSet = RefinedProductsNomInfoCache;
if (dataSet == null)
{
return;
}
foreach (var nomControl in nomContent.Controls.OfType<RefinedProductNominationCycleControl>())
{
nomControl.GridDataSource = dataSet.REFINED_PRODUCT_NOS_CYCLE_INFO;
nomControl.BindData();
}
}
Now even though I am slightly new at this, it feels a little dirty which probably means I am not doing things quite right still so if someone actually knows whether I am doing this right or not cares to comment that would be great :)

Related

Nested subscription to messages on xamarin forms

I'm new with Xamarin forms and don't know how to deal with this case. I've tryed to implement it in several ways but with no success.
I have a page where when user makes an action (write a text on a text box and send it with enter key) my app must make some checkings. Depending on the result of the checks, it could be necessary to show a modal page with a list of item to select. Ones user makes the selection process must continue with other checks. And here is my problem, because in this next checkings I have to show another page. User must make a selection/enter some date, and then continue to complete the proccess, but this page is not appear.
I'm using the messagingCenter to subscribe to the modal pages. First modal page appears and makes the selection well. Second modal page is never shown and then proccess never complets.
Here is some of my code:
NavigationPage navigationPage = new NavigationPage(new ListItemsPage(products));
Navigation.PushModalAsync(navigationPage);
MessagingCenter.Subscribe<ListItemsPage, Guid?>(this, "Select product", (obj, item) =>
{
try
{
if (item != null)
{
product = products.SingleOrDefault(x => x.Guid == item);
if (product != null) ProcessLine(product);
}
}
catch(Exception ex)
{
throw ex;
}
finally
{
MessagingCenter.Unsubscribe<ListItemsPage, Guid?>(this, "Select product");
}
});
On ListItemsPage I have this code whe item is selected:
private void MenuItem_Clicked(object sender, EventArgs e)
{
// some logic...
Navigation.PopModalAsync();
MessagingCenter.Send(this, "Select product", SelectedGuid);
}
SelectedGuid is a Guid type data and when debbugin is well selected.
Problems comes when goes to ProcessLine method.
private void ProcessLine(Product product) {
// make some logic...
NavigationPage navigationPage = new NavigationPage(new ControlUnitsPage(model));
Navigation.PushModalAsync(navigationPage);
MessagingCenter.Subscribe<ControlUnitsPage, ControlUnits>(this, "Select units, date and lot code", (obj, item) =>
{
try
{
if (item != null)
{
_date = item.Date;
_code = item.Code;
_units = item.Units;
Save(productLine, product, _units, _date,_code);
}
}
catch(Exception ex)
{
throw ex;
}
finally
{
MessagingCenter.Unsubscribe<ControlUnitsPage, ControlUnits>(this, "Select units, date and lot code");
}
});
}
ControlUnitsPage has the same structure as the last one page. First makes a PopModalAsync and then sends the message sending an instance of ControlUnits type.
private void Button_Clicked(object sender, EventArgs e)
{
//some logic...
Item = new ControlUnits() { Date = DateField.Date, Code = CodeField.Text, Units = int.Parse(SelectedUnits.Value.ToString()) };
Navigation.PopModalAsync();
MessagingCenter.Send(this, "Select units, date and lot code", Item);
}
I think problem is in the order of invoking method but dont know what is the properly order because I am not able to understand how pushmodal, popmodal methods work, whether or not I should use await with it if after that comes a subscription. I really don't know and I need help, please.
Thank you so much!
your Send and Subscribe calls both need to use matching parameters
if Subscribe looks like this
MessagingCenter.Subscribe<ControlUnitsPage, ControlUnits>(this, "Select units, date and lot code", (obj, item) => ... );
then Send needs to match
MessagingCenter.Send<ControlUnitsPage, ControlUnits>(this, "Select units, date and lot code", Item);

Can't change RootFolder of ASPxFileManager control using Callback method

I'm using DevExpress 13.1 to develop my web application. My page has two controls: A Gridview which contains some item and a FileManager control (is children of a callbackpanel) which contain files information of item which is focused on that Gridview. I'm using Gridview's FocusRowChange client event to get data and send it back to server through an callback (of callback panel) to set new RootFolder value, but it not works. Tell me where's my wrong?
Thanks in advance.
My code:
ASPX file:
function myGridView_FocusRowChanged(s, e) {
var index = s.GetFocusedRowIndex();
var soCT = s.GetRowValues(index,"SoChungThu;SoHopDong",LoadFileList);
}
function LoadFileList(values) {
myCallbackPanel.PerformCallback("CHANGE_ROOT_FOLDER" + "," + values);
}
CS file:
protected void myCallbackPanel_Callback(object sender, DevExpress.Web.ASPxClasses.CallbackEventArgsBase e)
{
var vals = e.Parameter.Split(',');
if (vals[0].ToUpper() == "CHANGE_ROOT_FOLDER")
{
var path = vals[0] + "/" + vals[1];
myFileManager.Settings.RootFolder = path;
}
}
}
I found a solution.
Store folder path in Session, and assign it to RootFolder in Page_Load event.
Thank for reading.

Need help removing this fudged code... Binding user control on postback

As per the title, I know the code I've posted below is utter poo, this is why I need your help!
I've put way too many hours into this, and it's either down to inexperience, a bug or I've screwed up somewhere.
I have a user control with a view properties that access the ViewState and two user controls within that display the properties.
Within the page_load of the user control, depending on the value of some of the properties, it will toggle the visibility of the controls within:
public partial class PatientStatus : System.Web.UI.UserControl
{
public string PatientName { get { return ViewState["PatientName"] as string; } set { ViewState["PatientName"] = value; } }
public bool ClinicianView { get { return Convert.ToBoolean(ViewState["ClinicianView"]); } set { ViewState["ClinicianView"] = value; } }
public string RangeTitle { get { return ViewState["RangeTitle"] as string; } set { ViewState["RangeTitle"] = value; } }
public int? RangeLimitNormSys { get { return ViewState["RangeLimitNormSys"] as int?; } set { ViewState["RangeLimitNormSys"] = value; } }
public int? RangeLimitNormDia { get { return ViewState["RangeLimitNormDia"] as int?; } set { ViewState["RangeLimitNormDia"] = value; } }
protected void Page_Load(object sender, EventArgs e)
{
bool ispostback = IsPostBack;
if (ispostback && ((System.Web.UI.WebControls.Repeater)(this.Parent.Parent)).DataSource != null)
{
object itm = ((RepeaterItem)this.Parent).DataItem;
if (itm is AppointmentRow)
{
AppointmentRow row = itm as AppointmentRow;
PatientName = row.Name;
RangeTitle = row.Range;
RangeLimitNormDia = row.RangeLimitNormDia;
RangeLimitNormSys = row.RangeLimitNormSys;
ispostback = false;
}
else if (itm is ReadingRow)
{
ReadingRow row = itm as ReadingRow;
PatientName = row.Name;
RangeTitle = row.Range;
RangeLimitNormDia = row.RangeLimitNormDia;
RangeLimitNormSys = row.RangeLimitNormSys;
ispostback = false;
}
else if (itm is PatientRow)
{
PatientRow row = itm as PatientRow;
PatientName = row.Name;
RangeTitle = row.Range;
RangeLimitNormDia = row.RangeLimitNormDia;
RangeLimitNormSys = row.RangeLimitNormSys;
ispostback = false;
}
}
if (!ispostback)
{
if (!string.IsNullOrWhiteSpace(RangeTitle))
{
placeHolder.Visible = true;
literalNA.Visible = false;
}
}
}
}
Previously the Page_Load event simply contained:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
if (!string.IsNullOrWhiteSpace(RangeTitle))
{
placeHolder.Visible = true;
literalNA.Visible = false;
}
}
}
But on postback, the RangeTitle property was always null, so even when removing the isPostBack statement, it didn't work property.
The only way to resolve it, was to really fudge it by the first block of code.
On the Page_Load of the parent page, and on !isPostBack I'm calling a method that gets and binds data to the repeater. This works fine. But on a postback, i'm calling the same method and for some reason, the user control isn't populating.
Any idea's?
Merry Christmas
Gav
Edit
In response to #jwiscarson
I have a table that is generated via an ASP:Repeater and within the ItemTemplate, I have added a User Control which contains a PlaceHolder and a Literal. Also within the User Control is a number of Properties.
Then on databind I pass across a number of values to the user control (I've tried both OnItemDataBound and inline using Eval). Then on the User Control's Page_Load event, as per the second block of code above, I first check if it's a postback, if not, I then check to see if the Property RangeTitle has a value.
If RangeTitle does not have a value, I then hide the placeholder that contains HTML that would display the RangeTitle and show a literal that displays N/A.
When loading the page for the first time, (!isPostBack) it works fine. But as soon as I create a postback, the User Controls within the repeater all revert to N/A even when their RangeTitle properties had a value.
On debugging, I set a breakpoint in the Page_Load of the User Control. When I first load the page, I can see that my properties have been populated correctly. Then on postback, Page_Load is called on the UserControl and the properties are populated correctly, then Page_Load is called again, but this time, the properties are empty.
i.e.
!isPostBack
UserControl::Page_Load < Correct data
isPostBack
UserControl::Page_Load < Correct data
myButton_Click (bind new data)
UserControl::Page_Load < No data
To make things even more confusing. The method called within myButton_Click to bind the data, is the exact same method called in the Page_Load of the Page to populate the repeater on !isPostBack
Thanks ;)
I would check to make sure that DataItem is accessible in this function. I think you need to listen to the ItemDataBound event and perform this work in that event.
Beyond that, it's difficult to suggest anything else concrete. I don't really understand why you're doing what you're doing (if you have this information in a Repeater, why does it also need to be in ViewState?). If you could explain your rationale for doing this, it might help me and anyone else who visits this question. You say that you're just trying to show/hide some specific items on the page. This is pretty complicated code without a lot of justification for that complication.
As an aside: you really, really need to break this code down and think about what you're trying to accomplish. Here are a few suggestions:
Separate the scopes inside your if and else if statements into functions that return the data you need.
Do not include lines like ((System.Web.UI.WebControls.Repeater)(this.Parent.Parent)).DataSource != null in an if statement. Perform this cast separately and store it in a variable, or write a small function that checks this.
Statements like this.Parent.Parent and other references to Parent controls are code smells, in my opinion. Even on a normal page, this would be a code smell, but what exactly is this.Parent going to reference when you include it in a UserControl?

Can't submit changes to database or page controls

I've got a pretty simple page, consisting of two DropDownLists populated from the database, and a button. The purpose of the page is pretty simply to allow users to delete an entry from the database. When the button is clicked then a simple LINQ query is executed to delete the intended target, and remove the entry from the dropdownlists, but it doesn't work unless the response is redirected within that function, even if SubmitChanges() was called. Why would this happen?
Edit: Code
protected void Page_Init(object sender, EventArgs e)
{
var result = Database.DB.Data.GetTable<Database.tbl_module_>().Where(module => module.deptCode == ((User)Session["user"]).deptCode);
foreach (var row in result)
{
this.listModuleCode.Items.Add(new System.Web.UI.WebControls.ListItem(row.code));
this.listModuleTitle.Items.Add(new System.Web.UI.WebControls.ListItem(row.title));
}
}
protected void Delete_Click(object sender, EventArgs e)
{
var DB = Database.DB.Data;
var table = DB.GetTable<Database.tbl_module_>();
var result = table.Where(module => module.deptCode == ((User)Session["user"]).deptCode && module.code == listModuleCode.SelectedItem.Text);
listModuleCode.Items.Remove(listModuleCode.SelectedItem);
listModuleTitle.Items.Remove(listModuleTitle.SelectedItem);
table.DeleteAllOnSubmit(result);
DB.SubmitChanges();
Response.Redirect("deletemodule.aspx"); // redirect to this page
}
We need to see your code to help more probably. However:
You need to make sure it knows to delete on submit:
var q = db.Customers.Where(c => c.CustomerID == 2).Single();
db.Customers.DeleteOnSubmit(q);
db.SubmitChanges();
Don't forget you can pass straight SQL to the object:
db.ExecuteCommand("DELETE FROM Customers WHERE ID = 2");
Which you might think is easier.

Scroll to top of page after async post back

So I need to scroll to the top of the page after an async post back in an asp.net update panel.
The code I used was this:
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestEventHandler);
function EndRequestEventHandler(sender, args)
{
scrollTo(0,0);
}
However, I only want this to be run when I click on a certain button which causes the async postback.
How do I wire this event up in my code behind button event?
Any help would be appreacited, thanks!
My quest for a solution is finally over. This question was part of the help, and the rest I found here.
Had to override ASP.NET Ajax's behaviour of memorizing the scroll position:
<script type="text/javascript">
var manager = Sys.WebForms.PageRequestManager.getInstance();
manager.add_beginRequest(beginRequest);
function beginRequest()
{
manager._scrollPosition = null;
}
</script>
And then use the bit of code in the answer here, on the codebehind of the page I wanted to scroll to the top:
ScriptManager.RegisterStartupScript(this, typeof(MyControl), "someText", "window.scrollTo(0, 0)", true);
I used Farinha's answer (thanks!) and changed it slightly so I could just call the method any place I wanted to scroll to the top, but maintain the scroll position otherwise.
public static void ScrollToTop(int intPosY = 0)
{
string strScript = #"var manager = Sys.WebForms.PageRequestManager.getInstance();
manager.add_beginRequest(beginRequest);
function beginRequest()
{
manager._scrollPosition = null;
}
window.scroll(0," + intPosY.ToString() + ");";
Page pagCurrent = GetCurrentPage();
ScriptManager.RegisterStartupScript(pagCurrent, pagCurrent.GetType(), string.Empty, strScript, true);
return;
}
public static Page GetCurrentPage()
{
return (HttpContext.Current.Handler as Page);
}
Try this:
protected void myButon_Click(object sender, EventArgs e)
{
ScriptManager.RegisterStartupScript(this, typeof(MyControl), "someText", "alert('!');", true);
}
Improving on the answers of #Farinha and #Bradford Scott, the code can be simplified to this:
var script =
"Sys.WebForms.PageRequestManager.getInstance()._scrollPosition = null; " +
"window.scrollTo(0, 0);"
ScriptManager.RegisterStartupScript(this, GetType(), "key", script, true);
I'm actually not even sure why their scripts work since they add the reset of the Sys.WebForms.PageRequestManager's _scrollPosition as an add_beginRequest handler, and here we are actually returning from the request.
Anyway, resetting the _scrollPosition right before making your own scrollTo() call definitely works.

Resources