Creating a MouseEnter Event for every Button in a window - c#

I've been working on an application months ago, my friend and i wanted to share this application with other friends, and i need to display some help to make the application easier because it was designed only for both of us.
The idea that came out is to display help on a text Block every time a hover event is popped from a button. So we added a textBlock. Now the problem that we still facing is how to create the Hover Event for every button in our Main Window, there are a lots of buttons in this window, So we can't add an event to every button in the XAML code.
What i am expecting from this answer is a way to add Hover Event to all buttons in the main window Programmatically ?
EDIT: after some googling and help, i can do the following :
foreach (UIElement btn in GeneralMenuGrid.Children)
{
if (btn is Button)
{
Button currentButton = (Button)btn;
currentButton.Content = "test";
}
}
This is just a test that will allow all the buttons in the GeneralMenuGrid control to have a content : test, now the problem again is that i have nested controls in this grid, how can i reach them?
EDIT : after years of goggling i got to loop through all the buttons in my window with this :
public static void EnumVisuals(Visual argVisual, Window currentWindow)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(argVisual); i++)
{
Visual childVisual = (Visual) VisualTreeHelper.GetChild(argVisual, i);
if (childVisual is Button)
{
var button = childVisual as Button;
button.MouseEnter += AllButtonsOnMouseEnter;
}
EnumVisuals(childVisual, currentWindow);
}
}
now in the AllButtonsOnMouseEnter function, i can't access a button, i made it public... i can't access it from this class, how can i send the window with the event arguments?

You wrote, "there are a lots of buttons in this window, so we can't add an event to every button in the XAML code." But you can - just add a style that applies to all buttons:
<Style TargetType="{x:Type Button}">
<EventSetter Event="MouseEnter" Handler="Button_MouseEnter"/>
</Style>
I don't know how you intend to get the help text relevant to each Button, but it's easy to store it in the Button's Tag:
<Button Tag="Instantly move from one place to another.">
Teleport
</Button>
Then write an event handler that shows the help in your TextBlock:
private void Button_MouseEnter(object sender, MouseEventArgs e)
{
Button button = sender as Button;
textblock_that_shows_help.Text = button.Tag;
}

I've created an extension method that does this, adapted from here: Find all controls in WPF Window by type
Put this class somewhere in your project:
public static class VisualTreeSearch
{
/// <summary>
/// Finds all elements of the specified type in the <see cref="System.Windows.DependencyObject"/>'s visual tree using a breadth-first search.
/// </summary>
/// <typeparam name="T">The type of element to search for.</typeparam>
/// <param name="root">The object to search in.</param>
/// <returns>A list of elements that match the criteria.</returns>
public static IEnumerable<T> Find<T>(this DependencyObject root) where T : DependencyObject
{
return root.Find<T>(false, true);
}
/// <summary>
/// Finds all elements of the specified type in the <see cref="System.Windows.DependencyObject"/>'s visual tree.
/// </summary>
/// <typeparam name="T">The type of element to search for.</typeparam>
/// <param name="root">The object to search in.</param>
/// <param name="depthFirst">True to do a depth-first search; false to do a breadth-first search</param>
/// <param name="includeRoot">True to include the root element in the search; false to exclude it</param>
/// <returns>A list of elements that match the criteria.</returns>
public static IEnumerable<T> Find<T>(this DependencyObject root, bool depthFirst, bool includeRoot) where T : DependencyObject
{
if (includeRoot)
{
var depRoot = root as T;
if (depRoot != null)
yield return depRoot;
}
var searchObjects = new LinkedList<DependencyObject>();
searchObjects.AddFirst(root);
while (searchObjects.First != null)
{
var parent = searchObjects.First.Value;
var count = VisualTreeHelper.GetChildrenCount(parent);
var insertAfterNode = depthFirst ? searchObjects.First : searchObjects.Last;
for (int i = 0; i < count; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
var depChild = child as T;
if (depChild != null)
yield return depChild;
insertAfterNode = searchObjects.AddAfter(insertAfterNode, child);
}
searchObjects.RemoveFirst();
}
}
}
To use the extension method, write the following in your window class (as an example). This code loops through all children, grandchildren, etc. of this (which should be your Window in this case) and finds all Button elements.
foreach (var button in this.Find<Button>())
{
button.MouseEnter += button_MouseEnter;
}
...
private void button_MouseEnter(object sender, MouseEventArgs e)
{
// Do stuff
}

