1

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.

fmotion1
  • 237
  • 4
  • 12
  • Why do you have `$e.DrawBackground()` there, since you want to draw your own background on some condition? -- This: `Add-Type -AssemblyName PresentationCore,PresentationFramework` is causing the other thing (not handled auto DpiAwareness) -- Replace `Graphics.DrawString()` with `TextRenderer.DrawText()` – Jimi Feb 18 '23 at 08:36
  • Probably unrelated to your question, but you have a C# to PS conversion issue with the line `$index = $e.Index -ge 0 ? $e.Index : 0`, it should be something like `$index = if($e.Index -ge 0) {$e.Index} else {0}`. If the `if` statement fails, then wrap in parentheses `$index=(if(...){...}else{...})` – Darin Feb 18 '23 at 13:26
  • I don't believe your `$COMBO_SeparationModel_DrawItem = {...}` needs the `param([System.Object]$sender, [DrawItemEventArgs]$e)` part. Create a copy of that assignment, commenting out the original, and then, in the copy, remove the `param(...)` part, replace `$sender` with `$this`, replace `$e` with `$_`, and give it a try. – Darin Feb 18 '23 at 13:37
  • This is an opinion, but I find WinForms code easier to read by removing the extra namespace path info. Add to top `using namespace System.Windows.Forms` and `using namespace System.Drawing` and delete all occurrences of `System.Windows.Forms.` and `System.Drawing.`. – Darin Feb 18 '23 at 13:43
  • Thanks for all the suggestions and help guys. I will be making all the modifications recommended here tomorrow when I'm not so tired. I really appreciate it. – fmotion1 Feb 18 '23 at 13:50
  • I know this sounds crazy, and in this exact scenario it might not work, but one at time, testing each change, try replacing `$Brush`, `$pt`, and more with strings. PS seems to have a crazy string to member of type conversion ability. For example, try replacing `$Brush` in `DrawString` with `"Black"`. Perhaps replace `[SolidBrush]::new([Color]::Azure)` in `FillRectangle` with `"Azure"`. Maybe `$pt` to `"$($e.Bounds.X), $($e.Bounds.Y)"`, changing `$e` to `"$_"` as needed. Experiment and find out what works, and doesn't work. – Darin Feb 18 '23 at 14:07

1 Answers1

1

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()
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398