// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UIElementExtensions.cs" company="Catel development team">
// Copyright (c) 2008 - 2011 Catel development team. All rights reserved.
// </copyright>
// <summary>
// Extensions for <see cref="UIElement" />.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
namespace Catel.Windows
{
/// <summary>
/// Extensions for <see cref="UIElement"/>.
/// </summary>
public static class UIElementExtensions
{
#region Win32 imports
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
public static implicit operator System.Drawing.Point(POINT p)
{
return new System.Drawing.Point(p.X, p.Y);
}
public static implicit operator POINT(System.Drawing.Point p)
{
return new POINT(p.X, p.Y);
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool InvalidateRect(IntPtr hWnd, IntPtr lpRect, bool bErase);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint);
#endregion
#region Methods
/// <summary>
/// Focuses the first control on the ContentElement. Also focuses the parents first.
/// </summary>
/// <param name="element">Reference to the current <see cref="IInputElement"/>.</param>
public static void FocusFirstControl(this IInputElement element)
{
FocusFirstControl(element, true);
}
/// <summary>
/// Focuses the first control on the ContentElement. Also focuses the parents first.
/// </summary>
/// <param name="element">Reference to the current <see cref="ContentElement"/>.</param>
public static void FocusFirstControl(this ContentElement element)
{
FocusFirstControl(element, true);
}
/// <summary>
/// Focuses the first control on the UI Element. Also focuses the parents first.
/// </summary>
/// <param name="element">Reference to the current <see cref="UIElement"/>.</param>
public static void FocusFirstControl(this UIElement element)
{
FocusFirstControl(element, true);
}
/// <summary>
/// Focuses the first control on the ContentElement.
/// </summary>
/// <param name="element">Reference to the current <see cref="IInputElement"/>.</param>
/// <param name="focusParentsFirst">if set to <c>true</c>, the parents are focused first.</param>
public static void FocusFirstControl(this IInputElement element, bool focusParentsFirst)
{
FocusFirstControl((object)element, focusParentsFirst);
}
/// <summary>
/// Focuses the first control on the ContentElement.
/// </summary>
/// <param name="element">Reference to the current <see cref="ContentElement"/>.</param>
/// <param name="focusParentsFirst">if set to <c>true</c>, the parents are focused first.</param>
public static void FocusFirstControl(this ContentElement element, bool focusParentsFirst)
{
FocusFirstControl((object)element, focusParentsFirst);
}
/// <summary>
/// Focuses the first control on the UI Element.
/// </summary>
/// <param name="element">Reference to the current <see cref="UIElement"/>.</param>
/// <param name="focusParentsFirst">if set to <c>true</c>, the parents are focused first.</param>
public static void FocusFirstControl(this UIElement element, bool focusParentsFirst)
{
FocusFirstControl((object)element, focusParentsFirst);
}
/// <summary>
/// Focuses the first control on the UI Element.
/// </summary>
/// <param name="element">Reference to the current element.</param>
/// <param name="focusParentsFirst">if set to <c>true</c>, the parents are focused first.</param>
private static void FocusFirstControl(object element, bool focusParentsFirst)
{
if (element is FrameworkElement)
{
FrameworkElement frameworkElement = element as FrameworkElement;
if (frameworkElement.IsLoaded)
{
FocusNextControl(frameworkElement, focusParentsFirst);
}
else
{
// Get handler (so we can nicely unsubscribe)
RoutedEventHandler frameworkElement_Loaded = null;
frameworkElement_Loaded = delegate
{
FocusNextControl(frameworkElement, focusParentsFirst);
frameworkElement.Loaded -= frameworkElement_Loaded;
};
frameworkElement.Loaded += frameworkElement_Loaded;
}
}
else
{
FocusFirstControl(element, focusParentsFirst);
}
}
/// <summary>
/// Focuses the next control on the UI Element.
/// </summary>
/// <param name="element">Element to focus the next control of.</param>
/// <param name="focusParentsFirst">if set to <c>true</c>, the parents are focused first.</param>
private static void FocusNextControl(object element, bool focusParentsFirst)
{
if (element is FrameworkElement)
{
FrameworkElement frameworkElement = element as FrameworkElement;
if (focusParentsFirst)
{
Stack<FrameworkElement> parentsToFocus = new Stack<FrameworkElement>();
FrameworkElement parent = frameworkElement.Parent as FrameworkElement;
while (parent != null)
{
if (parent.Focusable)
{
parentsToFocus.Push(parent);
}
parent = parent.Parent as FrameworkElement;
}
while (parentsToFocus.Count > 0)
{
FrameworkElement parentToFocus = parentsToFocus.Pop();
parentToFocus.Focus();
}
}
}
UIElement uiElement = element as UIElement;
ContentElement contentElement = element as ContentElement;
if (uiElement != null)
{
// Focus element itself
if (uiElement.Focusable)
{
uiElement.Focus();
}
}
else if (contentElement != null)
{
// Focus content element
if (contentElement.Focusable)
{
contentElement.Focus();
}
}
MoveFocus(element, FocusNavigationDirection.Next, 1);
}
/// <summary>
/// Moves the focus in a specific direction.
/// </summary>
/// <param name="element">The element.</param>
/// <param name="direction">The direction.</param>
/// <param name="hops">The hops.</param>
public static void MoveFocus(this IInputElement element, FocusNavigationDirection direction, int hops)
{
MoveFocus((object)element, direction, hops);
}
/// <summary>
/// Moves the focus in a specific direction.
/// </summary>
/// <param name="element">The element.</param>
/// <param name="direction">The direction.</param>
/// <param name="hops">The hops.</param>
public static void MoveFocus(this UIElement element, FocusNavigationDirection direction, int hops)
{
MoveFocus((object)element, direction, hops);
}
/// <summary>
/// Moves the focus in a specific direction.
/// </summary>
/// <param name="element">The element.</param>
/// <param name="direction">The direction.</param>
/// <param name="hops">The hops.</param>
public static void MoveFocus(this ContentElement element, FocusNavigationDirection direction, int hops)
{
MoveFocus((object)element, direction, hops);
}
/// <summary>
/// Moves the focus in a specific direction.
/// </summary>
/// <param name="element">The element.</param>
/// <param name="direction">The direction.</param>
/// <param name="hops">The hops.</param>
private static void MoveFocus(object element, FocusNavigationDirection direction, int hops)
{
if (hops <= 0)
{
return;
}
FrameworkElement frameworkElement = element as FrameworkElement;
bool delayMove = ((frameworkElement != null) && !frameworkElement.IsLoaded);
if (delayMove)
{
RoutedEventHandler frameworkElement_Loaded = null;
frameworkElement_Loaded = delegate
{
MoveFocus((object)frameworkElement, direction, hops);
frameworkElement.Loaded -= frameworkElement_Loaded;
};
frameworkElement.Loaded += frameworkElement_Loaded;
}
else
{
UIElement uiElement = element as UIElement;
ContentElement contentElement = element as ContentElement;
if (uiElement != null)
{
// Focus next
uiElement.MoveFocus(new TraversalRequest(direction));
}
else if (contentElement != null)
{
// Focus next
contentElement.MoveFocus(new TraversalRequest(direction));
}
if (hops > 1)
{
MoveFocus(Keyboard.FocusedElement, direction, hops - 1);
}
}
}
/// <summary>
/// Invalidates the rect as it is possible in win32.
/// </summary>
/// <param name="element">The element.</param>
/// <exception cref="ArgumentNullException">when <paramref name="element"/> is <c>null</c>.</exception>
public static void InvalidateRect(this UIElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
HwndSource hwndSource = PresentationSource.FromVisual(element) as HwndSource;
if (hwndSource == null)
{
return;
}
InvalidateRect(hwndSource.Handle, IntPtr.Zero, true);
}
#endregion
}
}