If you have ever tried used the HyperlinkButton inside your WP7 application and navigate to an external URL you are familiar with the following error.
"Navigation is only supported to relative URIs that are fragments, or begin with '/', or which contain ';component/'.\r\nParameter name: uri"
Basically when you set the NavigateUri property to anything that is not a local view you will get bad results. I wanted to build out a version of the HyperlinkButton which not only allows you to navigate to an external url via the WebBrowserTask but I also wanted something that was MVVM friendly. To accomplish these goals I needed to do the following:
Create a custom HyperlinkButton control called HyperlinkCommandButton
public class HyperlinkCommandButton : HyperlinkButton
{
public HyperlinkCommandButton()
{
Click += ( sender, e ) =>
{
if ( Command != null && Command.CanExecute( CommandParameter ) )
Command.Execute( CommandParameter );
};
}
public static DependencyProperty CommandProperty =
DependencyProperty.Register( "Command",
typeof( ICommand ), typeof( HyperlinkCommandButton ),
new PropertyMetadata( null, CommandChanged ) );
private static void CommandChanged( DependencyObject source, DependencyPropertyChangedEventArgs args )
{
var button = source as HyperlinkCommandButton;
if ( button == null ) return;
button.RegisterCommand( args.OldValue as ICommand, args.NewValue as ICommand );
}
private void RegisterCommand( ICommand oldCommand, ICommand newCommand )
{
if ( oldCommand != null )
oldCommand.CanExecuteChanged -= HandleCanExecuteChanged;
if ( newCommand != null )
newCommand.CanExecuteChanged += HandleCanExecuteChanged;
HandleCanExecuteChanged( newCommand, EventArgs.Empty );
}
private void HandleCanExecuteChanged( object sender, EventArgs args )
{
if ( Command != null )
IsEnabled = Command.CanExecute( CommandParameter );
}
public ICommand Command
{
get { return GetValue( CommandProperty ) as ICommand; }
set { SetValue( CommandProperty, value ); }
}
public static DependencyProperty CommandParameterProperty =
DependencyProperty.Register( "CommandParameter",
typeof( object ), typeof( HyperlinkCommandButton ),
new PropertyMetadata( null ) );
public object CommandParameter
{
get { return GetValue( CommandParameterProperty ); }
set { SetValue( CommandParameterProperty, value ); }
}
}
Create a ViewModel (which is based of of the MvvM Light framework)
public class MainPageViewModel : ViewModelBase
{
public MainPageViewModel()
{
Url = "http://www.yahoo.com";
InvokeUrlCommand = new RelayCommand( HandleUrl );
}
private void HandleUrl( string url )
{
var webBrowserTask = new WebBrowserTask { URL = url };
webBrowserTask.Show();
}
public ICommand InvokeUrlCommand { get; set; }
private string _url;
public string Url
{
get { return _url; }
set
{
_url = value;
RaisePropertyChanged( "Url" );
}
}
}
Hookup the ViewModel to the HyperlinkCommandButton
<WP7_HyperlinkTextPoc:HyperlinkCommandButton Content="HyperlinkButton" Height="30" HorizontalAlignment="Left" Margin="106,191,0,0" Command="{Binding InvokeUrlCommand}" CommandParameter="{Binding Url}" x:Name="hyperlinkButton2" VerticalAlignment="Top" Width="200" />
After you have created your custom HyperlinkButton which supports commanding and you have setup the ICommand correctly in your ViewModel you can see that enabling it in Xaml is cake. You should also notice that I am NOT using the NavigateUrl property on the control, this is because I want to handle everything via the command which was wired up.
Now in my example I am NOT checking the URL to see if it is truly a public web address but this would be very easy to do if you want it.
Till next time,
Posted
01-01-2011 1:36 PM
by
Derik Whittaker