Forum Discussion

vzmon1's avatar
vzmon1
Copper Contributor
Sep 08, 2022

Instantiating a class inherited from Forms

I created a user input GUI PS script based on the example in the MS doc https://docs.microsoft.com/en-us/powershell/scripting/samples/creating-a-custom-input-box?view=powershell-7.2, which worked fine. I then created a class that inherits from Forms, based on the same setup / config in a module. My issue is that when I instantiate my custom user input class what is displayed appears to be the base Forms class without any of the components I configured.  

 

If I include the $forms.showDialog inside the class constructor, my class displays correctly. My goals is to end up with a set of modularized PS scripts where a primary PS script instantiates the user input class, and runs the required logic from the primary, calling other classes / scripts as needed. 

 

I greatly appreciate any guidance that points me in the right direction.

 

User input class, created in moduel (psm1)

using namespace System.Windows.Forms
using namespace System.Drawing
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

 

Class InputWindow:Form{
#InputWindow() {


$form = [Form]::new()
$form.Size = [Size]::new(400, 415)
$form.MaximizeBox = $false
$form.StartPosition = "CenterScreen"
$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedSingle # prevents screen sizing
$form.Topmost = $true
$form.text = "Profile Removal"

#Label
$toolStrpLabel = [System.Windows.Forms.ToolStripStatusLabel]::new()
$toolStrpLabel.Size = [Size]::new(10,12)
$toolStrpLabel.text ="Current Status"

#StatusStrip
$statusStrip = [System.Windows.Forms.StatusStrip]::new()
$statusStrip.Size = [System.Drawing.size]::new(10, 12)
$statusStrip.Dock = [System.Windows.Forms.DockStyle]::Bottom
$statusStrip.SizingGrip = $false
$statusStrip.Text = "Test"
$statusStrip.Items.AddRange($toolStrpLabel)
$form.Controls.Add($statusStrip)


#Buttons
$cancelButton = [System.Windows.Forms.Button]::new()
$cancelButton.Margin = 5
$cancelButton.Anchor = 'right,bottom'
$cancelButton.Location = [System.Drawing.Point]::new(190, 300 )
$cancelButton.Size = [System.Drawing.Size]::new(75,23)
$cancelButton.Text = '&Cancel'
$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.CancelButton = $cancelButton
$form.AcceptButton = $cancelButton #Sets the default active button
$form.Controls.Add($cancelButton)

$okButton = [System.Windows.Forms.Button]::new()
$okButton.Margin = 5
$okButton.Anchor = 'right, bottom'
$okButton.Location = [System.Drawing.Point]::new(275,300)
$okButton.Size = [System.Drawing.Size]::new(75,23)
$okButton.Text = '&OK'
$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.Controls.Add($okButton)

#Message Banner
$msgBanner = [System.Windows.Forms.Label]::new()
$msgBanner.Location = [System.Drawing.Point]::new(10,20)
$msgBanner.Size = [System.Drawing.Size]::new(300,20)
$msgBanner.Text = 'Enter your MID information below:'
$form.Controls.Add($msgBanner)

#MID Label
$midLabel = [System.Windows.Forms.Label]::new()
$midLabel.Location = [System.Drawing.Point]::new(50,50)
$midLabel.Size = [System.Drawing.Size]::new(35,15)
$midLabel.Text = 'MID:'
$form.Controls.Add($midLabel)

#MID input box
$midBox = [System.Windows.Forms.TextBox]::new()
$midBox.Location = [System.Drawing.Point]::new(85,46)
$midBox.Size = [System.Drawing.Size]::new(70,20)
$midBox.Enabled = $true
$midBox.TextAlign = "Left"
$form.Controls.Add($midBox)

#Password label
$pswdLabel = [System.Windows.Forms.Label]::new()
$pswdLabel.Location = [System.Drawing.Point]::new(190,50)
$pswdLabel.Size = [System.Drawing.Size]::new(72,15)
$pswdLabel.Text = 'Password:'
$form.Controls.Add($pswdLabel)

#Password input box
$pswdBox = [System.Windows.Forms.TextBox]::new()
$pswdBox.Location = [System.Drawing.Point]::new(260,46)
$pswdBox.Size = [System.Drawing.Size]::new(70,20)
$pswdBox.Enabled = $true
$pswdBox.TextAlign = "Left"
$form.Controls.Add($pswdBox)

$form.Add_Shown({$midBox.Select()})
   } # END of Constructor


# } #END of Class InputWindow

Function Get-InputWindow () {
[InputWindow]::new()

}

 

Export-ModuleMember -Function Get-InputWindow

 

  • LainRobertson's avatar
    LainRobertson
    Silver Contributor

    vzmon1 

     

    I'm a little wary of replying here as you're touching on a topic that can blow out to be a very big discussion, and I'm also not keen to get involved in writing an application in PowerShell.

     

    That said, here's some very basic observations from what you've dropped in your original post:

     

    • You're mixing up inheritance and instantiation a bit too much;
    • You shouldn't use both "using namespace" and the Reflection.Load()-style approaches together - choose one or the other (or the Add-Type approach used by the Microsoft Docs article you've referenced as a third alternative.)

     

    In your example, you've defined a class named InputWindow which inherits from Form, but then within the constructor (which is broken in the example) you've created a local variable (named $form) and not done anything with the base class at all, making the inheritance quite unnecessary.

     

    As for the mixing up of inheritance and instantiation, this is most evident in the comment about needing to include the ShowDialog() (and equivalents) in the constructor. While this can be done, it's not good practice to do so.

     

    With your class constructor, all you're looking to do is define how things should look and behave when a new instance of the class is instantiated. So, what you're quite correct in including within the class constructor is the layout of all the elements and whatnot.

     

    Stepping up one level to the class itself, you might then also have methods defined in your custom class that perhaps perform some kind of saving of data, or posting it via a REST web call to a service, or even just spit it out as a string (i.e. ToString() is commonly overridden in classes.)

     

    The class itself is then consumed and used (through ::new() or New-Object, etc.) through the calling script/program/whatever, and it's from there that methods such as ShowDialog() should be called.

     

    But as I mentioned above, I think most of this is not really going to help you as it doesn't sound like you need an expansive class. I'd say forget about inheritance and keep going as you have done already and define each new "window" as its own class while keeping the use of each (such as calling ShowDialog() or fetching data, etc.) back out in the calling script (i.e. your module is the "library" while your script is what makes use of the library.)

     

    For posterity, here's what the official documentation says about classes, where I've bookmarked it some distance down where the article discusses inheritance with a good number of examples of the usage of the $this keyword.

     

     

    Cheers,

    Lain