How to highlight HTML syntax in C# windows form RichTextBox? - c#

I am writing a c# Html editor application, in which you type the code in a RichTextBox control. I want the RichTextBox to behave like notepad++ and other code editors in which the Html syntax gets highlighted in colors, like this for example:
How can I establish this in C# windows form RichTextBox? I have searched almost everywhere and didn't find anything that helped me. This is what I tried so far but I doesn't give the result I want:
private void SyntaxHighlight()
{
string[] tags = { "html","head","body","a","b","img","strong","p","h1","h2","h3","h4","h5","h6","embed","iframe","span","form",
"button","input","textarea","br","div","style","script","table","tr","td","th","i","u","link","meta","title"};
foreach (string s in tags)
{
richTextBox1.Find("<" + s);
richTextBox1.SelectionColor = Color.Blue;
richTextBox1.Find(">");
richTextBox1.SelectionColor = Color.Blue;
}
string[] attributes = { "href","src","height","width","rowspan","colspan","target","style","onclick","id","name","class"};
foreach (string s in attributes)
{
richTextBox1.Find(s + "=");
richTextBox1.SelectionColor = Color.Red;
}
}
Can someone help me? What should I write inside the SyntaxHighlight() method? can someone give me the appropriate code?
Thank you

Within your code you are only finding the 1st occurrence of the HTML tag and highlighting it. But instead, you should loop through the entire rich text content to finding proceeding occurrences of the same text. I just did a quick mock based on your exact code, please check it out.
private void highlightHTMLText()
{
string[] tags = { "html","head","body","a","b","img","strong","p","h1","h2","h3","h4","h5","h6","embed","iframe","span","form",
"button","input","textarea","br","div","style","script","table","tr","td","th","i","u","link","meta","title"};
foreach (string s in tags)
{
findAndHighlight("<" + s, Color.Blue);
findAndHighlight("</" + s, Color.Blue);
findAndHighlight(">", Color.Blue);
}
string[] attributes = { "href", "src", "height", "width", "rowspan", "colspan", "target", "style", "onclick", "id", "name", "class" };
foreach (string s in attributes)
{
findAndHighlight(s + "=", Color.Red);
}
}
private void findAndHighlight(string sSearchStr, Color oColor)
{
int index = richTextBox1.Text.IndexOf(sSearchStr);
while (index != -1)
{
richTextBox1.Select(index, sSearchStr.Length);
richTextBox1.SelectionColor = oColor;
index = richTextBox1.Text.IndexOf(sSearchStr, index + sSearchStr.Length);
}
}
Further as per this answer you should be able to make use of the same utility library Scintilla used by Notepad++ itself. As pointed out you do not need to re-invent the wheel, but as a developer I obviously prefer my own util (it is just me ;) ). Hope this helps.

Find doesn't move a cursor, it returns the location of the first match. Try this instead:
How to select text from the RichTextBox and then color it?

