Click here to Skip to main content
Click here to Skip to main content

Easy help with WPF

, 1 May 2009
Rate this:
Please Sign up or sign in to vote.
Adding simple HelpProvider functionality to your WPF applications couldn't be easier.

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
{
  /// <span class="code-SummaryComment"><summary>
</span>  /// This class provides the ability to easily attach Help functionality
  /// to Framework elements. To use it, you need to
  /// add a reference to the HelpProvider in your XAML. The FilenameProperty
  /// is used to specify the name of the helpfile, and the KeywordProperty specifies
  /// the keyword to be used with the search.
  /// <span class="code-SummaryComment"></summary>
</span>  /// <span class="code-SummaryComment"><remarks>
</span>  /// The FilenameProperty can be at a higher level of the visual tree than
  /// the KeywordProperty, so you don't need to set the filename each time.
  /// <span class="code-SummaryComment"></remarks>
</span>  public static class Help
  {
    /// <span class="code-SummaryComment"><summary>
</span>    /// Initialize a new instance of <span class="code-SummaryComment"><see cref="Help"/>.
</span>    /// <span class="code-SummaryComment"></summary>
</span>    static Help()
    {
      // Rather than having to manually associate the Help command, let's take care
      // of this here.
      CommandManager.RegisterClassCommandBinding(typeof(FrameworkElement),
        new CommandBinding(ApplicationCommands.Help,
          new ExecutedRoutedEventHandler(Executed),
          new CanExecuteRoutedEventHandler(CanExecute)));
    }

    #region Filename

    /// <span class="code-SummaryComment"><summary>
</span>    /// Filename Attached Dependency Property
    /// <span class="code-SummaryComment"></summary>
</span>    public static readonly DependencyProperty FilenameProperty =
      DependencyProperty.RegisterAttached("Filename", typeof(string), typeof(Help));

    /// <span class="code-SummaryComment"><summary>
</span>    /// Gets the Filename property.
    /// <span class="code-SummaryComment"></summary>
</span>    public static string GetFilename(DependencyObject d)
    {
      return (string)d.GetValue(FilenameProperty);
    }

    /// <span class="code-SummaryComment"><summary>
</span>    /// Sets the Filename property.
    /// <span class="code-SummaryComment"></summary>
</span>    public static void SetFilename(DependencyObject d, string value)
    {
      d.SetValue(FilenameProperty, value);
    }

    #endregion
   
    #region Keyword

    /// <span class="code-SummaryComment"><summary>
</span>    /// Keyword Attached Dependency Property
    /// <span class="code-SummaryComment"></summary>
</span>    public static readonly DependencyProperty KeywordProperty =
      DependencyProperty.RegisterAttached("Keyword", typeof(string), typeof(Help));

    /// <span class="code-SummaryComment"><summary>
</span>    /// Gets the Keyword property.
    /// <span class="code-SummaryComment"></summary>
</span>    public static string GetKeyword(DependencyObject d)
    {
      return (string)d.GetValue(KeywordProperty);
    }

    /// <span class="code-SummaryComment"><summary>
</span>    /// Sets the Keyword property.
    /// <span class="code-SummaryComment"></summary>
</span>    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)
    {
      // Call ShowHelp.
      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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Pete O'Hanlon
CEO
United Kingdom United Kingdom
A developer for over 30 years, I've been lucky enough to write articles and applications for Code Project as well as the Intel Ultimate Coder - Going Perceptual challenge. I live in the North East of England with 2 wonderful daughters and a wonderful wife.
 
I am not the Stig, but I do wish I had Lotus Tuned Suspension.
Follow on   Twitter   Google+

Comments and Discussions

 
GeneralCompile error PinmemberLOUIS Christian20-May-10 2:36 
QuestionCan you have way to achieve SetHelpString()? Pinmemberwmjcomcn21-Jul-09 16:19 
GeneralGood article. Pinmemberwmjcomcn12-Jul-09 20:28 
GeneralRe: Good article. PinmvpPete O'Hanlon12-Jul-09 21:54 
GeneralFilename can't be found. Pinmemberwmjcomcn9-Jul-09 23:54 
QuestionDo you have source code? Pinmemberwmjcomcn9-Jul-09 23:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140814.1 | Last Updated 1 May 2009
Article Copyright 2009 by Pete O'Hanlon
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid