views:

969

answers:

2

I'm using the Windows.Forms WebBrowser control in edit mode to enable html editing in our application (.net 3.5 C# windows forms). The problem is that in edit mode, the links in the html are not clickable (i.e. hovering over them does not show the hand mouse cursor, and click them just inserts the cursor at that spot, rather than navigating to that link).

I realize that this is by design, but is it possible to enable edit mode while keeping the links functional? We want the user to navigate around a set of local html pages while able to edit the content.

I'm setting edit mode like this, if its changes anything:

webBrowser.Document.ExecCommand("EditMode", false, null);
A: 

I would check for when the user hovers over a link, and optionally show something in the status bar. Allow the user to CTRL+click the link to open it.

EDIT: This may come in useful: http://www.codeproject.com/KB/mobile/browsermouseevents.aspx?msg=2732899

AndrewVos
Thanks Andrew, yes, I've already played around with that, and got it working by finding the element under the mouse cursor. Problem is that the users want a hand cursor, and setting the cursor property of the webbrowser doesn't work. Setting the cursor of the parent control (a panel) causes something to go wonky, and the entire app crashes. Ideally, I'd like something that is supported within the browser control, but if this is the best way, thats what will have to happen :)
Gareth
+1  A: 

Here's a small WinForm App which seems to work. When setting the edit mode it record all the link positions of all A tags and then check the cursor position of the mouse. I'm not sure if OffsetRectangle gives correct values if there is frames or nesting of tags, but the sample html in the app works.

It could be modified to capture onclick etc from other tags if needed.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
 public partial class Form1 : Form
 {
  private readonly Dictionary<Rectangle, Uri> _links = new Dictionary<Rectangle, Uri>();
  private readonly Regex _reLink = new Regex("href=['\"](?<link>.*?)['\"]");

  private const string _html =
   "<html><body><br><div style='top:20px;left:50px;border:solid 1px red'><a href='http://www.cnn.com'&gt;CNN&lt;/a&gt;&lt;/div&gt;&lt;br&gt;&lt;font size='14'>xx</font><a href='http://stackoverflow.com'&gt;stackoverflow&lt;/a&gt;&lt;/body&gt;&lt;/html&gt;";

  private bool _isEditMode;

  public Form1()
  {
   InitializeComponent();
   webBrowser1.DocumentText = _html;
   webBrowser1.Document.Click += Document_Click;
   webBrowser1.Document.MouseMove += Document_MouseMove;
  }

  private void Document_MouseMove(object sender, HtmlElementEventArgs e)
  {
   if (!_isEditMode) return;
   ChangeCursorIfOverLink(e);
  }

  private void ChangeCursorIfOverLink(HtmlElementEventArgs e)
  {
   foreach (KeyValuePair<Rectangle, Uri> link in _links)
   {
    if (CursorWithinControl(e, link.Key))
    {
     if (Cursor.Current != Cursors.Hand)
      Cursor.Current = Cursors.Hand;
     return;
    }
   }
   Cursor.Current = Cursors.Default;
  }

  private void Document_Click(object sender, HtmlElementEventArgs e)
  {
   NavigateLinkInEditMode(e);
  }

  private void NavigateLinkInEditMode(HtmlElementEventArgs e)
  {
   if (_isEditMode)
   {
    foreach (KeyValuePair<Rectangle, Uri> link in _links)
    {
     if (CursorWithinControl(e, link.Key))
     {
      webBrowser1.Navigate(link.Value);
      return;
     }
    }
   }
  }

  private bool CursorWithinControl(HtmlElementEventArgs e, Rectangle rectangle)
  {
   return e.MousePosition.X >= rectangle.Left
       && e.MousePosition.X <= rectangle.Left + rectangle.Width
       && e.MousePosition.Y >= rectangle.Top
       && e.MousePosition.Y <= rectangle.Top + rectangle.Height;
  }

  private void button1_Click(object sender, EventArgs e)
  {
   RecordLinkPositions();
   webBrowser1.Document.ExecCommand("EditMode", false, null);
   webBrowser1.DocumentText = _html;
   _isEditMode = true;
  }

  private void RecordLinkPositions()
  {
   foreach (HtmlElement element in webBrowser1.Document.All)
   {
    if (element.TagName == "A")
    {
     string url = _reLink.Match(element.OuterHtml).Groups["link"].Value;
     _links.Add(element.OffsetRectangle, new Uri(url));
    }
   }
  }
 }
}
Mikael Svenson
Awesome! Thanks, this works. I've also found that there is a method on the document called `GetElementFromPoint` that enables me to get away from having to store the element positions. The main idea here is that you're setting Cursor.Current, and not the web browser control's Cursor property, which was causing it to crash for me.
Gareth
In my sleep I came to think about an issue you need to think about. When the user actually edits, the OffsetRectangles will move. This could be fixed by having two webcontrols, where one is hidden. Every time a keystroke is entered in the EditMode one, you copy the content over to the hidden, and recreate the Offset list. Let me know if you need a sample of this.
Mikael Svenson