Search code examples
c#wpfxamlmvvmmvvm-light

In WPF and MVVM, how to add a "Copy" context menu to a <Hyperlink>?


In most web modern browsers, one can right click on a hyperlink and use the "Copy link address ..." context menu.

In WPF, I'm wondering if there is a method to add the same functionality to the <Hyperlink> XAML tag?

I'm using MVVM Light.


Solution

  • This is actually more difficult than it should be.

    The reason is that ContextMenu is not part of the visual tree, which means that attempting to use any of the most logical bindings returns null instead of the expected value.

    The solution is to wrap the entire Hyperlink in a UserControl, then use {Binding PlacementTarget.Content} to access the properties we want. In this case, the required property is the URL, which we need for the parameter when we want to copy the hyperlink to the clipboard via the context menu. Of course, we could specify the URL twice, but that violates the DRY (Dont Repeat Yourself) principle.

    I'm using MVVM Light.

    XAML

    The intent of the second Command Parameter is to bind to the contents of NavigateUri in the parent Hyperlink tag, and pass that in as a parameter for the context menu, so it can be copied onto the clipboard.

    <UserControl>
        <Hyperlink NavigateUri="http://www.google.com/" 
                   Command="{Binding OnClickHyperlink}"
                   CommandParameter="{Binding NavigateUri, RelativeSource={RelativeSource Self}}">
            www.google.com
            <Hyperlink.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Copy link address" 
                              Command="{Binding OnCopyHyperlink}"                                                  
                              CommandParameter="{Binding PlacementTarget.Content.NavigateUri, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}">
                    </MenuItem>
                </ContextMenu>
            </Hyperlink.ContextMenu>
        </Hyperlink>
    </UserControl>
    

    C# for Hyperlink Click

    private ICommand _onClickHyperlink;
    public ICommand OnClickHyperlink
    {
        get
        {
            return _onClickHyperlink ?? (_onClickHyperlink = new RelayCommand<Uri>(
                hyperlink =>
                {
                    // Handle Hyperlink click here using Process.Start().
                }));
        }
    }
    

    C# for Hyperlink Copy

    private ICommand _onCopyHyperlink;
    public ICommand OnCopyHyperlink
    {
        get
        {
            return _onCopyHyperlink ?? (_onCopyHyperlink = new RelayCommand<Uri>(
                hyperlink =>
                {
                    Clipboard.SetText(hyperlink.OriginalString);
                }));
        }
    }