question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Task Dialog: Improve hyperlink usage

See original GitHub issue

⏬⏬ CLICK HERE TO SCROLL DOWN TO THE FINAL PROPOSAL ⏬⏬


Hi,

as requested by @RussKie in https://github.com/dotnet/winforms/issues/146#issuecomment-502493259, I’ve opened a new issue for this topic. Note: The listed options don’t need to change the currently proposed API in an incompatible way, so this issue can be addressed at a later time.

The native Windows Task Dialog allows to use hyperlinks in certain text elements (text/content, footer, expander) by setting the TDF_ENABLE_HYPERLINKS flag. This allows you to specify a link with the form <a href="mylink">text</a> (similar to HTML). When the user clicks on the link, the TDN_HYPERLINK_CLICKED is sent to the callback with the content of the href attribute as parameter.

This is reflected in the currently proposed API in #146: To use links, you set the TaskDialogPage.HyperlinksEnabled property to true, and then you can set e.g.

page.Text = "This text contains <a href=\"link1\">a link</a>.";

to show a link in the task dialog text:

grafik

Then, you have to handle the TaskDialogPage.HyperlinkClicked event to get notified when the user clicks a link. That event will provide you the text of the href attribute, in that case link1.

However, there are some problems with this approach (as is simply a mapping of the native API):

  • Users need to know the exact syntax to specify a link (which is similar to HTML), and in source code you need to escape the double quotes.
  • There is a risk that when you enable hyperlinks and then show strings from external sources, they might get misinterpreted as hyperlink even if you actually just want it to show as plain text. As noted in the documentation for TDF_ENABLE_HYPERLINKS, this could cause security vulnerabilities, for example if you did something like Process.Start(e.Hyperlink) in the HyperlinkClicked event. (A safer way is not to use links like https://... directly in the href, but instead use keys like linkA, and then in the event, compare the key to open the corresponding link.)
  • In order to prevent strings from external sources to be interpreted as links (or when you actually want to display a string like <a href="..."></a> as part of the text directly without interpreting as hyperlink), you need to replace "<a" e.g. with "<\u200Ba" (zero width space), so that the task dialog interprets < as literal character. A minor downside of this is that if you then copy the dialog’s text contents with <kbd>Ctrl</kbd>+<kbd>C</kbd>, that invisible character is also copied. (I didn’t find another way for this, as the HTML/XML-like &lt; doesn’t seem to work; and while replacing "<a" with "<<a></a>a" (which I thought previously) would fix the copy problem with the invisible space, it cannot be used as it creates empty hyperlinks that are reachable by pressing <kbd>Tab ↹</kbd>.)
  • When you enable HyperlinksEnabled and there is at least one link (<a ...>) present in the text, an & character will be interpreted to specify a mnemonic char, instead of displaying it directly (which is the behavior when HyperlinksEnabled is not set), although then actually nothing happens when you press <kbd>Alt</kbd> and the character. Therefore, you would generally need to replace & with && in the strings when HyperlinksEnabled is set and you have at least one link in the text. (But if you then copy the task dialog’s text contents with <kbd>Ctrl</kbd>+<kbd>C</kbd>, it will copy the escaped form (&&) instead of the displayed form (&).)

Option A

As suggested by @GSPP: Don’t change the existing API, but add new methods that can be used to retrieve strings with the correct syntax when EnableHyperlinks is set, for example::

    public static string GetHyperlinkText(string href, string text)
    {
        // Don't allow "</a>" in the link text because we cannot escape that.
        if (text.Contains("</a>", StringComparison.OrdinalIgnoreCase))
            throw new ArgumentException();
        if (href.Contains("\"", StringComparison.OrdinalIgnoreCase))
            throw new ArgumentException();

        return $"<a href=\"{href}\">{text.Replace("&", "&&")}</a>";
    }

    public static string GetNonHyperlinkText(string text)
    {
        return text.Replace("&", "&&").Replace("<a", "<\u200Ba").Replace("<A", "<\u200BA");
    }

Then, you could set the text like this:

    page.Text = TaskDialog.GetHyperlinkText("myLink", "Hello World & others!") +
        TaskDialog.GetNonHyperlinkText(" External text: <a href=\"test\">&</a>");

grafik

With the helper methods, you wouldn’t need to specify the exact syntax for the link and you wouldn’t need to escape special characters like < by yourself.

Option B

As suggested by @jnm2: Also don’t change the existing API but add a new method

    public static string FormatWithHyperlinks(FormattableString str)

on the TaskDialog which you then could call with an interpolated string, that takes the href as argument and the link text as format string (or the other way round):

    page.Text = TaskDialog.FormatWithHyperlinks(
        $"Some text, then {"https://foo/":a link} & {"https://bar/":another link}; <a href=\"x&y\">no link</a>");

    // Or:
    page.Text = TaskDialog.FormatWithHyperlinks(
        $"Some text, then {"a link":https://foo/} & {"another link":https://bar/}; <a href=\"x&y\">no link</a>");

grafik

(However, with this option there will probably need to be another method that allows to escape strings (like GetNonHyperlinkText() in Option A) if you then want to embed other strings which shouldn’t get interpreted as link.)

Thanks!

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:64 (63 by maintainers)

github_iconTop GitHub Comments

5reactions
RussKiecommented, Apr 19, 2021

We’re proceeding with the following API, which is very much what the original proposal from @kpreisser was: kpreisser@187bdb4.

I acknowledge the API discoverability is a little vague, but having read the docs once on how to use the feature developers shouldn’t have any troubles using it again. We currently won’t be providing any helper methods to construct HREFs.

We can revisit the API and any helper functionality, if/when we detect a significant uptake of this API.

Proposed API

+    /// <summary>
+    /// Provides data for the <see cref="TaskDialogPage.LinkClicked"/> event.
+    /// </summary>
+    public class TaskDialogLinkClickedEventArgs : EventArgs
+    {
+        /// <summary>
+        /// Gets the value of the <c>target</c> attribute of the link that the user clicked.
+        /// </summary>
+        /// <remarks>
+        /// Note: In order to avoid possible security vulnerabilities when showing content
+        /// from unsafe sources in a task dialog, you should always verify the value of this
+        /// property before actually opening the link.
+        /// </remarks>
+        public string LinkTarget { get; }
+    }

     public class TaskDialogPage
     {
+        /// <summary>
+        ///   Occurs when the user has clicked on a link.
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        ///   This event will only be raised if <see cref="EnableLinks"/> is set to <see langword="true"/>.
+        /// </para>
+        /// </remarks>
+        public event EventHandler<TaskDialogLinkClickedEventArgs>? LinkClicked;
+
+        /// <summary>
+        /// <para>
+        ///   Gets or sets a value that specifies whether the task dialog should
+        ///   interpret strings in the form <c>&lt;a href="target"&gt;link Text&lt;/a&gt;</c>
+        ///   as hyperlink when specified in the <see cref="Text"/>,
+        ///   <see cref="TaskDialogFooter.Text"/>
+        ///   or <see cref="TaskDialogExpander.Text"/> 
+        ///   or <see cref="TaskDialogFootnote.Text"/> properties.
+        ///   When the user clicks on such a link, the <see cref="LinkClicked"/>
+        ///   event is raised, containing the value of the <c>target</c> attribute.
+        /// </para>
+        /// </summary>
+        /// <value>
+        ///   <see langword="true"/> to enable links; otherwise, <see langword="false"/>.
+        ///   The default value is <see langword="false"/>.
+        /// </value>
+        /// <remarks>
+        /// <para>
+        ///   The Task Dialog will not actually execute any links.
+        ///   Link execution must be handled in the <see cref="LinkClicked"/> event.
+        /// </para>
+        /// <para>
+        ///   Note: Enabling this setting causes the <c>"&amp;"</c> character to be
+        ///   interpreted as prefix for an access key character (mnemonic) if at least
+        ///   one link is used.
+        /// </para>
+        /// <para>
+        ///   When you enable this setting and you want to display a text
+        ///   without interpreting links, you must replace the strings <c>"&lt;a"</c>
+        ///   and <c>"&lt;A"</c> with something like <c>"&lt;\u200Ba"</c>.
+        /// </para>
+        /// </remarks>
+        public bool EnableLinks { get; set; }
     }
4reactions
RussKiecommented, Sep 23, 2020

I’m flat out busy with prep’ing the 5.0 release, and I don’t have any capacity for this until we ship. Sorry for the delay.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Hyperlinks in CTaskDialog footer - mfc
The OnHyperlinkClick is generic so if you have multiple links on the task dialog you might have to test the phrase to decide...
Read more >
Task Dialog - Win32 apps
The task dialog contains application-defined icons, messages, title, verification check box, command links, push buttons, and radio buttons.
Read more >
Introducing TaskDialogLib: Task Dialogs in XAML
Task dialogs allow you to place inline hyperlinks in certain text sections, namely Content , ExpandedInformation , and Footer . In the native ......
Read more >
About Task Dialogs - Win32 apps
A task dialog is a dialog box that can be used to display information and receive simple input from the user.
Read more >
Hyperlink in task instructions
Solved: I am trying to add an hyperlink text, to task instructions/description (using string.link()) in Workflow, but the field renders as ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found