// The Nova Project by Ken Beckett.
// Copyright (C) 2007-2012 Inevitable Software, all rights reserved.
// Released under the Common Development and Distribution License, CDDL-1.0: http://opensource.org/licenses/cddl1.php
using System;
using System.Runtime.Remoting.Messaging;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
namespace Nova.UI
{
/// <summary>
/// The style of rendered text.
/// </summary>
[Flags]
public enum TextStyle { Normal = 0, Bold = 1, Italic = 2, Proportional = 4, NoCache = 8 }
/// <summary>
/// Static helper methods for various WPF classes.
/// </summary>
public static class WPFUtil
{
#region /* STATIC HELPER METHODS */
/// <summary>
/// Process all pending UI events.
/// </summary>
public static void DoEvents()
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { }));
}
/// <summary>
/// Invoke the specified action on a worker thread, then invoke the completed action on the main thread
/// when the background operation is complete.
/// </summary>
public static void ExecuteActionOnThread<T>(DispatcherObject dispatcherObject, Action<T> action, T actionParameter, Action completedAction)
{
action.BeginInvoke(actionParameter, ExecuteActionOnThreadCallback<T>, new AsyncState(dispatcherObject, completedAction));
}
private static void ExecuteActionOnThreadCallback<T>(IAsyncResult result)
{
// Clean up the asynchronous operation, and invoke the completion action on the main UI thread
((Action<T>)((AsyncResult)result).AsyncDelegate).EndInvoke(result);
AsyncState asyncState = (AsyncState)result.AsyncState;
asyncState.DispatcherObject.Dispatcher.Invoke(asyncState.Action);
}
private class AsyncState
{
public readonly DispatcherObject DispatcherObject;
public readonly Action Action;
public AsyncState(DispatcherObject dispatcherObject, Action action)
{
DispatcherObject = dispatcherObject;
Action = action;
}
}
/// <summary>
/// Invoke the specified action on a worker thread, then invoke the completed action on the main thread
/// when the background operation is complete.
/// </summary>
public static void ExecuteActionOnThread<T, U>(DispatcherObject dispatcherObject, Action<T> action, T actionParameter, Action<U> completedAction, U completedParameter)
{
action.BeginInvoke(actionParameter, ExecuteActionOnThreadCallback<T, U>, new AsyncState<U>(dispatcherObject, completedAction, completedParameter));
}
private static void ExecuteActionOnThreadCallback<T, U>(IAsyncResult result)
{
// Clean up the asynchronous operation, and invoke the completion action on the main UI thread
((Action<T>)((AsyncResult)result).AsyncDelegate).EndInvoke(result);
AsyncState<U> asyncState = (AsyncState<U>)result.AsyncState;
asyncState.DispatcherObject.Dispatcher.Invoke(asyncState.Action, asyncState.Parameter);
}
private class AsyncState<U>
{
public readonly DispatcherObject DispatcherObject;
public readonly Action<U> Action;
public readonly U Parameter;
public AsyncState(DispatcherObject dispatcherObject, Action<U> action, U parameter)
{
DispatcherObject = dispatcherObject;
Action = action;
Parameter = parameter;
}
}
/// <summary>
/// Add a MenuItem to a Menu or ContextMenu.
/// </summary>
/// <param name="menu">The Menu or ContextMenu.</param>
/// <param name="header">The description (header) of the MenuItem to be added.</param>
/// <returns>The MenuItem added.</returns>
public static MenuItem AddMenuItem(ItemsControl menu, string header)
{
MenuItem item = new MenuItem { Header = header };
menu.Items.Add(item);
return item;
}
/// <summary>
/// Add a MenuItem to a Menu or ContextMenu.
/// </summary>
/// <param name="menu">The Menu or ContextMenu.</param>
/// <param name="header">The description (header) of the MenuItem to be added.</param>
/// <param name="clickHandler">The click event handler.</param>
/// <param name="enabled">True if the menu item is enabled.</param>
/// <returns>The MenuItem added.</returns>
public static MenuItem AddMenuItem(ItemsControl menu, string header, RoutedEventHandler clickHandler, bool enabled)
{
MenuItem item = AddMenuItem(menu, header);
item.IsEnabled = enabled;
item.Click += clickHandler;
return item;
}
/// <summary>
/// Add a checkable MenuItem to a Menu or ContextMenu.
/// </summary>
/// <param name="menu">The Menu or ContextMenu.</param>
/// <param name="header">The description (header) of the checkable MenuItem to be added.</param>
/// <returns>The MenuItem added.</returns>
public static MenuItem AddCheckableMenuItem(ItemsControl menu, string header)
{
MenuItem item = AddMenuItem(menu, header);
item.IsCheckable = true;
return item;
}
/// <summary>
/// Add a command to a Menu or ContextMenu.
/// </summary>
/// <param name="menu">The Menu or ContextMenu.</param>
/// <param name="command">The command to be added.</param>
/// <param name="target">The target of the command.</param>
/// <returns>The MenuItem added.</returns>
public static MenuItem AddMenuItemCommand(ItemsControl menu, RoutedCommand command, IInputElement target)
{
MenuItem item = new MenuItem { Header = command.Name, Command = command, CommandTarget = target };
menu.Items.Add(item);
return item;
}
/// <summary>
/// Add a command to a Menu or ContextMenu with a '...' button.
/// </summary>
/// <param name="menu">The Menu or ContextMenu.</param>
/// <param name="header">The description (header) of the MenuItem to be added.</param>
/// <param name="command">The command to be added.</param>
/// <param name="target">The target of the command.</param>
/// <returns>The MenuItem added.</returns>
public static MenuItem AddMenuItemCommandWithButton(ItemsControl menu, string header, RoutedCommand command, IInputElement target)
{
WrapPanel wrapPanel = new WrapPanel();
TextBlock textBlock = new TextBlock { Text = header + " " };
wrapPanel.Children.Add(textBlock);
Button button = new Button { Content = " . . . ", Height = 18, Command = command, CommandTarget = target, CommandParameter = "..." };
wrapPanel.Children.Add(button);
MenuItem item = new MenuItem { Header = wrapPanel, Command = command, CommandTarget = target };
menu.Items.Add(item);
return item;
}
/// <summary>
/// Add a checkable command to a Menu or ContextMenu.
/// </summary>
/// <param name="menu">The Menu or ContextMenu.</param>
/// <param name="command">The command to be added.</param>
/// <param name="target">The target of the command.</param>
/// <param name="isChecked">The current checked state.</param>
/// <returns>The MenuItem added.</returns>
public static MenuItem AddCheckableMenuItemCommand(ItemsControl menu, RoutedCommand command, IInputElement target, bool isChecked)
{
MenuItem item = AddMenuItemCommand(menu, command, target);
item.IsCheckable = true;
item.IsChecked = isChecked;
return item;
}
/// <summary>
/// Add a Separator to a Menu or ContextMenu.
/// </summary>
/// <param name="menu">The Menu or ContextMenu.</param>
public static void AddSeparator(ItemsControl menu)
{
menu.Items.Add(new Separator());
}
/// <summary>
/// Add a command binding to a Window.
/// </summary>
/// <param name="window">The Window.</param>
/// <param name="command">The command to be bound.</param>
/// <param name="executed">The executed event handler for the command.</param>
public static void AddCommandBinding(Window window, ICommand command, ExecutedRoutedEventHandler executed)
{
window.CommandBindings.Add(new CommandBinding(command, executed));
}
/// <summary>
/// Add a command binding to a Window.
/// </summary>
/// <param name="window">The Window.</param>
/// <param name="command">The command to be bound.</param>
/// <param name="executed">The executed event handler for the command.</param>
/// <param name="canExecute">The can-execute event handler for the command.</param>
public static void AddCommandBinding(Window window, ICommand command, ExecutedRoutedEventHandler executed, CanExecuteRoutedEventHandler canExecute)
{
window.CommandBindings.Add(new CommandBinding(command, executed, canExecute));
}
/// <summary>
/// Scroll the ListView so that the last item is visible.
/// </summary>
public static void ShowLastItem(ListView listView)
{
// Setup a call back, so we can scroll the item into view after the UI has updated
listView.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action<ListView>(ShowLastListViewItem), listView);
}
private static void ShowLastListViewItem(ListView listView)
{
if (listView.Items.Count > 0)
listView.ScrollIntoView(listView.Items[listView.Items.Count - 1]);
}
/// <summary>
/// Scroll the control to the top, so the ScrollViewer doesn't retain its state.
/// </summary>
public static void ScrollToTop(ItemsControl itemsControl)
{
// Scroll the stupid ScrollViewer to the top so it doesn't retain its state if the
// collection is cleared or otherwise updated (WPF bug!?).
// Note that this code is dependent upon the specific control template in use!
itemsControl.UpdateLayout();
if (VisualTreeHelper.GetChildrenCount(itemsControl) > 0)
{
DependencyObject outer = VisualTreeHelper.GetChild(itemsControl, 0);
if (outer != null) // Might be a Border or a ListBoxChrome
{
ScrollViewer scrollViewer = VisualTreeHelper.GetChild(outer, 0) as ScrollViewer;
if (scrollViewer != null)
scrollViewer.ScrollToTop();
}
}
}
/// <summary>
/// Clear the control's items, also resetting the ScrollViewer so it doesn't retain its state.
/// </summary>
public static void Clear(ItemsControl itemsControl)
{
ScrollToTop(itemsControl);
itemsControl.Items.Clear();
}
#endregion
}
}