How to make a simple hyperlink in XAML?
All I want to do is make a little hyperlink in XAML. I've tried everything. I give up.
What is the syntax for this?
<StackPanel Width="70" HorizontalAlignment="Center">
<Hyperlink Click="buttonClose_Click" Cursor="Hand"
Foreground="#555" Width="31" Margin="0 0 0 15"
HorizontalAlignment="Right">Close</Hyperlink>
<Button Width="60" Margin="0 0 0 3">Test 1</Button>
<Button Width="60" Margin="0 0 0 3">Test 2</Button>
<Button Width="60" Margin="0 0 0 3">Test 3</Button>
<Button Width="60" Margin="0 0 0 3">Test 4</Button>
</StackPanel>
Visual Studio Team: In Visual Studio 2010 I want Clippy to pop up and say "It seems you are trying to make a hyperlink" and tell me how to do it. Can't you do that with MEF? It would be retro cool, and these little "how do I do what I already know how to do in HTML" issues burn up so much time during the learning process with XAML.
You can't add a Hyperlink to a StackPanel -- you'll get a runtime error. (Actually, I'm kinda surprised it's not a compile-time error.) That's because Hyperlink doesn't live in the "controls" side of WPF with <Button>
and <StackPanel>
and other things that are laid out on rectangular chunks of screen and descend from UIElement
. Instead, it lives in the "text" side of things, with <Bold>
and <Run>
and <Paragraph>
and other generally texty things that word-wrap and flow in lines and paragraphs and descend from TextElement
.
Once you realize that there are two separate class hierarchies with different layout behaviors, it makes sense that Hyperlink would be on the "text" side of things (makes it easy to e.g. have a paragraph with a hyperlink in the middle, and even for that hyperlink to wrap across a line break).
But no, it's not so discoverable when you're starting out.
To mix the two worlds, and use a hyperlink as a control, all you need to do is put it in a TextBlock. TextBlock is a control-ish thing (i.e., can go in a StackPanel) that contains text-ish things (i.e., can contain a Hyperlink):
<TextBlock><Hyperlink Click="buttonClose_Click">Close</Hyperlink></TextBlock>
You can use a Button with a custom control template, the code below is a limited hyperlink style button (for example it only support textual hyperlinks) but maybe it'll point you in the right direction.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<Style x:Key="Link" TargetType="Button">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Foreground" Value="Blue"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock TextDecorations="Underline"
Text="{TemplateBinding Content}"
Background="{TemplateBinding Background}"/>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Button Content="Click Me!" Style="{StaticResource Link}"/>
</Page>
Try this:
<TextBlock>
<Hyperlink RequestNavigate="Hyperlink_RequestNavigate"
NavigateUri="http://www.msn.com">MSN</Hyperlink>
</TextBlock>
private void Hyperlink_RequestNavigate(object sender,
System.Windows.Navigation.RequestNavigateEventArgs e)
{
System.Diagnostics.Process.Start(e.Uri.AbsoluteUri);
}
<TextBlock>
<Hyperlink NavigateUri="{Binding YourUri}" RequestNavigate="YourRequestNavigate">
<TextBlock Text="{Binding YourText}" />
</Hyperlink>
</TextBlock>
This will linkify any binded text in the nested textblock, i have not found a better way yet, i would like the first textblock to not be there if possible. This will work for DataTemplates aswell.
You may find that if you're binding to anything other than simple text values you will need to use ContentPresenter
otherwise nothing will appear, this could be true if you're binding to an XML data source.
A Property Trigger for IsMouseOver gives the text an underline.
An example where I"m binding to XML is presented below.
<Style x:Key="JobNumberStyleButton" TargetType="{x:Type Button}">
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock>
<ContentPresenter
Margin="0,0,0,0"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
ContentStringFormat="{TemplateBinding ContentStringFormat}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="False"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock Padding="0,0,0,0" Margin="0,0,0,0">
<Underline>
<ContentPresenter
Margin="0,0,0,0"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
ContentStringFormat="{TemplateBinding ContentStringFormat}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="False"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Underline>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>