A little late to the party but, after wanting to create my own offline version of CodePen, I implemented my own version of html syntax highlighting following CodePen's theme.
This does syntax highlighting and markup formatting, though the formatting depends on whether or not your html is well-formed.
Just add this as a class for your RichTextBox, instantiate it accordingly and call it within whichever event works for you (I'm using it with the RTB's double_click event but that does eliminate double-click text selection). What I'm planning to do is add a timer, some boolean variables and work this within the key_up and key_down events to set the highlight update to be a bit more automatic and less intrusive on shortcuts. (which is hereby included below the class)
public void HighlightHTM(RichTextBox Htm_Input)
{
Htm_Input.Visible = false;
#region store the original caret position + forecolor
int originalIndex = Htm_Input.SelectionStart;
int originalLength = Htm_Input.SelectionLength;
Color originalColor = Color.FromArgb(200, 200, 200); // Grey
#endregion
#region try to format the markup
try { Htm_Input.Text = XElement.Parse(Htm_Input.Text).ToString(); } catch { }
#endregion
#region match everything but puncutation and equals
Regex e = new Regex(#"(.*?|=)[^\w\s]");
MatchCollection eMatches = e.Matches(Htm_Input.Text);
foreach (Match m in eMatches)
{
Htm_Input.SelectionStart = m.Groups[1].Index;
Htm_Input.SelectionLength = m.Groups[1].Length;
Htm_Input.SelectionColor = Color.FromArgb(221, 202, 126); // Yellow
}
#endregion
#region match tags
Regex t = new Regex(#"(<\w+|</\w+|/>|>)[^=]");
MatchCollection tMatches = t.Matches(Htm_Input.Text, 0);
foreach (Match m in tMatches)
{
Htm_Input.SelectionStart = m.Groups[1].Index;
Htm_Input.SelectionLength = m.Groups[1].Length;
Htm_Input.SelectionColor = Color.FromArgb(167, 146, 90); // Brown
}
#endregion
#region match quotes
Regex q = new Regex("\".*?\"");
MatchCollection qMatches = q.Matches(Htm_Input.Text);
foreach (Match m in qMatches)
{
Htm_Input.SelectionStart = m.Index;
Htm_Input.SelectionLength = m.Length;
Htm_Input.SelectionColor = Color.FromArgb(150, 179, 138); // Green
}
#endregion
#region match inner html
Regex h = new Regex(">(.+?)<");
MatchCollection hMatches = h.Matches(Htm_Input.Text);
foreach (Match m in hMatches)
{
Htm_Input.SelectionStart = m.Groups[1].Index;
Htm_Input.SelectionLength = m.Groups[1].Length;
Htm_Input.SelectionColor = Color.FromArgb(200, 200, 200); // Grey
}
#endregion
#region restoring the original colors, for further writing
Htm_Input.SelectionStart = originalIndex;
Htm_Input.SelectionLength = originalLength;
Htm_Input.SelectionColor = originalColor; // Light Grey
#endregion
Htm_Input.Focus();
Htm_Input.Visible = true;
}
Happy coding!
Edit: I should also mention that !doctype breaks formatting as it's not exactly xml-friendly in the context of "well-formed". For my purposes, all tags including body and relevant closings, css and js links are added programmatically at page save so only markup within the body tags are worked with inside the html RTB. This eliminates that problem.
You'll notice that this relies exclusively on Regex rather than on hard-coded tags and properties. I did this because tags and properties have a tendency to pop on and off the w3 scene quite often. That would force a dev to continually have to go back and edit those strings to remove deprecated tags / properties or to add new. Not optimal.
I also thought it prudent to go ahead and include the instantiation / usage examples to make this a bit more plug&play.
Above public Main(), instantiate like so:
#region Class Instantiation
SyntaxHighlight syntax = new SyntaxHighlight();
#endregion
... and, within your chosen event handler, call it like so:
private void htm_input_DoubleClick(object sender, EventArgs e)
{
syntax.HighlightHTM(Htm_Input);
}
Naturally, adding a SaveFileDialog and an OpenFileDialog pretty much provides this the functionality of your very own, albeit very basic, html editor. Toss in a WebBrowser control and apply the RTB's text as the WebBrowser's source and you've upgraded to live-view.
In the very least, this should serve as a viable reference for syntax highlighting in general. It really just boils down to identifying patterns and manipulating their colors so, for example, this will work effectively with css, javascript and even C# with some light adjusting of the pattern identification parameters.
The following is how I setup the automatic refresh with key_up / key_down and a timer set to 1000 ms:
#region Globals
int r = 0;
bool refresh = false;
#endregion
private void Htm_Input_KeyUp(object sender, KeyEventArgs e)
{
refresh = true; // enter refresh cycle
}
private void Htm_Input_KeyDown(object sender, KeyEventArgs e)
{
refresh = false; // abort refresh cycle
}
private void Timer_Refresh_Tick(object sender, EventArgs e)
{
// check if refresh cycle is entered, refresh at 3 seconds or reset the counter if aborted
if (refresh) { if (r == 3) { syntax.HighlightHTM(Htm_Input); refresh = false; r = 0; } r++; } else { r = 0; }
}

Related

C# - Button Placement inside a RichtextBox

I'm currently struggling with a button I want to add dynamically inside a RichTextBox.
The main issue is that I can't seem to get the location down properly (Location X & Y) also that the Text adjusts with Form Changes but the location of the Buttons won't.
The concept behind this is that I want to be able to write RTF Documents and include Button Clicks for certain Functions (Copy for now) without having to adjust the program every time.
My idea was to have a very specific Pattern -> [Copy|WHAT-TO-COPY] and replace every occurrence of this with a [C] Button in the application.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
LoadRTF();
AddButtons();
}
private void LoadRTF()
{
richTextBox1.LoadFile("RTF Path");
richTextBox1.ReadOnly = true;
}
private void AddButtons()
{
String Text = richTextBox1.Rtf;
richTextBox1.ReadOnly = false;
foreach (Match CopyMatch in Regex.Matches(Text, #"\[Copy\|.*\]", RegexOptions.IgnoreCase))
{
string CopyString = CopyMatch.Value;
var CopyPosition = richTextBox1.GetPositionFromCharIndex(richTextBox1.Find(CopyString));
Button Copy = new Button
{
Text = "Copy",
Size = new Size(20, 23),
Location = new Point(CopyPosition.X, CopyPosition.Y)
};
Copy.Click += (_, args) =>
{
Clipboard.SetText(CopyString.Substring(6, CopyString.Length - 7));
};
richTextBox1.SelectionStart = richTextBox1.Find(CopyString);
richTextBox1.SelectionLength = CopyString.Length;
richTextBox1.SelectedText = "";
richTextBox1.Controls.Add(Copy);
}
richTextBox1.ReadOnly = true;
}
}
The first issue is that this method apparently doesn't work to get the proper X,Y values for the Button Creation. It seems the values don't align with the found PositionFromChar(Index).
Am I headed in the entirely wrong direction is this a proper start? What am I missing?
Also, I'm still completely new to programming so sorry if this is a total mess.

Show tooltip in LineSeries WinForms Chart?

I am working on a Dashboard System where i am using Line Chart in WinForms. I need to show the tooptip on each line. I have tried this
var series = new Series
{
Name = chartPoint.SetName,
Color = chartPoint.ChartColor,
ChartType = SeriesChartType.Line,
BorderDashStyle = chartPoint.ChartDashStyle,
BorderWidth = chartPoint.BorderWidth,
IsVisibleInLegend = !chartPoint.HideLegend,
ToolTip = "Hello World"
};
but its not working for me
You have two options either use Keywords accepted by the Chart control.
myChart.Series[0].ToolTip = "Name #SERIESNAME : X - #VALX{F2} , Y - #VALY{F2}";
In the Chart control, a Keyword is a character sequence that is replaced with an automatically calculated value at run time. For a comprehensive list of keywords accepted by the Chart control look up Keyword reference
or
if you want something more fanciful, you have to handle the event GetToolTipText
this.myChart.GetToolTipText += new System.Windows.Forms.DataVisualization.Charting.Chart.ToolTipEventHandler(this.myChart_GetToolTipText);
Now I am not sure what you want to show on the ToolTip but you could add the logic accordingly. Assuming you want to show the values of the DataPoints in the series
private void myChart_GetToolTipText(object sender, System.Windows.Forms.DataVisualization.Charting.ToolTipEventArgs e)
{
switch(e.HitTestResult.ChartElementType)
{
case ChartElementType.DataPoint:
e.Text = myChart.Series[0].Points[e.HitTestResult.PointIndex]).ToString()
+ /* something for which no keyword exists */;
break;
case ChartElementType.Axis:
// add logic here
case ....
.
.
default:
// do nothing
}
After some RnD i got tooltips on Line Series, but still confused why its not working with this solution.
Here is the solution
series.ToolTip = string.Format("Name '{0}' : X - {1} , Y - {2}", chartPoint.SetName, "#VALX{F2}",
"#VALY{F2}");
mainChartControl.GetToolTipText += ChartControlGetToolTipText;
private void ChartControlGetToolTipText(object sender, ToolTipEventArgs e)
{
}

c# - Is there A Faster Way to Highlighting Regex Matches (RichTextBox)

I use RichTextBox for testing REGEX expression, with the code like:
rtbMain.SelectAll();
rtbMain.SelectionColor = Color.Black;
rtbMain.SelectionBackColor = Color.White;
Regex regex = new Regex(txtRegexPattern.Text, regexOptions);
Match matches = regex.Match(txtTest.Text);
while (matches.Success)
{
rtbMain.Select(matches.Index, match.Length);
rtbMain.SelectionColor = Color.Red;
rtbMain.SelectionBackColor = Color.Black;
}
But this method becomes too slow as soon as there are more than a few thousand (1000+) characters to be highlighted. I know I could delay processing, so that code gives user a chance to enter the whole Regular Expression, but still I think RichTextBox highlighting is working too slow.
I've searched the Google for different approaches and ways to speed up current solution, but I didn't have luck. I noticed that there are a few text editors which allow "syntax highlighting" (like ScintillNET, Avalon,...) but they use XML as input, so I think using them for my project (generating XML on every KeyUp event) wouldn't be the "best practice".
I have found and tested a "Fast colored Textbox" here: https://github.com/PavelTorgashov/FastColoredTextBox ...but the problem with this one is that it replaces the paste content while it uses its own new-line and tab character, and I cant use it in REGEX tester.
Is there any faster way to highlight all matches, maybe using a different user control?
EDIT:
APPROACH 1: Would generating the underlying RTF document be faster? I tried but had some problems with special characters, so I could test highlighting of the whole document, but it seemed to work quite fast with normal characters in a single line. I paused working on this since I read constructing RTF's can be quite hard, and I think I couldn't use none of the existing RTF libraries.
APPROACH 2: I am able to get only the displayed portion of RichTextBox, so I was thinking to only highlight that part. I guess this would significantly reduce processing (depends on RTB size), but I would need to trigger highlighting every time user scrolls; I'm not sure this would work well and create a decent user experience, so haven't tried it out yet.
Would anyone recommend any of the approaches above or maybe any others?
First:
The RichTextBox has an inherent problem: It is very slow in .NET. I found a solution how to make it 120 times faster. May be you try it out: C# RichEditBox has extremely slow performance (4 minutes loading) SOLVED
Second:
Building the RTF code from the scratch is far the fastest solution. Have a look a my article on codeproject. There is a RTF builder class that is reusable: http://www.codeproject.com/Articles/23513/SQL-Editor-for-Database-Developers
Please check Expresso at http://www.codeproject.com/Articles/3669/Expresso-A-Tool-for-Building-and-Testing-Regular-E
I have been using this program for editing and evaluating regex for years.
I have a doubt that you have setup your While loop in an incorrect manner.
Try something like this: (Untested, but will give you an idea how to troubleshoot this problem)
rtbMain.SelectAll();
rtbMain.SelectionColor = Color.Black;
rtbMain.SelectionBackColor = Color.White;
Regex regex = new Regex(txtRegexPattern.Text, regexOptions);
MatchCollection matches = regex.Matches(txtTest.Text);
if(matches.Count > 0)
{
foreach(Match m in matches)
{
rtbMain.Select(m.Index, m.Length);
rtbMain.SelectionColor = Color.Red;
rtbMain.SelectionBackColor = Color.Black;
}
}
else
{
Debug.Print("No matches found"); // See "Output" Window
}
EDIT
I did some workaround related to highlight RTF text and first thing I found is the mostly time taken by the process was these lines:
rtbMain.SelectionColor = Color.Red;
rtbMain.SelectionBackColor = Color.Black;
I tried selecting the text using SelectionStart and SelectionEnd properties instead .Select(), but NO change has been observed.
Regarding your first point which is related to constructing equivalent RTF, I tried that too but it is difficult to construct an equivalent RTF since there are lot of stuff there which needs to be handle. If it can be done the process time will be around < 1.5 seconds for more than 31k matches (a result of basic test on a specific sample).
So, I would suggest you to do it via THREADING and split task in two threads:
Here is an example source code:
(For worst case i found around 31341 matches and process took 4 seconds to highlight)
// declare variables either globally or in the same method
MatchCollection mcoll;
Stopwatch s;
int callbackCount = 0;
List<Match> m1 = null;
List<Match> m2 = null;
private void btnHighlight_Click(object sender, EventArgs e)
{
//reset any exisiting formatting
rtbMain.SelectAll();
rtbMain.SelectionBackColor = Color.White;
rtbMain.SelectionColor = Color.Black;
rtbMain.DeselectAll();
s = new Stopwatch();
s.Start();
Regex re = new Regex(#"(.)", RegexOptions.Compiled); // Notice COMPILED option
mcoll = re.Matches(rtbMain.Text);
// Break MatchCollection object into List<Matches> which is exactly half in size
m1 = new List<Match>(mcoll.Count / 2);
m2 = new List<Match>(mcoll.Count / 2);
for (int k = 0; k < mcoll.Count; k++)
{
if (k < mcoll.Count / 2)
m1.Add(mcoll[k]);
else
m2.Add(mcoll[k]);
}
Thread backgroundThread1 = new Thread(new ThreadStart(() => {
match1(null, null);
}));
backgroundThread1.Start();
Thread backgroundThread2 = new Thread(new ThreadStart(() =>
{
match2(null, null);
}));
backgroundThread2.Start();
}
public void match1(object obj, EventArgs e)
{
for (int i=0; i < m1.Count; i += 1)
{
if (rtbMain.InvokeRequired)
{
EventHandler d = new EventHandler(match1);
rtbMain.Invoke(d);
}
else
{
rtbMain.Select(m1[i].Index, m1[i].Length);
rtbMain.SelectionBackColor = Color.Black;
rtbMain.SelectionColor = Color.Red;
}
}
stopTimer();
}
public void match2(object obj, EventArgs e)
{
for (int j=0; j < m2.Count; j += 1)
{
if (rtbMain.InvokeRequired)
{
EventHandler d = new EventHandler(match2);
rtbMain.Invoke(d);
}
else
{
rtbMain.Select(m2[j].Index, m2[j].Length);
rtbMain.SelectionBackColor = Color.Black;
rtbMain.SelectionColor = Color.Red;
}
}
stopTimer();
}
void stopTimer()
{
callbackCount++;
if (callbackCount == 2) // 2 because I am using two threads.
{
s.Stop();
// Check Output Window
Debug.Print("Evaluated in : " + s.Elapsed.Seconds.ToString());
}
}
Since as you posted it takes around 30 sec to manipulate, hope 4 sec is bearable and user can be engaged by some loading screen as the other online converters do like Rubular and DerekSlager's .Net regex tester does.
Don't forget to have a look at Why Regex.Compiled preferred.

Links inside rich textbox?

I know that richtextboxes can detect links (like http://www.yahoo.com) but is there a way for me to add links to it that looks like text but its a link? Like where you can choose the label of the link? For example instead of it appearing as http://www.yahoo.com it appears as Click here to go to yahoo
edit: forgot, im using windows forms
edit: is there something thats better to use (as in easier to format)?
Of course it is possible by invoking some WIN32 functionality into your control, but if you are looking for some standard ways, check this post out:
Create hyperlink in TextBox control
There are some discussions about different ways of integration.
greetings
Update 1:
The best thing is to follow this method:
http://msdn.microsoft.com/en-us/library/f591a55w.aspx
because the RichText box controls provides some functionality to "DetectUrls". Then you can handle the clicked links very easy:
this.richTextBox1.LinkClicked += new System.Windows.Forms.LinkClickedEventHandler(this.richTextBox1_LinkClicked);
and you can simple create your own RichTextBox contorl by extending the base class - there you can override the methods you need, for example the DetectUrls.
Here you can find an example of adding a link in rich Textbox by linkLabel:
LinkLabel link = new LinkLabel();
link.Text = "something";
link.LinkClicked += new LinkLabelLinkClickedEventHandler(this.link_LinkClicked);
LinkLabel.Link data = new LinkLabel.Link();
data.LinkData = #"C:\";
link.Links.Add(data);
link.AutoSize = true;
link.Location =
this.richTextBox1.GetPositionFromCharIndex(this.richTextBox1.TextLength);
this.richTextBox1.Controls.Add(link);
this.richTextBox1.AppendText(link.Text + " ");
this.richTextBox1.SelectionStart = this.richTextBox1.TextLength;
And here is the handler:
private void link_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
System.Diagnostics.Process.Start(e.Link.LinkData.ToString());
}
I found a way which may not be the most elegant, but it's just a few lines of code and does the job. Namely, the idea is to simulate hyperlink appearance by means of font changes, and simulate hyperlink behavior by detecting what the mouse pointer is on.
The code:
public partial class Form1 : Form
{
private Cursor defaultRichTextBoxCursor = Cursors.Default;
private const string HOT_TEXT = "click here";
private bool mouseOnHotText = false;
// ... Lines skipped (constructor, etc.)
private void Form1_Load(object sender, EventArgs e)
{
// save the right cursor for later
this.defaultRichTextBoxCursor = richTextBox1.Cursor;
// Output some sample text, some of which contains
// the trigger string (HOT_TEXT)
richTextBox1.SelectionFont = new Font("Calibri", 11, FontStyle.Underline);
richTextBox1.SelectionColor = Color.Blue;
// output "click here" with blue underlined font
richTextBox1.SelectedText = HOT_TEXT + "\n";
richTextBox1.SelectionFont = new Font("Calibri", 11, FontStyle.Regular);
richTextBox1.SelectionColor = Color.Black;
richTextBox1.SelectedText = "Some regular text";
}
private void richTextBox1_MouseMove(object sender, MouseEventArgs e)
{
int mousePointerCharIndex = richTextBox1.GetCharIndexFromPosition(e.Location);
int mousePointerLine = richTextBox1.GetLineFromCharIndex(mousePointerCharIndex);
int firstCharIndexInMousePointerLine = richTextBox1.GetFirstCharIndexFromLine(mousePointerLine);
int firstCharIndexInNextLine = richTextBox1.GetFirstCharIndexFromLine(mousePointerLine + 1);
if (firstCharIndexInNextLine < 0)
{
firstCharIndexInNextLine = richTextBox1.Text.Length;
}
// See where the hyperlink starts, as long as it's on the same line
// over which the mouse is
int hotTextStartIndex = richTextBox1.Find(
HOT_TEXT, firstCharIndexInMousePointerLine, firstCharIndexInNextLine, RichTextBoxFinds.NoHighlight);
if (hotTextStartIndex >= 0 &&
mousePointerCharIndex >= hotTextStartIndex && mousePointerCharIndex < hotTextStartIndex + HOT_TEXT.Length)
{
// Simulate hyperlink behavior
richTextBox1.Cursor = Cursors.Hand;
mouseOnHotText = true;
}
else
{
richTextBox1.Cursor = defaultRichTextBoxCursor;
mouseOnHotText = false;
}
toolStripStatusLabel1.Text = mousePointerCharIndex.ToString();
}
private void richTextBox1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && mouseOnHotText)
{
// Insert your own URL here, to navigate to when "hot text" is clicked
Process.Start("http://www.google.com");
}
}
}
To improve on the code, one could create an elegant way to map multiple "hot text" strings to their own linked URLs (a Dictionary<K, V> maybe). An additional improvement would be to subclass RichTextBox to encapsulate the functionality that's in the code above.
Many moons later there is a solution for .NET (Core), as of at least .NET 6.0 (possibly earlier) - for .NET Framework (whose latest and last version is 4.8) you'll still need one of the other solutions here:
The .Rtf property now recognizes RTF-format hyperlinks; e.g., the following renders as:
This is a true RTF hyperlink: Example Link
this.richTextBox1.Rtf = #"{\rtf1 This is a true RTF hyperlink:\line {\field{\*\fldinst HYPERLINK ""https://example.org""}{\fldrslt Example Link}}} }";
The standard RichTextBox control (assuming you are using Windows Forms) exposes a rather limited set of features, so unfortunately you will need to do some Win32 interop to achieve that (along the lines of SendMessage(), CFM_LINK, EM_SETCHARFORMAT etc.).
You can find more information on how to do that in this answer here on SO.

Can I display links in a ListView's detail mode?

I'm displaying a set of search results in a ListView. The first column holds the search term, and the second shows the number of matches.
There are tens of thousands of rows, so the ListView is in virtual mode.
I'd like to change this so that the second column shows the matches as hyperlinks, in the same way as a LinkLabel shows links; when the user clicks on the link, I'd like to receive an event that will let me open up the match elsewhere in our application.
Is this possible, and if so, how?
EDIT: I don't think I've been sufficiently clear - I want multiple hyperlinks in a single column, just as it is possible to have multiple hyperlinks in a single LinkLabel.
You can easily fake it. Ensure that the list view items you add have UseItemStyleForSubItems = false so that you can set the sub-item's ForeColor to blue. Implement the MouseMove event so you can underline the "link" and change the cursor. For example:
ListViewItem.ListViewSubItem mSelected;
private void listView1_MouseMove(object sender, MouseEventArgs e) {
var info = listView1.HitTest(e.Location);
if (info.SubItem == mSelected) return;
if (mSelected != null) mSelected.Font = listView1.Font;
mSelected = null;
listView1.Cursor = Cursors.Default;
if (info.SubItem != null && info.Item.SubItems[1] == info.SubItem) {
info.SubItem.Font = new Font(info.SubItem.Font, FontStyle.Underline);
listView1.Cursor = Cursors.Hand;
mSelected = info.SubItem;
}
}
Note that this snippet checks if the 2nd column is hovered, tweak as needed.
Use ObjectListView -- an open source wrapper around a standard ListView. It supports links directly:
This recipe documents the (very simple) process and how you can customise it.
The other answers here are great, but if you don't want to have to hack some code together, look at the DataGridView control which has support for LinkLabel equivalent columns.
Using this control, you get all the functionality of the details view in a ListView, but with more customisation per row.
You can by inheriting the ListView control override the method OnDrawSubItem.
Here is a VERY simple example of how you might do:
public class MyListView : ListView
{
private Brush m_brush;
private Pen m_pen;
public MyListView()
{
this.OwnerDraw = true;
m_brush = new SolidBrush(Color.Blue);
m_pen = new Pen(m_brush)
}
protected override void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
{
e.DrawDefault = true;
}
protected override void OnDrawSubItem(DrawListViewSubItemEventArgs e)
{
if (e.ColumnIndex != 1) {
e.DrawDefault = true;
return;
}
// Draw the item's background.
e.DrawBackground();
var textSize = e.Graphics.MeasureString(e.SubItem.Text, e.SubItem.Font);
var textY = e.Bounds.Y + ((e.Bounds.Height - textSize.Height) / 2);
int textX = e.SubItem.Bounds.Location.X;
var lineY = textY + textSize.Height;
// Do the drawing of the underlined text.
e.Graphics.DrawString(e.SubItem.Text, e.SubItem.Font, m_brush, textX, textY);
e.Graphics.DrawLine(m_pen, textX, lineY, textX + textSize.Width, lineY);
}
}
You can set HotTracking to true so that when the user hovers mouse over the item it appears as link.

Categories