Maintaining focus across post backs is an apparently difficult task. Searching Google, you will find a ton of people that desire the same thing, but all hook it up differently, and mostly, custom-ly. I would like to avoid a custom implementation, especially if there's a way it's supported by .NET. Only after some very deep searching, did I come across PostBackOptions.TrackFocus, mentioned quietly in another stack overflow post. According to MSDN:
Gets or sets a value indicating whether the postback event should return the page to the current scroll position and return focus to the current control."
Holy crap, this is supported by .NET 4? AWESOME. But we have a ton of custom controls, how does .NET know how to set the focus on a control? I have no idea. Looking a the MSDN documentation for System.Web.UI.Control, there's an interesting method:
public virtual void Focus()
"Use the Focus method to set the initial focus of the Web page to the
control. The page will be opened in the browser with the control
selected."
Alright, clearly overridable. But what is the recommended method of doing so? It returns void. No examples. Unable to find any examples of people overriding this method in their implementations. However, after overriding it and doing nothing more than throwing an exception, it becomes evident that this is not how ASP.NET gets focus on a control that had focus before the post back: it never gets called.
After a ton of debugging using Firebug, I have found that enabling PostBackOptions.TrackFocus works! Sometimes. It is apparent that the focus of a control is only maintained when the control calls the __doPostBack JavaScript method. Other controls that launch a PostBack (when pressing enter inside the control), call WebForm_OnSubmit(), which doesn't update the ASP hidden field __LASTFOCUS. __doPostBack calls WebForm_OnSubmit() after setting the hidden fields.
This is where I'm currently stuck. It's looks as if I need to get everything to call __doPostBack, no matter what. There's very, very little documentation on the use of TrackFocus. So does anyone have any tips from here?
I've been maintaining focus accross postbacks using the method in this article:
(ie: store focus in __LASTFOCUS hidden field on field enter event clientside for all controls)
http://www.codeproject.com/KB/aspnet/MainatinFocusASPNET.aspx
If you've gotten as far as having __LASTFOCUS show up on the page, this should get you most of the rest of the way...
Note: It'd be nice to find a way to keep the extra javascript from bloating __VIEWSTATE for example.
It was working pretty well for me until I figured out that some of my pages included the hidden __LASTFOCUS field and some of my pages didn't. (That's what prompted me to search around and find your question) Now I'm just trying to figure out how to make sure __LASTFOCUS always shows up on every page I want to keep track of focus on... (Looks like I'll have to open a separate question about it)
Here is what I just did. Assuming you have a handler in your code behind that takes care of the event and has a signature like this:
protected void myEventHandler(object sender, EventArgs e)
You can use this line of code to restore focus back to the sending object:
ScriptManager.RegisterStartupScript((WebControl) sender, sender.GetType(), "RestoreFocusMethod", "document.getElementById(\"" + ((WebControl) sender).ClientID + "\").focus();", true);
just using the Focus() method of the sending control will reposition the page (if you are scrolled down a bit), but this works beautifully. And if you have specific handlers for your control, you can just use the control itself rather than casting the sender to a WebControl, like this:
protected void CityListDropDown_SelectedIndexChanged(object sender, EventArgs e)
{
...
ScriptManager.RegisterStartupScript(CityListDropDown, CityListDropDown.GetType(), "CityDropDownRefocus", "document.getElementById(\"" + CityListDropDown.ClientID + "\").focus();", true);
}
Related
I Have a Page Containing some controls, this page will be loaded into a ContentControl in a Window.
Now, How can i limit the focus cycle in my Page? I don't want to pass the focus to out of Page after pressing TAB in last item of my page.
As a brief, How Can I determine the next focus control and change it
It's always difficult to fully understand what a question author wants when they don't bother to provide you with more than a few hastily typed lines. However, as far as I understand your problem, it seems that you have some problem with focusing in your WPF Application.
The first thing to do is to direct you to the Focus Overview page on MSDN, where you can find out about the different focus types used in WPF. In particular, please pay attention to the Navigating Focus Programmatically section which discusses the TraversalRequest Class that can help developers to move focus programmatically.
You should also pay careful attention to the KeyboardNavigation section that discusses the KeyboardNavigation Class. This class contains some properties that enable you to define how the Tab key works in various scenarios, so this may be what you're after. It is used like this (from the last linked page on MSDN):
KeyboardNavigation.SetTabNavigation(navigationMenu, KeyboardNavigationMode.Cycle);
For future reference, you will get quicker and more accurate answers if you provide clear questions that include all of your requirements at the time of posting.
Have you tried looking at the FocusLost event - you might possibly be able to just refocus the page control with an event handler.
private void Page_LostFocus(object sender, System.Windows.RoutedEventArgs e)
{
((UIElement)sender).Focus();
}
On our ASP.Net 4.0 C# web app I get the error above in the subject line during debug.
I'm not really doing anything funky with dynamic controls on the page as some search results indicated could have been the source of the problem.
The page has 2 ajax:CalendarExtenders and has been working fine for a while.
Although its probably related, I can see how really but this is what I was currently working on when the error came up.
We have a control on the page that raises an event on an autorefresh feature. [Its a map control].
My page subscribes to that event and upon doing so DataBinds an asp:GridView. We need to bind the data in the grid every time to ensure the grid and the map control are in sync. [Its a vehicle tracking page]
The error occurs on the DataBind command.
I have removed the Extenders only to come up with the same error.
The databind is straightforward but I'm sure the error resides elsewhere. I'll include it anyway.
this.SearchGrid.DataSource = resultsWithMetrics;
this.SearchGrid.AllowPaging = true;
this.SearchGrid.PageIndex = this.SearchGridPager.CurrentPage;
this.SearchGrid.AllowPaging = this.SearchGridPager.PageSize > 0;
this.SearchGrid.PageSize = this.SearchGridPager.PageSize > 0 ? this.SearchGridPager.PageSize : this.SearchGrid.PageSize;
this.SearchGrid.DataBind();
Maybe its simply not understanding the page life cycle that has tripped me up, anyway would be glad of some help.
Use the following code in your usercontrol or page control. It will work if you are using any ajax control.
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (this.DesignMode == true)
{
this.EnsureChildControls();
}
this.Page.RegisterRequiresControlState(this);
}
The issue was that on row databind the extender controls were added to each row and it was these extenders that caused the issue.
The event that triggered the the rebind came from a user control that used callbacks to communicate to the server and subsequently raise the event which probably arrived too late anyway in the page lifecycle.
Without fully understanding what was going on it all smelt a bit dodgy and I have changed the structure of the app a bit.
What is the easiest way to create code-behind (webforms) event handlers for say a button from HTML source view?
In VB.NET it is quite easy to switch to code behind page and use the object and events combo boxes along the top to select and create.
In c# those are missing (and I really don't like design view).
Make sure the Properties window is open.
Click anywhere in the element in source view.
Click the lightning symbol (events) in the Properties window.
Find the event you want to create a handler for.
Double click it.
I agree that this is less trivial with C# then with VB. My personal preference is to simply add a function with the following signature (always works):
protected void MyButtonName_Clicked(object sender, EventArgs e)
{
Button btn = (Button) sender; // remember, never null, and cast always works
... etc
}
Then, inside the code view of the HTML/ASP.NET part (aka the declarative code) you simply add:
<asp:Button runat="server" OnClick="MyButtonName_Clicked" />
I find this faster in practice then going through the several properties menus, which don't always work depending on focus and successful compile etc. You can adjust the EventArgs to whatever it is for that event, but all events work with the basic signature above. If you don't know the type, just place breakpoint on that line and hover over the e object when it breaks to find out the actual type (but most of the time you'll know it beforehand).
After a few times doing this, it becomes a second nature. If you don't like it, wait a moment for VS2010, it's been made much easier.
Note: both VB and C# never show the objects or events of elements that are placed inside naming containers (i.e., GridView, ListView). In those cases, you have to do it this way.
I have subclassed a Treeview and on instantiation it loads a new ImageList (and the associated Images).
Whenever I switch to the designer view, it's also trying to run this code, however the images aren't in the designer's path, so it crashes. I ended up putting in a hack to see if the current directory is "Visual Studio", then do nothing... but that's so ugly.
I find this happening for other things. If a control is trying to use objects during load/initalization that are only available while the program is running, then the Design View cannot bring up the control.
But is there a way to get around this?
I guess what I'm hoping for is having a try/catch for the Designer (only) with the ability to ignore a few errors I know will be happening (like FileNotFoundException, etc.).
Thanks
Everything that inherits from System.Windows.Forms.Control has a DesignMode property that returns a boolean indicating if you are in design mode or not. You could use this to determine when to/when not to load external resources.
Usually it is better to move the loading of these resources to an override of OnLoad as they are rarely required directly at construction. This fixes the issue you are seeing and means that only trees which get displayed at least once will perform these additional resource loading steps.
Otherwise, you can just exclude these steps during design time by checking the DesignMode property and acting accordingly.
This is a fine pattern to use if you're making a control library with a sample of images when shown in the designer or hook ins to other designer features but as a pattern for development I'm not sure it's very effective.
I would suggest shifting your "business logic" (in this case your loading of certain images into a treeview) outside of the bounds of your treeview control. In your case I would place the logic within the Load event of the form that the control is inside:
public void Load(object sender, EventArgs e)
{
string path = "c:\somePath\toAwesome\Images";
myFunkyTreeView.AddImages(path);
}
For larger apps I personally think you want to shift the logic even out of the forms themselves, but this is debatable measure as it requires additional plumbing as a trade-off for the flexibility this provides.
Thanks for pointing me in the right directioon guys.
I had tried registering to the OnLoad event, but that event is triggered when the Design View comes up, so that didn't quite work for me (am I doing something wrong?).
Anyway, I looked a bit more into the DesignMode property. It can only work for Controls, and sometimes your object may not even be a control.
So here's the answer I prefer:
if (LicenseManager.UsageMode == LicenseUsageMode.Designtime) {
// design-time stuff
} else {
// run-time stuff
}
Found it here.
Outline
OK, I have Google'd this and already expecting a big fat NO!! But I thought I should ask since I know sometimes there can be the odd little gem of knowledge lurking around in peoples heads ^_^
I am working my way through some excercises in a book for study, and this particular exercise is User Controls. I have cobbled together a control and would like to set the DefaultEvent for it (having done this for previous controls) so when I double-click it, the default event created is whatever I specify it to be.
NOTE: This is a standard User Control (.ascx), NOT a custom rendered control.
Current Code
Here is the class & event definition:
[System.ComponentModel.DefaultEvent("OKClicked")]
public partial class AddressBox : System.Web.UI.UserControl
{
public event EventHandler OKClicked;
Current Result
Now, when I double click the the control when it is on a ASPX page, the following is created:
protected void AddressBox1_Load(object sender, EventArgs e)
{
}
Not quite what I was expecting! So, my question:
Is it possible to define a DefaultEvent for a UserControl? Is it a hack? If it's [not] supported, is there a reason?
Side Note: How do we put underscores in code? I cant seem to put and escape char in?
Here is a possible answer, without testing (like martin did).
In reflector, you will see that the DefaultEventAttribute allows itself to be inherited.
In reflector, you see that the UserControl class has it's default event set to the Load event.
So the possible reason is that even though you are decorating your user control with the default event of OKClick, VS might still be thinking that the default event is load, as it's being inherited from UserControl whose default event is Load.
Just a high level guess at what might be happening.
OK, I checked this out, Inheriting from WebControl rather than UserControl.. All worked fine.
Looks like Darren Kopp takes the crown for this one! Thanks for the input!