Search code examples
c#powershellwinformscombobox

PowerShell and Winforms, Trying to change the selection color of a ComboBox set to DropDownList and OwnerDrawFixed


I've looked at this: Change the Selection Color of a WinForms ComboBox

And many other posts on this site, but none have worked out for me. I think it might be the way I'm adapting the C# code to my app.

Here's a screenshot of the form:

enter image description here

I'm trying to change the style of the dropdowns. When you switch a ComboBox to DropDownList, the style of the ComboBox changes to a darker gray color (See all of the dropdowns other than "Separation Model).

I've changed one of the dropdowns to OwnerDrawFixed, and then I'm handling the drawing of the text and background with a DrawItem event handler.

Here's the code:

$COMBO_SeparationModel_DrawItem = {

    param(
        [System.Object]
        $sender, 
        [System.Windows.Forms.DrawItemEventArgs]
        $e
    )

    $Brush = [System.Drawing.SolidBrush]::new([System.Drawing.Color]::Black)  
    $Point = [System.Drawing.Point]::new(2, $e.Index * $e.Bounds.Height + 1) 
    $index = $e.Index -ge 0 ? $e.Index : 0  
    
    $ptx = $e.Bounds.X
    $pty = $e.Bounds.Y
    $pt = [System.Drawing.PointF]::new($ptx,$pty)

    $e.DrawBackground()

    if (($e.State -and [System.Windows.Forms.DrawItemState]::Selected) -eq [System.Windows.Forms.DrawItemState]::Selected){
        $e.Graphics.FillRectangle([System.Drawing.SolidBrush]::new([System.Drawing.Color]::Azure), $e.Bounds)
    }else{
        # $e.Graphics.FillRectangle([System.Drawing.SolidBrush]::new($COMBO_SeperationModel.BackColor), $e.Bounds)  
        $e.Graphics.FillRectangle([System.Drawing.SolidBrush]::new($COMBO_SeperationModel.BackColor), [System.Drawing.Rectangle]::new($Point, $e.Bounds.Size))  
    }      
    
    $e.Graphics.TextRenderingHint = [System.Drawing.Text.TextRenderingHint]::AntiAliasGridFit
    $e.Graphics.DrawString($COMBO_SeperationModel.Items[$index].ToString(), $e.Font, $Brush, $pt, [System.Drawing.StringFormat]::GenericDefault); 
    $e.DrawFocusRectangle()

}

$DemucsForm_Load = {
     # $wshell = New-Object -ComObject Wscript.Shell
     # $wshell.Popup($e.State,0,"Done",0x1)
}

. (Join-Path $PSScriptRoot 'DemucsForm.designer.ps1')

Add-Type -AssemblyName PresentationCore,PresentationFramework
[System.Windows.Forms.Application]::EnableVisualStyles()

$FORM_VSYSDemucsUI.ShowDialog()

But when I run this code, everything is Azure instead of only the selected item:

enter image description here

Another problem / side-effect is that after I entered my custom DrawItem event handler, the Label components on the forum are no longer anti-aliased. See the "10 Total Files Selected..." text under the header.

I feel like I've tried everything and I just can't get it to work.

Can someone take a look and tell me what I'm doing wrong?

I would really appreciate any help so I can move on to actually building out the functionality.


Solution

  • The following example shows:

    • How you can customize drawing of a ComboBox.
    • How you must take care of disposing GDI+ objects like Brush
    • How you can make the form DPI aware

    enter image description here

    using assembly System.Windows.Forms
    using namespace System.Windows.Forms
    using namespace System.Drawing
    using namespace System
    #Enable visual styles
    [Application]::EnableVisualStyles()
    
    #Enable DPI awareness
    $code = @"
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern bool SetProcessDPIAware();
    "@
    $Win32Helpers = Add-Type -MemberDefinition $code -Name "Win32Helpers" -PassThru
    $null = $Win32Helpers::SetProcessDPIAware()
    
    $form = [Form] @{
        ClientSize = [Point]::new(500, 200);
        StartPosition = "CenterScreen";
        Text = "Test";
        AutoScaleDimensions = [SizeF]::new(6, 13);
        AutoScaleMode = [AutoScaleMode]::Font;
    }
    $comboBox1 = [ComboBox] @{
        Location = [Point]::new(8,8);
        Width = 300;
        DataSource = ("Lorem", "Ipsum", "Dolor", "Sit", "Amet");
        DropDownStyle = [ComboBoxStyle]::DropDownList;
        DrawMode = [DrawMode]::OwnerDrawFixed;
    }
    $comboBox1.Add_DrawItem({param([Object]$sender, [DrawItemEventArgs]$e)
        $txt = ""
        if($e.Index -ge 0)
        {
            $txt = $comboBox1.GetItemText($comboBox1.Items[$e.Index])
        }
        $bgColor = [Color]::White
        if(($e.State -band [DrawItemState]::Selected) -eq [DrawItemState]::Selected) 
        {
            $bgColor = [Color]::Tomato
        }
        $fColor = [Color]::Black
        if(($e.State -band [DrawItemState]::Selected) -eq [DrawItemState]::Selected)
        {
            $fColor = [Color]::White
        }
        $bgBrush = [SolidBrush]::new($bgColor)
        try
        { 
            $e.Graphics.FillRectangle($bgBrush, $e.Bounds)
            [TextRenderer]::DrawText($e.Graphics,$txt, $e.Font,
                $e.Bounds, $fColor, $bgColor, 
                ([TextFormatFlags]::Left -bor [TextFormatFlags]::VerticalCenter))
        }
        finally
        {
            $bgBrush.Dispose()
        }
    });
    $form.Controls.Add($comboBox1)
    $null = $form.ShowDialog()