Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Integrating help in a WPF application

Tags:

wpf

chm

what are the possible approaches to integrate local (so not on-line) help in a WPF application? It would be more like a manual, but I would like to integrate it in some way.

EDIT: just found http://wordtoxaml.codeplex.com, I will try that one. It converts word document into xaml, which I can display in WPF.

EDIT 2: I found a working solution: write manual in word, save as XPS, and display it using https://web.archive.org/web/20111116005415/http://www.umutluoglu.com/english/post/2008/12/20/Showing-XPS-Documents-with-DocumentViewer-Control-in-WPF.aspx

like image 719
L-Four Avatar asked Feb 25 '11 11:02

L-Four


People also ask

Is WPF still relevant 2021?

WPF is still one of the most used app frameworks in use on Windows (right behind WinForms).

Is it possible to use Windows Forms in a WPF application?

You can use the WindowsFormsHost to add single Forms into an WPF application.


2 Answers

We use RoboHelp and generate a chm file, sometimes referred to as an HTML Help file. The .NET Framework's Help class has a method ShowHelp that you call, passing the chm file and the topic you want to display. You can tell it to display by topic title, by ID etc. We display using the topic title so the call looks like this:

System.Windows.Forms.Help.ShowHelp(null, "Help/ExiaProcess.chm", HelpNavigator.Topic, helpTopic);

Next you can create a class called HelpProvider that creates an attached property called HelpTopic. This allows you to attach a HelpTopic property to any FrameworkElement. The class also uses the static constructor to hook the built-in F1 help command to command handlers that retrieve the attached property from the source and open the help.

using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;

/// <summary>
/// Provider class for online help.  
/// </summary>
public class HelpProvider
{
    #region Fields

    /// <summary>
    /// Help topic dependency property. 
    /// </summary>
    /// <remarks>This property can be attached to an object such as a form or a textbox, and 
    /// can be retrieved when the user presses F1 and used to display context sensitive help.</remarks>
    public static readonly DependencyProperty HelpTopicProperty = 
        DependencyProperty.RegisterAttached("HelpTopic", typeof(string), typeof(HelpProvider));

    #endregion Fields

    #region Constructors

    /// <summary>
    /// Static constructor that adds a command binding to Application.Help, binding it to 
    /// the CanExecute and Executed methods of this class. 
    /// </summary>
    /// <remarks>With this in place, when the user presses F1 our help will be invoked.</remarks>
    static HelpProvider()
    {
        CommandManager.RegisterClassCommandBinding(
            typeof(FrameworkElement),
            new CommandBinding(
                ApplicationCommands.Help,
                new ExecutedRoutedEventHandler(ShowHelpExecuted),
                new CanExecuteRoutedEventHandler(ShowHelpCanExecute)));
    }

    #endregion Constructors

    #region Methods

    /// <summary>
    /// Getter for <see cref="HelpTopicProperty"/>. Get a help topic that's attached to an object. 
    /// </summary>
    /// <param name="obj">The object that the help topic is attached to.</param>
    /// <returns>The help topic.</returns>
    public static string GetHelpTopic(DependencyObject obj)
    {
        return (string)obj.GetValue(HelpTopicProperty);
    }

    /// <summary>
    /// Setter for <see cref="HelpTopicProperty"/>. Attach a help topic value to an object. 
    /// </summary>
    /// <param name="obj">The object to which to attach the help topic.</param>
    /// <param name="value">The value of the help topic.</param>
    public static void SetHelpTopic(DependencyObject obj, string value)
    {
        obj.SetValue(HelpTopicProperty, value);
    }

    /// <summary>
    /// Show help table of contents. 
    /// </summary>
    public static void ShowHelpTableOfContents()
    {
        System.Windows.Forms.Help.ShowHelp(null, "Help/ExiaProcess.chm", HelpNavigator.TableOfContents);
    }

