1

First time asking a question here. I'm working on designing a dynamic popup script to be used by other people.

One of the needs for this script is to have a popup that allows an everyday tech to create a message that has multiple links.

To do this, I've been trying to create a form with a single linklabel that has multiple links added to Linklabel.Links

The problem I've encountered is that while I can get a hyperlink to work when the user clicks on the label, I can't get powershell to differentiate between which hyper was clicked by the user.

Below is a snippet of my code. I have built custom tags to identify when the user want's to create a hyperlink

Add-Type -AssemblyName System.Windows.Forms

#....

# User's custom popup message. 
# Original script sorts through this information to identify where the hyper links should be.
$testLink = 'test <hlink-start><text-start>Click Here<text-end>http://www.google.com<hlink-end> Words Words <hlink-start>https://www.vmware.com<hlink-end> done'

# Stores the linklabel text after the tags in $testLink have been sorted through
$NewLabelText = ""

# Custom object that stores the url of each hyperlink; the startPosition; and the Length of text for the url
$URLinfo = New-Object -TypeName PSObject

#... Skipping code that sorts through $testLink 
#... Outcome is the following:
#... 
#... $NewLabelText =
#... 'test Click Here Words Words https://www.vmware.com done'
#... 
#... $URLinfo =
#... URL                    StartPos LinkLength
#... ---                    -------- ----------
#... http://www.google.com         5         10
#... https://www.vmware.com       28         22

# creates a test windows form
$form = New-Object System.Windows.Forms.Form

# Form title
$form.Text = "sample"

# Create new LinkLabel
$linklabel = New-Object System.Windows.Forms.LinkLabel

# Set LinkLabel text
$linklabel.Text = $NewLabelText

# Defines text that should be a hyperlink
foreach ($URL in $URLinfo)
{
    $linklabel.Links.Add($URL.StartPos, $URL.LinkLength, $URL.URL)
    
    # Attempted to navigate to defined Web page when Specific link is click.
    # Errors out. Add_Click is not a method of Links.
    #$linklabel.Links.add_Click({[system.Diagnostics.Process]::start($URL.URL)})
    

}

# Customizing linklable size
$linklabel.AutoSize = $true

# Add linklabel to form
$form.Controls.Add($linklabel)

# Customizing form size
$form.AutoSize = $true

# The add_click method here works. 
# Makes the entire Linklabel clickable including the non-highlighted parts
#$linklabel.add_Click({[system.Diagnostics.Process]::start($URLinfo[0].URL)})

# Shows form
$form.ShowDialog()

The below Microsoft documents show a method that allows for users to have multiple hyperlinks in a single linklabel, however this appears to use event handlers and is specifically for C#. Not sure if I can translate the solution to Powershell.

https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.linklabel.link?view=windowsdesktop-6.0

I also found an interesting solution using XML, but I don't know enough about xml to modify the code found here.

https://github.com/kunaludapi/Powershell/blob/master/Powershell%20WPF%20linklabel%20Hyperlink%20Demo.ps1

Pezz570
  • 13
  • 3
  • Your proposed answer. Just as an FYI... as posted the code is not valid. It will fail (vs the other option provided) here: ***The variable '$NewLabelText' cannot be retrieved because it has not been set.***, and here: The variable '$URLinfo' cannot be retrieved because it has not been set. So, refactor/correction is required before the use attempt. – postanote Aug 23 '22 at 05:22
  • I know. If you look at the comments you'll noticed that I skipped some of the code that set those variables while also commenting out what the variables will eventually equal. that chunk of code was not important to the question – Pezz570 Aug 23 '22 at 23:44

2 Answers2

0

When you use a PowerShell script block { ... }) as an event delegate, you can formally declare the arguments that .NET passes to the delegate as parameters, using a param(...) block.

Here, you want to subscribe to the link label's LinkClicked event, which expects a LinkLabelLinkClickedEventHandler delegate, whose event-arguments object contains the originating link in its .Link property, whose .LinkData property contains the target URL:

$linklabel.add_LinkClicked({
  param($evtSender, $evtArgs)
  # Launch the default browser with the target URL.
  Start-Process $evtArgs.Link.LinkData
})    

Note the use of Start-Process for opening the URL as the PowerShell-native alternative to using the underlying .NET API, [System.Diagnostics.Process]::start().

Note:

  • For brevity, the parameter declarations above are untyped, but you may type them, which can provide IntelliSense when developing in Visual Studio Code with the PowerShell extension, for instance. See the docs for details on parameter declarations.

  • See this answer for a detailed discussion of using script blocks as event handlers, including an explanation of why .add_<eventName>() must be used in PowerShell to register for an event.


Here's a self-contained sample (PS v5+) based on a streamlined version of your code:

using namespace System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms

$form = [Form] @{ Text = 'sample' }

$linklabel = [LinkLabel] @{
    Text     = 'Link 1: example.org; link 2: google.com'
    AutoSize = $true
}

# Sample hyperlinks to add to the text of the link label control.
$URLInfo = @'
StartPos,LinkLength,Url
7,11,http://example.org
29,10,https://google.com
'@ | ConvertFrom-Csv

# Add them.
foreach ($URL in $URLinfo) {
    $null = $linklabel.Links.Add($URL.StartPos, $URL.LinkLength, $URL.URL)
}

# Register a handler for when the user clicks a link.
$linklabel.add_LinkClicked({
        param($evtSender, $evtArgs)
        # Launch the default browser with the target URL.
        Start-Process $evtArgs.Link.LinkData
    })   

$form.Controls.Add($linklabel)

$null = $form.ShowDialog()
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    This is the correct answer, I think! Thanks for the feedback. I actually managed to figure it out prior to reviewing your answer. – Pezz570 Aug 22 '22 at 19:52
  • Glad to hear it, @Pezz570. The advantage of the approach here is that using the event-arguments parameter _directly_ tells you what link was clicked on - no need to search through the label's `.Links` collection. – mklement0 Aug 22 '22 at 19:56
0

Thanks for all your answers! I actually was able to figure it out for those wondering. Please note that in the code below, $NewLabelText and $URLinfo has already been set in previous code.

$form = New-Object System.Windows.Forms.Form

$form.Text = "sample"

$linklabel = New-Object System.Windows.Forms.LinkLabel

$linklabel.Text = $NewLabelText

#$linkLabel.LinkClicked += [System.Windows.Forms.LinkLabelLinkClickedEventHandler]$linkLabel_LinkClicked

foreach ($URL in $URLinfo)
{
    $linklabel.Links.Add($URL.StartPos, $URL.LinkLength, $URL.URL)
    
    #$linklabel.Links.add_Click({[system.Diagnostics.Process]::start($URL.URL)})
    

}



$linklabel.AutoSize = $true

$form.Controls.Add($linklabel)

$form.AutoSize = $true

$linkLabel_LinkClicked = 
[System.Windows.Forms.LinkLabelLinkClickedEventHandler]{    
    param(
        [object]$sender, 
        [System.Windows.Forms.LinkLabelLinkClickedEventArgs]$e
    )

    Write-Host $e.link
    Write-Host $sender.links[0].LinkData
    
    $sender.Links[$sender.Links.IndexOf($e.Link)].Visited = $true;

    # Display the appropriate link based on the value of the 
    # LinkData property of the Link object.
    $target = $e.Link.LinkData

    [system.Diagnostics.Process]::start($target)
}

$linkLabel.add_LinkClicked($linkLabel_LinkClicked)


$form.ShowDialog()
Pezz570
  • 13
  • 3