If, like me, you like your applications to provide Context sensitive help, you’ve probably had a play around with the ApplicationCommands.Help
command. In order to simplify hooking your help into your application, I’ve written the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using form = System.Windows.Forms;
using System.Windows.Media;
namespace HelpProvider
{
public static class Help
{
static Help()
{
CommandManager.RegisterClassCommandBinding(typeof(FrameworkElement),
new CommandBinding(ApplicationCommands.Help,
new ExecutedRoutedEventHandler(Executed),
new CanExecuteRoutedEventHandler(CanExecute)));
}
#region Filename
public static readonly DependencyProperty FilenameProperty =
DependencyProperty.RegisterAttached("Filename", typeof(string), typeof(Help));
public static string GetFilename(DependencyObject d)
{
return (string)d.GetValue(FilenameProperty);
}
public static void SetFilename(DependencyObject d, string value)
{
d.SetValue(FilenameProperty, value);
}
#endregion
#region Keyword
public static readonly DependencyProperty KeywordProperty =
DependencyProperty.RegisterAttached("Keyword", typeof(string), typeof(Help));
public static string GetKeyword(DependencyObject d)
{
return (string)d.GetValue(KeywordProperty);
}
public static void SetKeyword(DependencyObject d, string value)
{
d.SetValue(KeywordProperty, value);
}
#endregion
#region Helpers
private static void CanExecute(object sender, CanExecuteRoutedEventArgs args)
{
FrameworkElement el = sender as FrameworkElement;
if (el != null)
{
string fileName = FindFilename(el);
if (!string.IsNullOrEmpty(fileName))
args.CanExecute = true;
}
}
private static void Executed(object sender, ExecutedRoutedEventArgs args)
{
DependencyObject parent = args.OriginalSource as DependencyObject;
string keyword = GetKeyword(parent);
if (!string.IsNullOrEmpty(keyword))
{
form.Help.ShowHelp(null, FindFilename(parent), keyword);
}
else
{
form.Help.ShowHelp(null, FindFilename(parent));
}
}
private static string FindFilename(DependencyObject sender)
{
if (sender != null)
{
string fileName = GetFilename(sender);
if (!string.IsNullOrEmpty(fileName))
return fileName;
return FindFilename(VisualTreeHelper.GetParent(sender));
}
return null;
}
#endregion
}
}
Using it couldn’t be simpler, set up the Filename
in your XAML and add any keywords you need to search on against your FrameworkElement
items. The advantage of this approach is that you can bind different parts of your UI to different helpfiles if you want.
<Window
x:Class="HelpSample.Window1"
xmlns="<a href="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
http://schemas.microsoft.com/winfx/2006/xaml/presentation</a>"
xmlns:x="<a href="http://schemas.microsoft.com/winfx/2006/xaml">
http://schemas.microsoft.com/winfx/2006/xaml</a>"
Title="Window1" Height="300" Width="300"
xmlns:help="clr-namespace:HelpProvider;assembly=HelpProvider"
help:Help.Filename="MyHelpfile.chm"
>
I hope this helps you as much as it helps me.