after a lot of googling and chat help... i finally did it, maybe other will be interested this is how i proceeded :
i created a static recursive function that will get all the buttons in the window:
public static void EnumVisuals(Visual argVisual, Button toModifyButton)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(argVisual); i++)
{
Visual childVisual = (Visual) VisualTreeHelper.GetChild(argVisual, i);
if (childVisual is Button)
{
var button = childVisual as Button;
button.MouseEnter += (sender, eventArgs) =>
{
toModifyButton.Content = "Get the Help text from database if you want";
};
}
EnumVisuals(childVisual, toModifyButton);
}
}
why do you send a Button :
i need to write the help in a button, and the only way i found to access it's content property is to send it via this function and of course make it public.
Hope you'll find this helpfull.

Related

How can you store a Control in a class and reuse it in a binding?

I am trying to reuse NumberBoxes for a GridView because having the NumberBoxes embedded directly in the GridView data template causes undesirable behavior, while reusing them does not. The problem is that I keep getting exceptions. They say "No installed components were detected" on the following line (templateRoot.FindName("NumberBox") as GridViewItem).Content = item.NumberBox; in this context
/// <summary>
/// The callback for updating a container in the GridView named CardGridView.
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
private void UpdateGridViewContainer(ListViewBase sender, ContainerContentChangingEventArgs args)
{
if (args.Phase == 1)
{
Grid templateRoot = args.ItemContainer.ContentTemplateRoot as Grid;
CardItem item = args.Item as CardItem;
(templateRoot.FindName("NumberBox") as GridViewItem).Content = item.NumberBox;
TypedEventHandler<NumberBox, NumberBoxValueChangedEventArgs> handler =
(box, args) =>
{
if (!double.IsNaN(args.NewValue))
{
_viewModel.ChangeCount(args, item);
}
};
item.SetHandler(handler);
}
}
The exception is thrown when the Page that contains the GridView is left and renavigated to. I have tried nulling out the NumberBoxes when the page is left, but that did not work. Well, it appeared to before the issue cropped up again.
This is the code that nulls out the NumberBoxes
/// <summary>
/// Creates new NumberBoxes for when this Page is loaded again.
/// </summary>
private void ResetNumberBoxes()
{
foreach (CardItem card in CardGridView.Items.Cast<CardItem>())
{
card.ResetNumberBox();
}
CardGridView.ItemsSource = null;
CardGridView.Items.Clear();
}
ResetNumberBox is just setting the NumberBox to null and assigning a new one.
The exception details
System.Runtime.InteropServices.COMException
HResult=0x800F1000
Message=No installed components were detected. (0x800F1000)
Source=WinRT.Runtime
StackTrace:
at WinRT.ExceptionHelpers.<ThrowExceptionForHR>g__Throw|20_0(Int32 hr)
at ABI.Microsoft.UI.Xaml.Controls.IContentControlMethods.set_Content(IObjectReference _obj, Object value)
An update. I have removed the GridViewItem control from the DataTemplate and tried doing the following with the same result
CardItem item = args.Item as CardItem;
Grid.SetColumn(item.NumberBox, 1);
item.NumberBox.HorizontalAlignment = HorizontalAlignment.Center;
item.NumberBox.VerticalAlignment = VerticalAlignment.Center;
(templateRoot.FindName("GridViewTemplate") as Grid).Children.Add(item.NumberBox);
I also examined a heap dump right before the line that throws, and there was only one instance of the Grid, and the NumberBox had a parent of null.

ListView drag&drop re-order for ListView in "details" view code usage

