IsKeyboardFocusWithin in silverlight - 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.

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
}
}

LogicalTree Control directly under mouse no VisualTree

This is a WPF application.
I'm trying to get the control directly under the mouse and it's proving to be a surprising pain.
Mouse.DirectlyOver, InputHitTest and VisualTreeHelper.HitTest all reference the VISUAL tree. I'm trying to grab the control itself.
Example: If I have a TextBox and use any of the above, it will return a TextBoxView whereas I want the TextBox itself.
This is happening inside of a PreviewLeftButtonDown event. Sender is not an option as Sender is always a ListViewItem for me. If I check e.OriginalSource it is still the VisualTree element and not the actual control.
Happy to explain further if need be.
Thanks
You just need to walk up the visual tree until you find the type you want. Here's a link to some code.
Here's the code from that link
// walk up the visual tree to find object of type T, starting from initial object
public static T FindUpVisualTree<T>(DependencyObject initial) where T : DependencyObject
{
DependencyObject current = initial;
while (current != null && current.GetType() != typeof(T))
{
current = VisualTreeHelper.GetParent(current);
}
return current as T;
}
I agree with mdm20 that the only way to get to the Textbox is by traversing up to the parent. In fact, here is a link to the same question asked a couple of years ago, with the same answer. If you want to limit unnecessary tree traversal, then you could stop searching once you hit the ListViewItem, as anything above that point is not what you are looking for anyway.
However, add the last link and this one together, and it actually seems to me that you already have your answer. If a TextBoxView is returned, then you know that a textbox was hit. You could even cache the incoming TextBox that goes through the HitTestFilterCallBack potentially, but that is more of a theory and possibly bug prone. However, going down that path, you could just test if the TextBox coming through the filter is the parent of the TextBoxView
Walk up the visual tree until you find a UIElement:
public UIElement FindUIElement(DependencyObject visualTreeNode)
{
UIElement elem;
while ((elem = (visualTreeNode as UIElement)) != null)
visualTreeNode = VisualTreeHelper.GetParent(visualTreeNode);
return elem;
}

Define Background Color of Alternating TreeView Rows Based on Visibility

Is there a way in WPF to define the background of alternating visible rows?
I've tried setting the AlternationCount property, but that restarts for every child node which gives a weird look.
Ideally what I would like is to know what the visual index of a given node is. Only expanded nodes are counted.
There was no easy way to do this as WPF creates nested containers for the tree nodes. So as Rachel mentioned, looping through the items seemed to be the way to go. But I didn't want to depart too much from the built in ItemsControl.AlternationIndex attached property as that is the one people would expect. Because it is readonly I had to access it via reflection, but after that things fell into place.
First off, make sure you handle the Loaded, Expanded and Collapsed events of your TreeViewItem. In the event handler find the owning TreeView and do a recursive alternation count set of all visible nodes. I created an extension method to handle it:
public static class AlternationExtensions
{
private static readonly MethodInfo SetAlternationIndexMethod;
static AlternationExtensions()
{
SetAlternationIndexMethod = typeof(ItemsControl).GetMethod(
"SetAlternationIndex", BindingFlags.Static | BindingFlags.NonPublic);
}
public static int SetAlternationIndexRecursively(this ItemsControl control, int firstAlternationIndex)
{
var alternationCount = control.AlternationCount;
if (alternationCount == 0)
{
return 0;
}
foreach (var item in control.Items)
{
var container = control.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
if (container != null)
{
var nextAlternation = firstAlternationIndex++ % alternationCount;
SetAlternationIndexMethod.Invoke(null, new object[] { container, nextAlternation });
if (container.IsExpanded)
{
firstAlternationIndex = SetAlternationIndexRecursively(container, firstAlternationIndex);
}
}
}
return firstAlternationIndex;
}
}
As you can see it runs through each node and sets the custom alternation index. It checks if the node is expanded and if so continues the count on the child nodes.
Above I mentioned that you have to handle the Loaded event for the TreeViewItem. If you only handle the expanded and collapsed events you won't get the new containers that are created when a node is first opened. So you have to do a new pass when the child node has been created and added to the visual tree.
Something I've done with javascript is create an OnLoaded event for a table which loops through the table rows and if the row is visible, it sets the background color to a nextColor variable, and changes the nextColor variable to the opposite color. That might work here.

