Reputation: 951
I have a WPF RichTextBox with isReadOnly
set to True
. I would like users to be able to click on HyperLinks contained within the RichTextBox, without them having to hold down Ctrl.
The Click event on the HyperLink doesn't seem to fire unless Ctrl is held-down, so I'm unsure of how to proceed.
Upvotes: 20
Views: 17324
Reputation: 28968
Do not handle any mouse events explicitly and do not force the cursor explicitly - like suggested in every answer.
It's also not required to make the complete RichTextBox
read-only (as suggested in another answer).
To make the Hyperlink
clickable without pressing the Ctrl key, the Hyperlink
must be made read-only e.g., by wrapping it into a TextBlock
(or alternatively by making the complete RichTextBox
read-only, of course).
Then simply handle the Hyperlink.RequestNavigate
event or/and attach an ICommand
to the Hyperlink.Command
property:
<RichTextBox IsDocumentEnabled="True">
<FlowDocument>
<Paragraph>
<Run Text="Some editable text" />
<TextBlock>
<Hyperlink NavigateUri="https://duckduckgo.com"
RequestNavigate="OnHyperlinkRequestNavigate">
DuckDuckGo
</Hyperlink>
</TextBlock>
</Paragraph>
</FlowDocument>
</RichTextBox>
Upvotes: 1
Reputation: 3312
My answer is based on @BionicCode's answer, which I wanted to extend with the event handler code, which I had some difficulties to get it working.
<RichTextBox IsDocumentEnabled="True" IsReadOnly="True">
<FlowDocument>
<Paragraph>
<Run Text="Some editable text" />
<Hyperlink x:Name="DuckduckgoHyperlink"
NavigateUri="https://duckduckgo.com">
DuckDuckGo
</Hyperlink>
</Paragraph>
</FlowDocument>
</RichTextBox>
I changed his code slightly:
RichTextBox
to be readonly. When the RichTextBox
is readonly, it is not necessary to put the HyperLink
into a TextBlock
. However, using TextBlock
in a RichTextBlock
where the user can make changes is a great suggestion.Hyperlink
a name.Code behind
I needed to display some rich text with links in a HelpWindow
:
public HelpWindow() {
InitializeComponent();
DuckduckgoHyperlink.RequestNavigate += Hyperlink_RequestNavigate;
}
private void Hyperlink_RequestNavigate(object sender,
RequestNavigateEventArgs e)
{
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) {
UseShellExecute = true,
});
e.Handled = true;
}
Note that the same event handler can be used by any HyperLink
. Another solution would be not to define the URL in XAML but hard code it in the event handler, in which case each HyperLink
needs its own event handler.
In various Stackoverflow answers I have seen the code:
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
Which resulted in the error message:
System.ComponentModel.Win32Exception: 'An error occurred trying to start process 'https://duckduckgo.com/' with working directory '...\bin\Debug\net6.0-windows'. The system cannot find the file specified.'
Upvotes: 1
Reputation: 21899
If you want to turn Arrow into a Hand cursor always without default system navigation, below is the approach.
<RichTextBox>
<RichTextBox.Resources>
<Style TargetType="{x:Type Hyperlink}">
<EventSetter Event="MouseEnter" Handler="Hyperlink_OnMouseEnter"/>
</Style>
</RichTextBox.Resources>
</RichTextBox>
private void Hyperlink_OnMouseEnter(object sender, MouseEventArgs e)
{
var hyperlink = (Hyperlink)sender;
hyperlink.ForceCursor = true;
hyperlink.Cursor = Cursors.Hand;
}
Upvotes: -1
Reputation: 19
I changed EventSetter from @hillin's answer. MouseLeftButtonDown didn't work in my code (.Net framework 4.5.2).
<EventSetter Event="RequestNavigate" Handler="Hyperlink_RequestNavigate" />
private void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
{
Process.Start(e.Uri.ToString());
}
Upvotes: 1
Reputation: 1779
JHubbard80's answer is a possible solution, it's the easiest way if you do not need the content to be selected.
However I need that :P here is my approach: set a style for the Hyperlink
s inside the RichTextBox
. The essential is to use a EventSetter
to make the Hyperlink
s handling the MouseLeftButtonDown
event.
<RichTextBox>
<RichTextBox.Resources>
<Style TargetType="Hyperlink">
<Setter Property="Cursor" Value="Hand" />
<EventSetter Event="MouseLeftButtonDown" Handler="Hyperlink_MouseLeftButtonDown" />
</Style>
</RichTextBox.Resources>
</RichTextBox>
And in codebehind:
private void Hyperlink_MouseLeftButtonDown(object sender, MouseEventArgs e)
{
var hyperlink = (Hyperlink)sender;
Process.Start(hyperlink.NavigateUri.ToString());
}
Thanks to gcores for the inspiaration.
Upvotes: 16
Reputation: 2277
I found a solution. Set IsDocumentEnabled to "True" and set IsReadOnly to "True".
<RichTextBox IsReadOnly="True" IsDocumentEnabled="True" />
Once I did this, the mouse would turn into a 'hand' when I hover over a text displayed within a HyperLink tag. Clicking without holding control will fire the 'Click' event.
I am using WPF from .NET 4. I do not know if earlier versions of .NET do not function as I describe above.
Upvotes: 33
Reputation: 951
Managed to find a way around this, pretty much by accident.
The content that's loaded into my RichTextBox is just stored (or inputted) as a plain string. I have subclassed the RichTextBox to allow binding against it's Document property.
What's relevant to the question, is that I have an IValueConverter Convert() overload that looks something like this (code non-essential to the solution has been stripped out):
FlowDocument doc = new FlowDocument();
Paragraph graph = new Paragraph();
Hyperlink textLink = new Hyperlink(new Run(textSplit));
textLink.NavigateUri = new Uri(textSplit);
textLink.RequestNavigate +=
new System.Windows.Navigation.RequestNavigateEventHandler(navHandler);
graph.Inlines.Add(textLink);
graph.Inlines.Add(new Run(nonLinkStrings));
doc.Blocks.Add(graph);
return doc;
This gets me the behavior I want (shoving plain strings into RichTextBox and getting formatting) and it also results in links that behave like a normal link, rather than one that's embedded in a Word document.
Upvotes: 5