select tabItem programmatically in WPF - wpf

I have different tabItems in a TabControl
and each tabItem has some input fields.
I am moving between the tabItems programmatically (like a wizard to move from the first to the next)
I am using this code inside the "Next" button
tabItem2.isSelected = true;
my problem that when I move between the tabItems by clicking on them, the focus (keyboard focus) will move to the first textbox input.
But programmatically with the previous code, the focus won't move to the first input textbox item inside the tabItem.
Any idea?

If you're forcing the IsSelected property, I'd also give the first TextBox a name and set the focus after you set the selected tab.
If you're building your UI dynamically, this won't work, but you can create a utility method which searches the logical tree (or the visual tree if you're using presenters/view-models) for the first input control and then set the focus.

These solutions didn't work for me. It got as far selecting the TabItem I wanted, but it wasn't able to select/focus the desired TreeViewItem. (It would only focus the TVI if the TabItem was already selected.) The solution below finally worked for me.
(FYI: The snippets below are part of app that is similar to Microsoft Help Viewer 2.0. When you click the "Sync" button, it first selects the Contents tab if not already selected, then traverses into tree view until it finds the matching tree view item. Which it then selects/focuses.)
Cheers
private void OnClick_SyncContents(object sender, RoutedEventArgs e)
{
// If the help-contents control isn't visible (ie., some other tab is currently selected),
// then use our common extension method to make it visible within the tab control. Once
// it visible, the extension method will call the event handler passed (which is this method)
if (!this.m_UcHelpFileContents.IsVisible)
{
this.m_UcHelpFileContents.
SelectParentTabItem_WaitForMeToBecomeVisible_ThenCallThisEventHandlerWithNullArguments
(this.OnClick_SyncContents);
}
else
{
// Else the help-contents control is currently visible, thus focus the
// matching tree view item
/* Your code here that focuses the desired tree view item */
}
}
public static class CommonExtensionMethods
{
public static void
SelectParentTabItem_WaitForMeToBecomeVisible_ThenCallThisEventHandlerWithNullArguments
(this FrameworkElement frameworkElement, RoutedEventHandler eventHandlerToCallWhenVisible)
{
// First, define the handler code for when the given framework element becomes visible
DependencyPropertyChangedEventHandler HANDLER = null;
HANDLER = (s, e) =>
{
// If here, the given framework element is now visible and its tab item currently selected
// Critical: first and foremost, undo the latch to is-visible changed
frameworkElement.IsVisibleChanged -= HANDLER;
// Now invoke the event handler that the caller wanted to invoke once visible
frameworkElement.Dispatcher.BeginInvoke(eventHandlerToCallWhenVisible, null, null);
};
// Use our common extension method to find the framework element's parent tab item
TabItem parentTabItem = frameworkElement.GetFirstParentOfType<TabItem>();
if (parentTabItem != null)
{
// Assign the handler to the given framework element's is-visible-changed event
frameworkElement.IsVisibleChanged += HANDLER;
// Now set the tab item's is-selected property to true (which invokes the above
// handler once visible)
parentTabItem.IsSelected = true;
}
}
public static T GetFirstParentOfType<T>
(this FrameworkElement frameworkElement) where T : FrameworkElement
{
for (FrameworkElement fe = frameworkElement.Parent as FrameworkElement;
fe != null;
fe = fe.Parent as FrameworkElement)
{
if (fe is T)
return fe as T;
}
// If here, no match
return null;
}
}

Related

Is it possible to create a control once and have it generated everytime it is needed?

Say for example you have a stackpanel that you would like to programmatically add buttons to.
The code behind for the button generation and addition to the stackpanel is:
Button button = new Button();
button.Content = "Button";
button.HorizontalAlignment = HorizontalAlignment.Left;
button.Name = "Button" + i
stackPanel1.Children.Add(button);
My question is - Is it possible to generate the button once and have it as some kind of template that can be added to the stackpanel whenever it is needed without going through the generation code again?
In WPF each UIElement can only be the logical child of one control at any given time, see WPF Error: Specified element is already the logical child of another element. Disconnect it first, so no you can't use the same button and add it to another control later on, unless you're sure you've gotten rid of that stackpanel
But, you can do recycling. See Optimizing Performance: Controls. especially if you are willing to override MeasureOverride and ArrangeOverride of you stackpanel
I've actually written such a recycler because I had grids with many controls, and I wanted to implement some kind of virtualizing grid. These are the main methods of my class:
internal class Recycler
{
private UIElementCollection _children;
public Recycler(UIElementCollection children)
{
_children = children;
//You need this because you're not going to remove any
//UIElement from your control without calling this class
}
public void Recycle(UIElement uie)
{
//Keep this element for recycling, remove it from Children
}
public UIElement GiveMeAnElement(Type type)
{
//Return one of the elements you kept of this type, or
//if none remain create one using the default constructor
}
}

WPF tab index issue

I'm working on an application where a user defines a the controls on a form and can set the tab index of any control. As each control is added to the Grid that comprises the viewable form area, the tab index is set to either 0 (default) or some user-defined tab index. Tabbing through the form works fine until the tabindex of one of the controls is changed at runtime(the index doesn't seem to matter.) After this, tabbing cycles only through some of the controls and in addition, the window menu items are now tab stops(they weren't prior to the tabindex change.) Also, the menu's tab properties aren't bound to any datacontext.
The control that's currently changed is a checkbox, but I'm unable to reproduce the behavior with a simplified layout, so any suggestions would help.
Our "form pages" user controls invisible and beneath the current visible page were never disabled when new ones were pushed on the top. Therefore they were included in the tab indexing behavior causing unwanted side effects.
This helped me get to the bottom of the issue:
void InitializeFocusLogger() {
//debug key logging to make focus bugs easier to track
EventManager.RegisterClassHandler(
typeof(UIElement),
Keyboard.PreviewGotKeyboardFocusEvent,
(KeyboardFocusChangedEventHandler)OnPreviewGotKeyboardFocus);
}
string lastID = string.Empty;
private void OnPreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) {
FrameworkElement control = e.NewFocus as FrameworkElement;
if (control == null) return;
ControlViewModel controlVM = control.DataContext as ControlViewModel;
if (controlVM == null || lastID == controlVM.ID) return;
lastID = controlVM.ID;
Debug.Print("Focused: {0} TabIndex: {1}", controlVM.ID, controlVM.TabIndex);
}