This questions has nothing in common with Reorder / move / dragdrop ListViewItems within the same ListView Control in C# Windows Forms because it will only work for large/small icons view mode!
I found some nice little code here:
http://snipplr.com/view/33427/
using System.Drawing;
using System.Windows.Forms;
namespace System.Windows.Forms // May need to set to something else
{
/// <summary>
/// A ListView with DragDrop reordering.
/// <see cref="http://support.microsoft.com/kb/822483/en-us"/>
/// </summary>
public class ListViewWithReordering : ListView
{
protected override void OnItemDrag(ItemDragEventArgs e)
{
base.OnItemDrag(e);
//Begins a drag-and-drop operation in the ListView control.
this.DoDragDrop(this.SelectedItems, DragDropEffects.Move);
}
protected override void OnDragEnter(DragEventArgs drgevent)
{
base.OnDragEnter(drgevent);
int len = drgevent.Data.GetFormats().Length - 1;
int i;
for (i = 0; i <= len; i++)
{
if (drgevent.Data.GetFormats()[i].Equals("System.Windows.Forms.ListView+SelectedListViewItemCollection"))
{
//The data from the drag source is moved to the target.
drgevent.Effect = DragDropEffects.Move;
}
}
}
protected override void OnDragDrop(DragEventArgs drgevent)
{
base.OnDragDrop(drgevent);
//Return if the items are not selected in the ListView control.
if (this.SelectedItems.Count == 0)
{
return;
}
//Returns the location of the mouse pointer in the ListView control.
Point cp = this.PointToClient(new Point(drgevent.X, drgevent.Y));
//Obtain the item that is located at the specified location of the mouse pointer.
ListViewItem dragToItem = this.GetItemAt(cp.X, cp.Y);
if (dragToItem == null)
{
return;
}
//Obtain the index of the item at the mouse pointer.
int dragIndex = dragToItem.Index;
ListViewItem[] sel = new ListViewItem[this.SelectedItems.Count];
for (int i = 0; i <= this.SelectedItems.Count - 1; i++)
{
sel[i] = this.SelectedItems[i];
}
for (int i = 0; i < sel.GetLength(0); i++)
{
//Obtain the ListViewItem to be dragged to the target location.
ListViewItem dragItem = sel[i];
int itemIndex = dragIndex;
if (itemIndex == dragItem.Index)
{
return;
}
if (dragItem.Index < itemIndex)
itemIndex++;
else
itemIndex = dragIndex + i;
//Insert the item at the mouse pointer.
ListViewItem insertItem = (ListViewItem)dragItem.Clone();
this.Items.Insert(itemIndex, insertItem);
//Removes the item from the initial location while
//the item is moved to the new location.
this.Items.Remove(dragItem);
}
}
}
}
which is based on / was adapted from http://www.codeproject.com/Articles/4576/Drag-and-Drop-ListView-row-reordering.
Unfortunately the code download doesn't work.
However - how is this code beeing used? Simply pasting it in my forms-class won't work. I supposed this code simply overrides all drag events from all listviews within a form?!
Sorry this is such a stupid question, but how do I use this code?
Someone has kindly taken the standard .NET ListView control and used something called Inheritance to make their own version with extra functionality. You should read up on it, it's a useful thing to know.
To get it to work in your project you need to add this class as described below.
I assume you're using Visual Studio, you haven't mentioned otherwise.
In Solution Explorer on the right hand side Right Click on your
solution and go to Add > Class.
Name the class whatever you like (ListViewWithReordering.cs would
make the most sense) and then open that file. Past all of this code
into the file.
Where it says namespace at the top you'll want to edit this to
match your own programs namespace.
Build your project.
In the Toolbox on the left where you normally see all of the
standard .NET controls there should be a section at the top that now
contains ListViewWithReordering.
Drag this control onto your project.
Done! Use it like any other ListView control. The rest should work automatically (as long as this class is indeed reliable).
I created a design example of drag and drop, download the project on the following link. Any questions ask again.
https://drive.google.com/file/d/0B21l6Fz0byBMSi1VTm52V2E4VVE/view?usp=sharing&resourcekey=0-jNA3sA85A6lygH3TyISstA
First add this class to your project.
Then, If you already have a listview from the forms designer, go to the InitializeComponent() method of your form constructor. There your listview is declared as:
private System.Windows.Forms.ListView listView1;
Change this declaration to:
private ListViewWithReordering listView1;

DateTimePicker OnValueChanged event is raised twice

