In my current design approach I am running into a case where I'm having to perform an extra Database read and Databind on controls, only to overwrite them later on in the page lifecycle. The end result to the user is the same, but behind the scenes I'm doing two unnecessary and costly operations.
The search button is on the Master page. All the display controls (gridviews/tables/labels/etc) are on the content pages. The database call and databinding must be done in the Page_Init() method (most of the controls being used must be bound here).
When the user searches for an item, I save his search input in the session to persist it as being actively viewed across all the other content pages. So that when any content page is initialized, I check to see if they are actively viewing an item, and if so display his details.
// Master Page
protected void BtnSearch_OnClick(object sender, EventArgs e)
{
MySession.Current.ItemName = TxtItem.Text.Trim();
Server.Transfer("~/default.aspx");
}
// Content Page
protected void Page_Init(object sender, EventArgs e)
{
// If they're actively viewing an item, display its info
bool HasActiveItem = string.IsNullOrEmpty(MySession.Current.ItemName) ? false : true;
if (HasActiveItem)
{
// Makes one DB call to get all info;
// Binds all that info to GridViews/tables/labels on the page
BindAllDataControls(MySession.Current.ItemName);
// Display
DisplayItemDetails();
}
}
Here's the Issue: Say the user is currently viewing an Item = Boots. This value is saved in the session. Now the user searches for Item = Shirts and clicks the search button.
When the content page loads, he checks if there's anything in the session, there is but it's Item = Boots. It performs the unnecessary database/databind calls. Then, the BtnSearch click event triggers, we load the new Item = Shirts value into the session, and start the life cycle over. This lifecycle is good to go.
How can I get rid of that extra processing? Is the approach wrong?
I've thought about performing Page.IsPostback() and Page.IsCallback() logic during the content page initialization, but there are multiple other controls that cause postbacks and callbacks in the real web application. Therefore, I don't think I can get enough info from this to make the determination to skip or not. I've thought about wrapping the entire part of the page that contains all the GridViews/tables/labels/etc. in an ajax Callback, but I don't think that's a good approach. I've thought about sending an ajax call back to the server during the BtnSearch click that sets a flag. Then during load, we read that flag to skip or not, but there's no guarentee that that ajax call will process before the search BtnClick event.
Any ideas? Or should I just eat these extra calls and be done with it? There's got to be another way.
The most simple solution I see here is to make the check of search text change direct on PageInit and not on the button call.
// Master Page
protected void BtnSearch_OnClick(object sender, EventArgs e)
{
// remove that lines
// MySession.Current.ItemName = TxtItem.Text.Trim();
// Server.Transfer("~/default.aspx");
}
// Content Page
protected void Page_Init(object sender, EventArgs e)
{
// direct add here your variable on session
var vSearchText = TxtItem.Text.Trim();
if(!string.IsNullOrEmpty(vSearchText))
MySession.Current.ItemName = vSearchText ;
// ------------ rest of your code ---------------
// If they're actively viewing an item, display its info
bool HasActiveItem = string.IsNullOrEmpty(MySession.Current.ItemName) ? false : true;
if (HasActiveItem)
{
// Makes one DB call to get all info;
// Binds all that info to GridViews/tables/labels on the page
BindAllDataControls(MySession.Current.ItemName);
// Display
DisplayItemDetails();
}
}
Now to tell you the truth, the Server.Transfer is not good idea, what I do is that I use parameter in the URL, to include the search string from the input of the user, so when the user is add something for search I create the url as:
http://www.mydomain.com?q=test
and then I read the q and use it to fill the search box and make the search. This way you also have a SEO friendly search, the user can save his search and you avoid the server transfer that have other issues.
Related
I'm Not good at English, Thanks in advance.
I have problem that i just cant figure out right now. I am trying to develop a UWP and im stuck implementing this functionality.
I have a Header(Navigation view) in NavPage, such as this.
As you know, The Header and The Frame in one page, frame load a View, When I click "Up" or other button(appbarButton), I want pass a Info to Frame's current page(is MainPage).
At frist, I want invoking a current page method by appBarbutton, need static.No
then, I want pass a info to that page, and handle it in OnNavigatedTo(NavigationEventArgs e)
here is problem.
this is code:
private void BackHome(object sender, RoutedEventArgs e)
{
ContentFrame.Navigate(typeof(MainPage), "*BackHome");
}
private void BackUp(object sender, RoutedEventArgs e)
{
ContentFrame.Navigate(typeof(MainPage), "*BackUp");
}
I try two buttons.
But...when I click this appbar button,it is work, info pass success, but because Navigate() method, the frame's current page is reload, Parameters passed before useless
How pass info and don't reload?
any ideas? thanks every body.
Navigate might actually not be the best suited method to call since what it actually does is telling your frame to read the content of the specified Page, something that you only want to do when your specified page is different from the one you're currently in.
Edit 1.
Do you really need to pass information to the page for the situations where you're going to keep using the same content as your frame? If not, and just in order to stop the reloading for those situations, just check if you are actually calling the Navigate method to another page.
if(ContentFrame.SourcePageType != typeof(MainPage))
ContentFrame.Navigate(typeof(MainPage), "*BackUp");
In my page.xaml, i have hooked the Back hardware button as this:
Windows.Phone.UI.Input.HardwareButtons.BackPressed += HardwareButtons_BackPressed;
and implement the method:
private void HardwareButtons_BackPressed(object sender, Windows.Phone.UI.Input.BackPressedEventArgs e)
{
// Handle the Virtual Hardware Button: Back,
// When user taps it, I need to get the previous page name.
System.Diagnostics.Debug.WriteLine("CurrentSourcePageType = " + Frame.CurrentSourcePageType.FullName);
System.Diagnostics.Debug.WriteLine("Back button is pressed...");
}
But here, the Frame.CurrentSourcePageType.FullName is already the back navigated page name, how can i get the previous page name?
Maybe I need to describe my question better here:
Suppose I have 2 pages A and B,
Through page A I navigate to page B, in the page B I have done something, then I only want to use back button to trigger some customize action (I don't want to add button in my Page), but this action needs to get the page B's name first.
First of all, HardwareButtons.BackPressed is an app-wide event, so it's not a good idea to subscribe to it in Page, unless you do it very carefully and remember to unsubscribe when not needed any more. It's worth also to mention that you should pay special attention if anything has been susbscribed to this event before your Page - for example in app's constructor or anywhere else. (some typical places are shows here in Igrali's answer).
To do what you want you can for example either build an app-wide event that will be responsible for an action, or use the code as shown below (following Rob Caplan's answer). In both cases the name (type) of your previous page you can get from Frame.BackStack.
RelayCommand myGoBackCommand;
public BasicPage1()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
this.navigationHelper.SaveState += this.NavigationHelper_SaveState;
myGoBackCommand = new RelayCommand(() => GoBackAction());
this.navigationHelper.GoBackCommand = myGoBackCommand;
}
private void GoBackAction()
{
//print previous page name before going back
Debug.WriteLine(Frame.BackStack.Last().SourcePageType);
if (navigationHelper.CanGoBack())
navigationHelper.GoBack();
}
Note: To make the above code work, delete all other subscriptions to HardwareButtons.BackPressed and make your pages BasicPages (use navigation helper and so on). Of course you can have other subscriptions, but you need to handle them carefully, in most cases these events are fired with order they were subscribed.
Remark - in case you need also handle the case when user navigates back from other Page and you want the name of that page - then recognize type of navigation in OnNavigatedTo (forward/back navigation) and then read suitable Frame's stack - BackStack or ForwardStack.
The code above is using BasicPage template - if you don't have mentioned classes, then add to your project a new BasicPage and VS should ask you if you want to add the common files (NavigationHelper ans so on).
When a Page is navigated to in silverlight you can override this method.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
}
The NavigationEventArgs has a NavigationMode enumeration which is defined as
public enum NavigationMode
{
New = 0,
Back = 1,
Forward = 2,
Refresh = 3,
}
But calling e.NavigationMode always throws a NotImplementedException
Is there a way in silverlight to detect a page is being navigated to because the user hit the forward/back browser button.
What I am trying to achieve is some kind of state that can be preserved when the user hits the back button.
For example assume you have a customer page which is showing a list of customers in a datagrid. The user can select a customer and there is a detail view which shows all the orders for that customer. Now within an order item you can click a hyperlink link that takes you to the shipping history of the order which is a separate page. When the user hits the back button I want to go back to the customers page and automatically select the customer he was viewing. Is this possible at all ?
I also tried out the fragment navigation feature
NavigationService.Navigate(new Uri("#currentcustomerid="
+ customer.Id.ToString(), UriKind.Relative));
when the customer selection changes but this adds too many items to the history when the user clicks various customers on the customer page.
EDIT
There is also an method you can override
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
}
which is the same as handling the NavigationService.Navigating event as indicated by BugFinder's answer. In this method e.NavigationMode always returns New when when you hit the Back or Forward Button. The only time this method returns Back is when you explicitly call NavigationService.GoBack()
The
public enum NavigationMode
{
New = 0,
Back = 1,
Forward = 2,
Refresh = 3,
}
applies to the Navigating event..
if I do
_ns.Navigating += ns_Navigating;
void ns_Navigating(object sender, NavigatingCancelEventArgs e)
{
if (SecurityCheck(e.Uri.OriginalString)) return;
e.Cancel = true;
ShowError("You are not authorised to view this page");
}
I can see there that e.NavigationMode is set. You could do your test there?
I don't think there are any easy ways to do it out of the box, as far as I know.
What you are trying to achieve can be easily done using a framework I created at
http://ultimateframework.codeplex.com
What I have done is to mesh the silverlight navigation frame and prism navigation together, so you will need unity and prism and mvvm friendly.
What you want to achieve can be done using the framework in the following ways
1) Implement IsNavigationTarget and returns true --> which will keep the same instance when navigating back, therefore, keeping the selection/selected item.
2) Access the onnavigatedto's journal to track where you came from, say /item/1 was the previous stack, so you know back button has been pressed from item 1.
3) You can even implement your own back/forward/refresh within the custom control provided for achieving the same result (not in codeplex yet)
I actually use it for production code at work, and I created it, so please feel free to try it out. Note that the version on there is buggy, and I have yet to have time to release our latest build, but should you require it, I will update it for you :), just pm me.
Set a variable in the usercontrol containing the contentframe which indicates what customer is active.
Add a handler for the contentframe's Navigated event in the usercontrol. Use this to check the variable indicating what customer is active (if the variable is not null), and to select the customer.
This might be what you are looking for:
http://jakkaj.wordpress.com/2008/09/08/control-silverlight-by-using-browser-back-and-foward-buttons/
I am currently working on an app for WP7 for my university, and need a temporary solution to a problem. Now this solution is, that I will be loading a webpage using the web browser control for WP7. For example: http://m.iastate.edu/laundry/
Now as you see on the webpage, there are certain elements I want to hide, for example the back button. For now, what I have done to handle the back button is something like this:
private void webBrowser1_Navigating(object sender, NavigatingEventArgs e)
{
// Handle loading animations
// Handle what happens when the "back" button is pressed
Uri home = new Uri("http://m.iastate.edu/");
// The the current loading address is home
// Cancel the navigation, and go back to the
// apps home page.
if (e.Uri.Equals(home))
{
e.Cancel = true;
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
}
}
Now that works beautifully, except for the part that there is a back button on the hardware.
So my second option is to completely hide the back button ONLY on that page, and not its children. So not on http://m.iastate.edu/laundry/l/0
I am still debating on just parsing the data and displaying it in my own style, but I'm not sure if that's completely needed seeing how the data needs constant internet service and is already in a well-put format. Plus, I feel like that would be a waste of resources? Throw in your opinions on that too :)
Thanks!
You should inject a script in the page with InvokeScript.
Here is the kind of Javascript code you need to remove the back button:
// get the first child element of the header
var backButton = document.getElementsByTagName("header")[0].firstChild;
// check if it looks like a back button
if(backButton && backButton.innerText == "Back") {
// it looks like a back button, remove it
document.getElementsByTagName("header")[0].removeChild[backButton];
}
Call this script with InvokeScript:
webBrowser1.InvokeScript("eval", "(function() { "+ script +"}()");
Warning: IsScriptEnabled must be set to true on the web control
If the removal of the back button depends of the page, just test the navigating URI in C# and inject the script if neeeded.
I am trying to create a popup which will be used to select a month/year for a textbox. I have kind of got it working but however when I try and read from the textbox when I Submit the form it returns an empty string. However visually on the page I can see the result in there when I click the Done button which can be seen in the screenshot.
http://i27.tinypic.com/2eduttx.png - is a screenshot of the popup
I have wrapped the whole textbox/popup inside a Web User Control
Here is the code of the control
Code Behind
ASP Page
and then read from the Textbox on the button click event with the following
((TextBox)puymcStartDate.FindControl("txtDate")).Text
Any suggestions of how to fix the problem?
You may need to read the form posted value rather than the value from the view state. I have the following methods in my code to handle this.
The below code just grabs the values in the request headers (on post back) and sets/updates the controls. The problem is that when using the ASP.NET Ajax controls, it doesn't register an update on the control, so the viewstate isn't modified (I think). Anyways, this works for me.
protected void btnDone_Click(object sender, EventArgs e)
{
LoadPostBackData();
// do your other stuff
}
// loads the values posted to the page via form postback to the actual controls
private void LoadPostBackData()
{
LoadPostBackDataItem(this.txtYear);
LoadPostBackDataItem(this.txtDate);
// put other items here if needed
}
// loads the values posted to the page via form postback to the actual controls
private void LoadPostBackDataItem(TextBox control)
{
string controlId = control.ClientID.Replace("_", "$");
string postedValue = Request.Params[controlId];
if (!string.IsNullOrEmpty(postedValue))
{
control.Text = postedValue;
}
else
{
control.Text = null; // string.Empty;
}
}