IsKeyboardFocusWithin in silverlight

How to get the keyboard focus is anywhere within the element or its visual tree child elements in Silverlght?
There are two possible solutions depending on your scenario (we usually prefer more detail in a question).
First you can use the FocusManager.GetFocusedElement() static method to get the element that currently has the focus. You could then use the VisualTreeHelper to determine if the element is with the your element. I would normally use an extension class to make using VisualTreeHelper easier. Mine is found here. With that class present. Then:-
public static bool IsFocusIn(DependencyObject element)
{
DependendyObject focusedElement = FocusManager.GetFocusedElement() as DependencyObject;
if (focusedElement != null)
{
return focusedElement.Ancestors().Any(e => e == element);
}
return false;
}
The second approach is to add event handlers to the GotFocus and LostFocus events of your element. You can then track whenever the focus enters or leaves any control within your element.

cannot retrieve object of TreeViewItem

I have a tree view in silver light which i am creating dynamically from my code behind on the load event of my .xaml page. My treeview contains numerous treeviewitems.The header of my treeviewitem contains a stack panel. My stackpanel contains a check box as child. I have created an event handler for Unchecked event of the check box.
Now here is my problem.
When the unchecked event of my check box is triggered i want to retrieve the object of treeviewitem that is consuming the check box.
Here is the code snippet that shows how i am creating my treeviewitem
objTreeviewItem = new TreeViewItem();
objStackPanel = new StackPanel();
objStackPanel.Orientation = Orientation.Horizontal;
objCheckBox = new CheckBox();
objCheckBox.Content = "Checkbox1";
objCheckBox.Unchecked += new RoutedEventHandler(objCheckBox_Unchecked);
objStackPanel.Children.Add(objCheckBox);
objTreeviewItem.Header = objStackPanel;
Here is the code snippet for unchecked event of the checkbox
void objCheckBox_Unchecked(object sender, RoutedEventArgs e)
{
try
{
TreeViewItem objItem = (((e.OriginalSource) as CheckBox).Parent as StackPanel).Parent as TreeViewItem;
}
catch (Exception ex)
{
}
}
The above statement in the try block is returning me a null value. Hence i am not able to retrieve the treeviewitem which is consuming the check box on which event has been triggered.
So is there any other Property or method(other then the parent property) which can help me get the treeviewitem.
Please any kind of help will be appreciated
The OriginalSource property does not return your CheckBox but whatever control on which the event started which may be a TextBlock inside the CheckBox, cast the sender instead, it will always be the control the event is associated with.

WPF ItemsControl get container from data object (TreeView, Multiselect)

How can I get the Container for an object in WPF ItemsControl.
I am writing a multiselect treeview with bindable SelectedItem und SelectedItems Dependency Properties. So long everything works just fine. The only thing is, when I click on an item in the tree with pressed ctrl a second time this item should not be selected but the last previous selected item. The TreeView contains a private Method called ChangeSelection. As far as i understand the first parameter is the Container, the second is the TreeViewItem and the last wherether the item shall be selected or not.
I implement the multiselection with catching the SelectedItemChanged event.
This code works for the new selected item
private void OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var view = ItemContainerGenerator.ContainerFromItem(e.NewValue) as TreeViewItem;
// ...
}
BUT if i want to get the TreeViewItem from an item saved in an ObservableCollection... it will not work.
EDIT: Ok, as i found out. The code above works only for the first level of items...
EDIT: The solution for this problem isn't trivial. It is possible to find the selected treeview item by using a viewmodel (f.e. an interface which provides basics like: IsSelected, IsExpanded, IsEnabled and Parent). You can search the TreeViewItem like this:
if (treeViewItem.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
{
EventHandler eventHandler = null;
eventHandler = delegate
{
treeViewItem.ItemContainerGenerator.StatusChanged -= eventHandler;
// Call the search function recursive XYZ(tree, treeViewItem.ItemContainerGenerator.ContainerFromItem(nextLevelItem) as TreeViewItem);
};
// wait for the containers to be generated
treeViewItem.ItemContainerGenerator.StatusChanged += eventHandler;
}
else
{
// Call the search function recursive XYZ(tree, treeViewItem.ItemContainerGenerator.ContainerFromItem(nextLevelItem) as TreeViewItem);
}

Resources