1

I want to create a PowerShell based GUI with windows forms to display items in different containers and enable the user to drag items (control objects) from one container to another. Because of some bad flickering I copied the answer from this question and converted the code into PowerShell: How to double buffer .NET controls on a form?

The problem is, that the objects are "smearing" on the form when I start dragging them and I can't find how to solve this.

class Dictionary : System.Collections.DictionaryBase
{
    Dictionary() {}
    [Bool]Exists([System.Object] $Key) {return ($Key -in $this.Keys) }
}

function SetDoubleBuffered()
{
    param([System.Windows.Forms.Control] $TargetControl)

    [System.Reflection.PropertyInfo] $DoubleBufferedProp = [System.Windows.Forms.Control].GetProperty("DoubleBuffered", [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Instance)
    $DoubleBufferedProp.SetValue($TargetControl, $True, $Null)
}

function Button_MouseDown()
{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$True)][System.Object] $Sender,
        [parameter(Mandatory=$True)][System.EventArgs] $EventArguments
    )

    $Sender.Tag.DragStart = $False
    $Sender.Tag.StartX = $EventArguments.X
    $Sender.Tag.StartY = $EventArguments.Y
}

function Button_MouseUp()
{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$True)][System.Object] $Sender,
        [parameter(Mandatory=$True)][System.EventArgs] $EventArguments
    )

    $Sender.Tag.DragStart = $False
    $Sender.Tag.StartX = 0
    $Sender.Tag.StartY = 0
}

function Button_MouseMove()
{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$True)][System.Object] $Sender,
        [parameter(Mandatory=$True)][System.EventArgs] $EventArguments
    )
    if ($EventArguments.Button.value__ -gt 0)
    {
        if (-not $Sender.Tag.DragStart)
        {
            if ([System.Math]::sqrt([System.Math]::Pow($Sender.Tag.StartX - $EventArguments.X, 2) + [System.Math]::Pow($Sender.Tag.StartY - $EventArguments.Y, 2)) -ge 10)
            {
                $Sender.Tag.DragStart = $true
            }
        }
        else
        {
            $Sender.Left = $Sender.Left + ($EventArguments.X - $Sender.Tag.StartX)
            $Sender.Top = $Sender.Top + ($EventArguments.Y - $Sender.Tag.StartY)
        }
    }
    else
    {
        $Sender.Tag.DragStart = $False
        $Sender.Tag.StartX = 0
        $Sender.Tag.StartY = 0
    }
}

function OpenForm()
{
    $Form = [System.Windows.Forms.Form]::new()
    $Form.Text = "DragTest"
    $Form.Width = 900
    $Form.Height = 900

    $Button = [System.Windows.Forms.Button]::new()
    $Button.Left = 10
    $Button.Top = 30
    $Button.Height = 20
    $Button.Width = 100
    $Button.Text = "MyButton"
    $Button.Name = "SomeButton"
    $Button.Tag = [Dictionary]::new()
    $Button.Add_MouseDown({Button_MouseDown -Sender $Button -EventArguments $_})
    $Button.Add_MouseUp({Button_MouseUp -Sender $Button -EventArguments $_})
    $Button.Add_MouseMove({Button_MouseMove -Sender $Button -EventArguments $_})
    
    $Form.Controls.Add($Button)

    SetDoubleBuffered -TargetControl $Form
    SetDoubleBuffered -TargetControl $Button

    $Form.ShowDialog() | Out-Null
}

cls
OpenForm
GuidoT
  • 280
  • 1
  • 12
  • Almost sounds like a display driver issue. I ran your PowerShell code flawlessly (after a quick fix for loading `System.Windows.Forms` and `System.Drawing` using partial names), with no evidence of "smearing". Perhaps call `$Form.Invalidate()` inside `Button_MouseMove()` after DragStart has started. Otherwise, check if hardware acceleration is enabled, then disable it during `$Form` creation. – leeharvey1 Apr 11 '21 at 18:26

0 Answers0