TreeControl/SRxBorderedUserControl.ps1

class SRxBorderedUserControl : System.Windows.Forms.UserControl
{
    hidden [SRxBorderSides]       $_borderSides
    hidden [System.Drawing.Color] $_borderColor
    hidden [float]                $_borderWidth

    SRxBorderedUserControl()
    {
        $this._borderSides = [SRxBorderSides]::None
        $this._borderColor = [System.Drawing.SystemColors]::ControlDark
        $this._borderWidth = [float]1.0

        $styles = (
            [System.Windows.Forms.ControlStyles]::UserPaint `
            -bor [System.Windows.Forms.ControlStyles]::AllPaintingInWmPaint `
            -bor [System.Windows.Forms.ControlStyles]::OptimizedDoubleBuffer `
            -bor [System.Windows.Forms.ControlStyles]::ResizeRedraw
        )

        $this.SetStyle($styles, $true)
        $this.UpdateStyles()

        $this.add_Paint({
            param(
                [object]$sender,
                [System.Windows.Forms.PaintEventArgs]$e
            )

            ([SRxBorderedUserControl]$sender).PaintSideBorders($e)
        })
    }

    [SRxBorderSides] GetBorderSides()
    {
        return $this._borderSides
    }

    [void] SetBorderSides([SRxBorderSides]$value)
    {
        if ($this._borderSides -eq $value)
        {
            return
        }

        $this._borderSides = $value
        $this.Invalidate()
    }

    [System.Drawing.Color] GetBorderColor()
    {
        return $this._borderColor
    }

    [void] SetBorderColor([System.Drawing.Color]$value)
    {
        if ($this._borderColor.ToArgb() -eq $value.ToArgb())
        {
            return
        }

        $this._borderColor = $value
        $this.Invalidate()
    }

    [float] GetBorderWidth()
    {
        return $this._borderWidth
    }

    [void] SetBorderWidth([float]$value)
    {
        $this.ValidateBorderWidth($value, 'value')

        if ($this._borderWidth -eq $value)
        {
            return
        }

        $this._borderWidth = $value
        $this.Invalidate()
    }

    [void] SetSideBorder(
        [SRxBorderSides]$sides,
        [System.Drawing.Color]$color
    )
    {
        $this.SetSideBorder($sides, $color, $this._borderWidth)
    }

    [void] SetSideBorder(
        [SRxBorderSides]$sides,
        [System.Drawing.Color]$color,
        [float]$width
    )
    {
        $this.ValidateBorderWidth($width, 'width')

        $changed = (
            ($this._borderSides -ne $sides) -or
            ($this._borderColor.ToArgb() -ne $color.ToArgb()) -or
            ($this._borderWidth -ne $width)
        )

        if (-not $changed)
        {
            return
        }

        $this._borderSides = $sides
        $this._borderColor = $color
        $this._borderWidth = $width
        $this.Invalidate()
    }

    [void] ClearSideBorder()
    {
        $this.SetBorderSides([SRxBorderSides]::None)
    }

    hidden [void] ValidateBorderWidth(
        [float]$value,
        [string]$parameterName
    )
    {
        if (
            [System.Single]::IsNaN($value) -or
            [System.Single]::IsInfinity($value) -or
            ($value -le [float]0.0)
        )
        {
            throw [System.ArgumentOutOfRangeException]::new(
                $parameterName,
                $value,
                'Border width must be a finite value greater than zero.'
            )
        }
    }

    [void] PaintSideBorders([System.Windows.Forms.PaintEventArgs]$e)
    {
        if ($null -eq $e)
        {
            return
        }

        if ($this._borderSides -eq [SRxBorderSides]::None)
        {
            return
        }

        $clientWidth = $this.ClientSize.Width
        $clientHeight = $this.ClientSize.Height

        if (($clientWidth -le 0) -or ($clientHeight -le 0))
        {
            return
        }

        $pen = [System.Drawing.Pen]::new(
            $this._borderColor,
            $this._borderWidth
        )

        try
        {
            $halfWidth = [float]($this._borderWidth / [float]2.0)

            $leftX = $halfWidth
            $topY = $halfWidth
            $rightX = [float]([System.Math]::Max(
                [double]$halfWidth,
                ([double]$clientWidth - [double]$halfWidth)
            ))
            $bottomY = [float]([System.Math]::Max(
                [double]$halfWidth,
                ([double]$clientHeight - [double]$halfWidth)
            ))

            $minimumX = [float]0.0
            $minimumY = [float]0.0
            $maximumX = [float]$clientWidth
            $maximumY = [float]$clientHeight

            if (($this._borderSides -band [SRxBorderSides]::Left) -ne [SRxBorderSides]::None)
            {
                $e.Graphics.DrawLine($pen, $leftX, $minimumY, $leftX, $maximumY)
            }

            if (($this._borderSides -band [SRxBorderSides]::Top) -ne [SRxBorderSides]::None)
            {
                $e.Graphics.DrawLine($pen, $minimumX, $topY, $maximumX, $topY)
            }

            if (($this._borderSides -band [SRxBorderSides]::Right) -ne [SRxBorderSides]::None)
            {
                $e.Graphics.DrawLine($pen, $rightX, $minimumY, $rightX, $maximumY)
            }

            if (($this._borderSides -band [SRxBorderSides]::Bottom) -ne [SRxBorderSides]::None)
            {
                $e.Graphics.DrawLine($pen, $minimumX, $bottomY, $maximumX, $bottomY)
            }
        }
        finally
        {
            if ($null -ne $pen)
            {
                $pen.Dispose()
            }
        }
    }
}