I am using the DateTimePicker WPF control from the Extended WPF Toolkit Community Edition library version 2.5.
My problem is that when I pick a date, the OnValueChanged event is raised twice instead of just once.
Here is the code I am using:
XAML:
<StackPanel>
<xctk:DateTimePicker AutoCloseCalendar="True" Name="picker" Width="400" Height="40" ValueChanged="UpDownBase_OnValueChanged"/>
<ListBox Height="300" Name="listbox"></ListBox>
</StackPanel>
C# code behind:
private void UpDownBase_OnValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var value = picker.Value;
if (value == null)
listbox.Items.Add("[NULL]");
else
listbox.Items.Add(value.Value.ToString(CultureInfo.InvariantCulture));
}
Now, whenever I pick a new date, the list box will be populated with two new items. I also debugged the program and confirmed that the event handler is actually invoked twice.
How can I solve this issue?
UPDATE:
I tried version 2.4 and it seems that the issue is gone. It seems to me now that this might be a possible bug in version 2.5.
This would appear to be because the event in 2.5 is being fired from:
at Xceed.Wpf.Toolkit.DateTimePicker.OnValueChanged(Nullable`1 oldValue, Nullable`1 newValue) in C:\Users\Mark Vinten\Downloads\wpftoolkit-114314\Main\Source\ExtendedWPFToolkitSolution\Src\Xceed.Wpf.Toolkit\DateTimePicker\Implementation\DateTimePicker.cs:line 264
And then subsequently from the base class:
at Xceed.Wpf.Toolkit.TimePicker.OnValueChanged(Nullable`1 oldValue, Nullable`1 newValue) in C:\Users\Mark Vinten\Downloads\wpftoolkit-114314\Main\Source\ExtendedWPFToolkitSolution\Src\Xceed.Wpf.Toolkit\TimePicker\Implementation\TimePicker.cs:line 264
Now the base class, also seems to go through the CLR binding process suggesting that this is the bound value. I'm still looking into why that would be, but a workaround is to use Binding as such:
MainWindow.cs
public DateTime? DateTimeValue
{
get { return (DateTime?)GetValue(DateTimeValueProperty); }
set { SetValue(DateTimeValueProperty, value); }
}
// Using a DependencyProperty as the backing store for DateTimeValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DateTimeValueProperty =
DependencyProperty.Register("DateTimeValue", typeof(DateTime?), typeof(MainWindow), new PropertyMetadata(null, new PropertyChangedCallback(DateTimeValueProperty_Changed)));
private static void DateTimeValueProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MainWindow mw = d as MainWindow;
System.Diagnostics.Debug.WriteLine("d is " + d == null ? "null" : d.GetType().FullName);
if (mw != null && e.Property == DateTimeValueProperty)
{
var value = e.NewValue as DateTime?;
var listbox = FindChild<ListBox>(mw, "listbox");
if (value == null)
listbox.Items.Add("[NULL]");
else
listbox.Items.Add(value.Value.ToString(System.Globalization.CultureInfo.InvariantCulture));
}
}
/// <summary>
/// Finds a Child of a given item in the visual tree.
/// </summary>
/// <param name="parent">A direct parent of the queried item.</param>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="childName">x:Name or Name of child. </param>
/// <returns>The first parent item that matches the submitted type parameter.
/// If not matching item can be found,
/// a null parent is being returned.</returns>
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
MainWindow.xaml
<StackPanel DataContext="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}}}">
<xctk:DateTimePicker AutoCloseCalendar="True" Name="picker" Width="400" Height="40" Value="{Binding DateTimeValue}" />
<ListBox Height="300" Name="listbox"></ListBox>
</StackPanel>
This uses the binding system which automatically checks whether the value has changed and only raises events if it has.
Note: FindChild<> was a function that I found on this How can I find WPF controls by name or type? post
Update with final summary
The reason for this appears to be is because there is a TimePicker embedded within the DateTimePicker to provide the functionality. Unfortunately, both DateTimePicker and TimePicker derive from the same base and thus raise the same routed event within UpDownBase where T is DateTime?.
if you check on the event arguments, e.RoutedEVent is always UpDownBase.OnValueChanged since this is the class raising the event. e.Source or e.OriginalSource is always the DateTimePicker itself meaning you have no useful way to filter out one or the other event.
There is code within DateTimeUpDown.RaiseValueChangedEvent() to check if the TemplatedParent is a TimePicker to prevent re-raising but whether the event is raised from the DateTimePicker or the TimePicker the TemplatedParent always seems to be the DateTimePicker so that fails thus you get the event twice.
I have raised a bug with the findings on the WPFToolkit project site:
https://wpftoolkit.codeplex.com/workitem/22014
I solved this problem/bug by checking the originalsource of the event.
if(e.OriginalSource is Xceed.Wpf.Toolkit.DateTimePicker)
{
if(((Xceed.Wpf.Toolkit.DateTimePicker)e.OriginalSource).IsFocused == true)
{
ResetDataTable();
}
}
Since I don't display the timepicker in this control, I also make sure that it's the datetimepicker that has focus, and not the timepicker. Might be redundant
I solved this by comparing the original source and the source.
e.OriginalSource == e.Source