Winforms treeview, recursively check child nodes problem

The following code is taken direct from Microsoft at http://msdn.microsoft.com/en-us/library/system.windows.forms.treeview.aftercheck%28VS.80%29.aspx.
// Updates all child tree nodes recursively.
private void CheckAllChildNodes(TreeNode treeNode, bool nodeChecked)
{
foreach (TreeNode node in treeNode.Nodes)
{
node.Checked = nodeChecked;
if (node.Nodes.Count > 0)
{
// If the current node has child nodes, call the CheckAllChildsNodes method recursively.
this.CheckAllChildNodes(node, nodeChecked);
}
}
}
// NOTE This code can be added to the BeforeCheck event handler instead of the AfterCheck event.
// After a tree node's Checked property is changed, all its child nodes are updated to the same value.
private void node_AfterCheck(object sender, TreeViewEventArgs e)
{
// The code only executes if the user caused the checked state to change.
if (e.Action != TreeViewAction.Unknown)
{
if (e.Node.Nodes.Count > 0)
{
/* Calls the CheckAllChildNodes method, passing in the current
Checked value of the TreeNode whose checked state changed. */
this.CheckAllChildNodes(e.Node, e.Node.Checked);
}
}
}
You put it in a form containing a treeview and call node_AfterCheck on (surprise, surprise), the treeview AfterCheck event. It then recursively checks or unchecks the child nodes on the treeview.
However if you actually try it, and click several times on the same treeview check box fast enough, the child nodes end up with their check out-of-sync with the parent. You probably need a couple of levels of children with perhaps 100 children in-total for the UI update to be slow enough to notice this happening.
I've tried a couple of things (such as disabling the treeview control at the beginning of node_AfterCheck and re-enabling at the end), but the out-of-sync problem still happens.
Any ideas?
The .NET TreeView class heavily customizes mouse handling for the native Windows control in order to synthesize the Before/After events. Unfortunately, they didn't get it quite right. When you start clicking fast, you'll generate double-click messages. The native control responds to a double-click by toggling the checked state for the item, without telling the .NET wrapper about it. You won't get a Before/AfterCheck event.
It's a bug but they won't fix it. The workaround is not difficult, you'll need to prevent the native control from seeing the double-click event. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox, replacing the existing one.
using System;
using System.Windows.Forms;
class MyTreeView : TreeView {
protected override void WndProc(ref Message m) {
// Filter WM_LBUTTONDBLCLK
if (m.Msg != 0x203) base.WndProc(ref m);
}
}
Using the solution above, I think it is need to paint more detailed steps, how to apply it for those who want to apply it to an already created TreeView. For example, for me, a beginner, this caused difficulties, but here is the solution:
Creating a class "NoClickTree.cs" in your project.
Include this code in new class:
public class NoClickTree : TreeView
{
protected override void WndProc(ref Message m)
{
// Suppress WM_LBUTTONDBLCLK
if (m.Msg == 0x203) { m.Result = IntPtr.Zero; }
else base.WndProc(ref m);
}
}
Go to Form1.Designer.cs or "yourWindowWithTreeView".Designer.cs
Find original initialization at the end of the file, something like private System.Windows.Forms.TreeView treeView;
Replace them on private NoClickTree treeView;
In function private void InitializeComponent() find original initialization, something like this.treeView = new System.Windows.Forms.TreeView();
Replace them on this.treeView = new NoClickTree();
Done!
This steps helped me for solve this problem.

select tabItem programmatically in 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;
}
}

Resources