    /// <summary>
    /// Show a help topic in the online CHM style help. 
    /// </summary>
    /// <param name="helpTopic">The help topic to show. This must match exactly with the name 
    /// of one of the help topic's .htm files, without the .htm extention and with spaces instead of underscores
    /// in the name. For instance, to display the help topic "This_is_my_topic.htm", pass the string "This is my topic".</param>
    /// <remarks>You can also pass in the help topic with the underscore replacement already done. You can also 
    /// add the .htm extension. 
    /// Certain characters other than spaces are replaced by underscores in RoboHelp help topic names. 
    /// This method does not yet account for all those replacements, so if you really need to find a help topic
    /// with one or more of those characters, do the underscore replacement before passing the topic.</remarks>
    public static void ShowHelpTopic(string helpTopic)
    {
        // Strip off trailing period.
        if (helpTopic.IndexOf(".") == helpTopic.Length - 1)
            helpTopic = helpTopic.Substring(0, helpTopic.Length - 1);

        helpTopic = helpTopic.Replace(" ", "_").Replace("\\", "_").Replace("/", "_").Replace(":", "_").Replace("*", "_").Replace("?", "_").Replace("\"", "_").Replace(">", "_").Replace("<", "_").Replace("|", "_") + (helpTopic.IndexOf(".htm") == -1 ? ".htm" : "");
        System.Windows.Forms.Help.ShowHelp(null, "Help/ExiaProcess.chm", HelpNavigator.Topic, helpTopic);
    }

    /// <summary>
    /// Whether the F1 help command can execute. 
    /// </summary>
    private static void ShowHelpCanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        FrameworkElement senderElement = sender as FrameworkElement;

        if (HelpProvider.GetHelpTopic(senderElement) != null)
            e.CanExecute = true;
    }

    /// <summary>
    /// Execute the F1 help command. 
    /// </summary>
    /// <remarks>Calls ShowHelpTopic to show the help topic attached to the framework element that's the 
    /// source of the call.</remarks>
    private static void ShowHelpExecuted(object sender, ExecutedRoutedEventArgs e)
    {
        ShowHelpTopic(HelpProvider.GetHelpTopic(sender as FrameworkElement));
    }

    #endregion Methods
}

With that in place, you can call your help from code like this:

private void HelpButton_Click(object sender, RoutedEventArgs e)
{
    Help.HelpProvider.ShowHelpTopic("License Key Dialog");
}

What's even nicer, now you can attach help to any FrameworkElement in your UI like this,

<Window name="MainWin"
    ...
    ...
    xmlns:help="clr-namespace:ExiaProcess.UI.Help"
    ...
    ...
    help:HelpProvider.HelpTopic="Welcome to YourApp" />      
    ...
    ...
    <TextBox help:HelpProvider.HelpTopic="Bug Title" />
    ...
    ...
    <ComboBox help:HelpProvider.HelpTopic="User Drop Down"/>
    ...

Now when the user presses F1 on the windows or any element, they'll get context-sensitive help.

like image 75
Nigel Shaw Avatar answered Sep 28 '22 19:09

Nigel Shaw


I had a similar need except I only needed to wire up the F1 key to our existing Help code.

I ended up pulling a mix of about 5 different StackOverflow pages so I'm putting it here in case someone else has a similar need.

In my MainWindow.xaml I added a KeyBinding in inputBindings to wire the F1 to an ICommand:

<Window.InputBindings>
    (other bindings here...)
    <KeyBinding Key="F1" Command="{Binding Path=ShowHelpCommand}"/>
</Window.InputBindings>

Then in my MainWindowViewModel.cs I added this ICommand which calls my existing Help code.

    private ICommand _showHelpCommand;
    public ICommand ShowHelpCommand
    {
        get
        {
            return _showHelpCommand ??
                   (_showHelpCommand = new RelayCommand(p => DisplayCREHelp(), p => true));
        }
    }

I hope this helps anyone with a similar issue.

like image 39
CodeChops Avatar answered Sep 28 '22 21:09

CodeChops