C#

Showing help for the focused WPF control with F1

We want to be able to display Help text when we focus on some FrameworkElement and pressing F1.

We have two constraints:

  1. the solution must be in MVVM.
  2. The solution need to allow to add more controls easily.

The solution is to use CommandBinding to execute the command and AttachedProperty to set the Help description.

Comment: Here we use ToolTip to show the help, of course this is can do in any other way. Also the way to get the Help description can be do in any other way.

static class HelpManager{

private static Predicate hasDescription;

static HelpManager() {
CommandManager.RegisterClassCommandBinding(
typeof(FrameworkElement),
new CommandBinding(
ApplicationCommands.Help,
new ExecutedRoutedEventHandler(Executed),
new CanExecuteRoutedEventHandler(CanExecute)));

hasDescription = element =>
GetHelpDescription(element) != null &&
Properties.Resources.ResourceManager.
GetString(GetHelpDescription(element)) != null;
}

public static readonly DependencyProperty HelpDescriptionProperty =
DependencyProperty.RegisterAttached(
"HelpDescription",
typeof(string),
typeof(HelpManager));

public static string GetHelpDescription(DependencyObject obj) {
return (string)obj.GetValue(HelpDescriptionProperty);
}

public static void SetHelpDescription(DependencyObject obj, string value) {
obj.SetValue(HelpDescriptionProperty, value);
}

static private void CanExecute(object sender, CanExecuteRoutedEventArgs e){
var element = sender as FrameworkElement;
if (element == null) return;
e.CanExecute = hasDescription(element);
}

static private void Executed(object sender, ExecutedRoutedEventArgs e) {
var element = sender as FrameworkElement;
if (element == null) return;
ShowHelp(element);
}

private static void ShowHelp(FrameworkElement element) {
var tooltip = ((ToolTip)element.ToolTip) ?? new ToolTip();
tooltip.IsOpen = true;
tooltip.StaysOpen = false;
tooltip.Content =
Properties.Resources.ResourceManager.GetString(GetHelpDescription(element));
}

}

How to use?

Normal:

<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Example"
Title="MainWindow" Height="350" Width="525">
<Button Height="30" local:HelpManager.HelpDescription="SampleHelp"/>
</Window>

With style setter

<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Example"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>

<Setter Property="local:HelpManager.HelpDescription" Value="Help2"/>
</Style>
</Window.Resources>
<TextBox />
</Window>

With external control

<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Example"
xmlns:external="clr-namespace:WpfControlLibrary1;assembly=WpfControlLibrary1"
Title="MainWindow" Height="350" Width="525">
<external:UserControl1 local:HelpManager.HelpDescription="ExternalHelp"/>
</Window>

You can use it also in nested control (e.g. button within button)