How to disallow interaction on a view behind uipopovercontroller

I have a UIPopoverController that I am using and I have two buttons each displays a popup when clicked. However, I do not want the popup to be displayed at the same time - meaning I do not want the user to be able to press the one button and while the popup is displayed be able to press the other button. It seems like I have tried everything - disabling the user interaction on the buttons, hiding the view behind the pop up, using passthrough views for the pop and more. None of it works! The disabling of the user interaction seems to work for the most part but then stops disallowing the user to interact with the button and causes the application to crash...
popupView.PassthroughViews = new UIView[]{this.View.Superview, this.View, this.Gray}; //gray is another view that sits under the view that calls the popup
this.View.UserInteractionEnabled = false;
this.PositiveMeterBtn.UserInteractionEnabled = false;
this.View.Hidden = true;
My UIPopoverController is declared at the class level and I have even done code like this:
if(popupView != null)
return;
I still get multiple popups. I am using mono touch/xamarin - is this a bug with xamarin or an ios issue? Am I handling this in the correct manner?
I haven't worked with Xamarin before, but what's worked for me in native Objective-C is
[controller setModalInPopover:YES];
where controller is the view controller displayed within the popover.
From the UIViewController class reference:
#property(nonatomic, readwrite, getter=isModalInPopover) BOOL modalInPopover
The default value of this property is NO. Setting it to YES causes an owning popover controller to disallow interactions outside this view controller while it is displayed.
You can either make the popover modal but if it doesn't contain content that is meant to be modal, you shouldn't block the user.
Usually the better option is to make two helper methods and place them for instance in your app delegate. The methods take care that an existing popover is dismissed if another one is to be shown. This way you will have a maximum of on UIPopoverController and don't have to worry about dismissal.
/// <summary>
/// Shows a popover.
/// </summary>
/// <param name='controllerToShow'>the controller to show in the popover</param>
/// <param name='showFromRect'>the rectangle to present the popover from. Not used if showFromItem is specified.</param>
/// <param name='showInView'>the view the popover is hosted in</param>
/// <param name='showFromItem'>the bar button item the popover gets presented from.</param>
/// <param name='popoverContentSize'>the content size of the popover</param>
/// <param name='animated'>If set to <c>true</c>, animated the popover</param>
/// <param name='arrowDirection'>the allowed arrow directions</param>
/// <param name='onDismiss'>callback if the popover gets dismissed. Careful that the object that owns the callback doesn't outlive the popover controller to prevent uncollectable memory.</param>
public static void ShowPopover(UIViewController controllerToShow, RectangleF showFromRect, UIView showInView, UIBarButtonItem showFromItem, SizeF popoverContentSize, bool animated = true, UIPopoverArrowDirection arrowDirection = UIPopoverArrowDirection.Any, EventHandler onDismiss = null)
{
if(AppDelegateBase.popoverController != null)
{
AppDelegateBase.DismissPopover(false);
}
if(showFromItem == null && showFromRect.IsEmpty)
{
// Nothing to attach the popover to.
return;
}
popoverController = new UIPopoverController(controllerToShow);
if(!popoverContentSize.IsEmpty)
{
popoverController.SetPopoverContentSize(popoverContentSize, false);
}
if(onDismiss != null)
{
popoverController.DidDismiss += onDismiss;
}
// Send a notification that a popover will be presented.
NSNotificationCenter.DefaultCenter.PostNotificationName("WillPresentPopover", popoverController);
if(showFromItem != null)
{
popoverController.PresentFromBarButtonItem(showFromItem, arrowDirection, animated);
}
else
{
popoverController.PresentFromRect(showFromRect, showInView, arrowDirection, animated );
}
}
/// <summary>
/// Dismisses the popover presented using ShowPopover().
/// </summary>
/// <param name='animated'>If set to <c>true</c>, animates the dismissal</param>
public static void DismissPopover(bool animated = false)
{
if(popoverController != null)
{
popoverController.Dismiss(animated);
}
AppDelegateBase.popoverController = null;
}
private static UIPopoverController popoverController;
One thing you might try is using the method
-(BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
And in that method check if one of your popover view controller's is on screen.
if (popupView.view.window) {
return NO;
} else {
return YES;
}

Set Focus to Any Control After PostBack

First I have 3 TextBoxes, a DropDownList, a CheckBox, and 2 LinkButtons in a table. The TextBoxes and DropDownList must do an AutoPostBack because there are some calculations being done on the server side which called by OnTextChanged.
My issue here is that after the content of any Control (which requires a PostBack) changes and you attempt to move focus to any other Control on the page (whether it be tabbing or using the mouse to click on any other Control) the focus does not move to the next Control or the Control you clicked.
I tried using a method which finds the current Control and based on the TabIndex, adds 1 which moves the focus to the next control. The problem here is that it does not allow for focus to be set on any other Control other than that one with the next TabIndex. To better explain here is an example of the situation.
Example: I have TextBox1, TextBox2, and TextBox3. I change the content in TextBox2 then want to move backwards to TextBox1. When I click in TextBox1 the focus is set to TextBox3 since the code is using the order of the TabIndex. ** A key factor here is that TextBox1 DOES get focus before the server side code fires but then loses focus and sets it on TextBox3 **
To me, it seems like the only way this can be accomplished would be to add a function on the client side which finds the Control with focus before the PostBack occurs, then reset the focus on that Control after the server side code completes. It does not seem like anything I do on the server side will work since the focus has already been lost when this occurs.
I have searched and searched but have not been able to find any solution to this particular issue, everything I have found deals with setting focus to the NEXT Control instead of the Control you want to have focus. Any help would be greatly appreciated. Thank you in advance.
Here is the code I am using to move to the next Control based on the TabIndex but this does not work in my situation.
protected void Page_Load(object sender, EventArgs e)
{
WebControl ctrl = GetPostBackControl() as WebControl;
if (ctrl != null && !SetNextFocus(Controls, ctrl.TabIndex + 1)) { ctrl.Focus(); }
}
public Control GetPostBackControl()
{
Control control = null;
string ctrlname = Request.Params.Get("__EVENTTARGET");
if (ctrlname != null && ctrlname != string.Empty)
{
control = FindControl(ctrlname);
control.Focus();
}
else
{
foreach (string ctl in Request.Form)
{
Control c = FindControl(ctl);
if (c is Button)
{
control = c;
break;
}
}
}
return control;
}
private bool SetNextFocus(ControlCollection controls, int tabIndex)
{
foreach (Control control in controls)
{
if (control.HasControls())
{
bool found = SetNextFocus(control.Controls, tabIndex);
if (found) { return true; }
}
WebControl webControl = control as WebControl;
if (webControl == null) { continue; }
if (webControl.TabIndex != tabIndex) { continue; }
webControl.Focus();
return true;
}
return false;
}
Try doing something like this to retain focus after postback:
/// <summary>
/// Overrides the OnLoad event moves the cursor to the appropriate control.
/// </summary>
/// <param name="e"></param>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
int currentTabIndex = 1;
WebControl postBackCtrl = (WebControl)GetControlThatCausedPostBack(Page);
foreach (WebControl ctrl in Panel1.Controls.OfType<WebControl>())
{
ctrl.TabIndex = (short)currentTabIndex;
if (postBackCtrl != null)
{
if (ctrl.TabIndex == postBackCtrl.TabIndex + 1)
ctrl.Focus();
}
currentTabIndex++;
}
}
/// <summary>
/// Retrieves the control that caused the postback.
/// </summary>
/// <param name="page"></param>
/// <returns></returns>
private Control GetControlThatCausedPostBack(Page page)
{
//initialize a control and set it to null
Control ctrl = null;
//get the event target name and find the control
string ctrlName = Page.Request.Params.Get("__EVENTTARGET");
if (!String.IsNullOrEmpty(ctrlName))
ctrl = page.FindControl(ctrlName);
//return the control to the calling method
return ctrl;
}
Thank you for the help, it did get me a little closer. The only thing that was preventing it from working correctly is that my page has some variables which are being carried over from other pages. When the page loads, the override is causing the page to break since it is unable to locate those controls. I was able to fix the issue by adding a HiddenField on the .aspx page and adding the following code to the onload() function:
if (document.getElementById("HiddenField1").value != "") {
var contr = document.getElementById(document.getElementById"HiddenField1").value);
contr.focus();
document.getElementById("HiddenField1").value = "";
}
as well as adding a new function which is called on each of the TextBoxes:
function tabFocus(e) {
document.getElementById("HiddenField1").value = e.id;
}
I ended up not having to make any changes in the code behind. Once again, thank you for the help!

Categories

Resources