I need a listview that can support a Column header with two lines of text. I have spent a lot of time trying to use add_DrawColumnHeader to create that 2nd line. I have no issues creating it in c# but powershell seems to be elusive.
when loading the e.Graphics.Drawstring with the Rectangle I get this error:
Cannot convert argument "point", with value: "{X=60,Y=8,Width=60,Height=8}", for "DrawString" to type "System.Drawing.PointF"
When I check to see if the Overload is available, I see this, so it should work:
OverloadDefinitions
-------------------
void DrawString(string s, System.Drawing.Font font, System.Drawing.Brush brush, float x, float y)
void DrawString(string s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.RectangleF layoutRectangle)
void DrawString(string s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.RectangleF layoutRectangle, System.Drawing.StringFormat format)
void DrawString(string s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.PointF point)
void DrawString(string s, System.Drawing.Font font, System.Drawing.Brush brush, float x, float y, System.Drawing.StringFormat format)
void DrawString(string s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.PointF point, System.Drawing.StringFormat format)
If I use the Visual Studio Powershell Form I don't get a error, but I don't get columns either.. LOL
This is a simplified example of my code:
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = "ListView with Custom Column Headers"
$form.Size = New-Object System.Drawing.Size(400, 300)
$listView1 = New-Object System.Windows.Forms.ListView
$listView1.Location = New-Object System.Drawing.Point(10, 10)
$listView1.Size = New-Object System.Drawing.Size(380, 200)
$listView1.View = [System.Windows.Forms.View]::Details
$listView1.OwnerDraw = $true
# Add columns
$columnHeader1 = New-Object System.Windows.Forms.ColumnHeader
$columnHeader1.Tag = "First Line`nSecond Line"
$columnHeader2 = New-Object System.Windows.Forms.ColumnHeader
$columnHeader2.Tag = "Third Line`nFourth Line"
$listView1.Columns.AddRange(@($columnHeader1, $columnHeader2))
# Add items for demonstration
$listView1.Items.Add("Item 1") | Out-Null
$listView1.Items[0].SubItems.Add("Subitem 1") | Out-Null
$listView1.Items.Add("Item 2") | Out-Null
$listView1.Items[1].SubItems.Add("Subitem 2") | Out-Null
$listView1.Items.Add("Item 3") | Out-Null
$listView1.Items[2].SubItems.Add("Subitem 3") | Out-Null
# Subscribe to the DrawColumnHeader event
$listView1.add_DrawColumnHeader({
param($sender, $e)
$e.DrawDefault = $false
$Global:ee = $e
# Draw two rows of text
$sf = New-Object System.Drawing.StringFormat
$sf.LineAlignment = [System.Drawing.StringAlignment]::Center
$sf.Alignment = [System.Drawing.StringAlignment]::Center
$HalfHeight = $e.Bounds.Height / 2
$TopHeightHalf = $e.Bounds.Top + $e.Bounds.Height / 2
$firstLineRect = New-Object System.Drawing.Rectangle ($e.Bounds.Left, $e.Bounds.Top, $e.Bounds.Width, $e.Bounds.Height / 2)
$secondLineRect = New-Object System.Drawing.Rectangle ($e.Bounds.Left,$e.Bounds.Top + $e.Bounds.Height / 2 , $e.Bounds.Width, $HalfHeight)
$e.Graphics.DrawString("First Line", $listView1.Font, [System.Drawing.Brushes]::Black, $firstLineRect, $sf)
$e.Graphics.DrawString("Second Line", $listView1.Font, [System.Drawing.Brushes]::Black, $secondLineRect, $sf)
})
# Add ListView control to the form
$form.Controls.Add($listView1)
# Show the form
$form.ShowDialog() | Out-Null
You're missing an F
- change:
$firstLineRect = New-Object System.Drawing.Rectangle ($e.Bounds.Left, $e.Bounds.Top, $e.Bounds.Width, ($e.Bounds.Height / 2))
to
$firstLineRect = New-Object System.Drawing.RectangleF ($e.Bounds.Left, $e.Bounds.Top, $e.Bounds.Width, ($e.Bounds.Height / 2))
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ^ RectangleF
This appears to be caused by a difference in the way PowerShell does type coercion versus C# - your code is passing parameters to DrawString
with the following types:
$e.Graphics.DrawString(
"First Line", # System.String
$listView1.Font, # System.Drawing.Font
[System.Drawing.Brushes]::Black, # System.Drawing.SolidBrush
$firstLineRect, # System.Drawing.Rectangle
$sf # System.Drawing.StringFormat
)
There's no exact match for these parameters though - the overloads of DrawString
are:
write-host ($e.Graphics.DrawString | out-string)
OverloadDefinitions
-------------------
void DrawString(string s, System.Drawing.Font font, System.Drawing.Brush brush, float x, float y)
void DrawString(System.ReadOnlySpan[char] s, System.Drawing.Font font, System.Drawing.Brush brush, float x, float y)
void DrawString(string s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.PointF point)
void DrawString(System.ReadOnlySpan[char] s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.PointF point)
void DrawString(string s, System.Drawing.Font font, System.Drawing.Brush brush, float x, float y, System.Drawing.StringFormat format)
void DrawString(System.ReadOnlySpan[char] s, System.Drawing.Font font, System.Drawing.Brush brush, float x, float y, System.Drawing.StringFormat format)
void DrawString(string s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.PointF point, System.Drawing.StringFormat format)
void DrawString(System.ReadOnlySpan[char] s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.PointF point, System.Drawing.StringFormat format)
void DrawString(string s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.RectangleF layoutRectangle)
void DrawString(System.ReadOnlySpan[char] s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.RectangleF layoutRectangle)
void DrawString(string s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.RectangleF layoutRectangle, System.Drawing.StringFormat format)
void DrawString(System.ReadOnlySpan[char] s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.RectangleF layoutRectangle, System.Drawing.StringFormat format)
The C# compiler realises System.Drawing.SolidBrush
inherits from System.Drawing.Brush
, and it also finds the implicit conversion operator for RectangleF.Implicit(Rectangle to RectangleF)
and it uses this to invoke the following overload:
void DrawString(
string s,
System.Drawing.Font font,
System.Drawing.Brush brush,
System.Drawing.RectangleF layoutRectangle,
System.Drawing.StringFormat format
)
PowerShell, on the other hand, seems to be trying to bind to this overload of DrawString
for... reasons (basically it's not 100% identical to C#):
void DrawString(
string s,
System.Drawing.Font font,
System.Drawing.Brush brush,
System.Drawing.PointF point, # <--- PointF
System.Drawing.StringFormat format
)
and the error is saying it can't convert $firstLineRect
(a System.Drawing.Rectangle
) to a System.Drawing.PointF
for the point
parameter.
If you explicitly assign a System.Drawing.RectangleF
to $firstLineRect
PowerShell will select the same overload as C# and you're golden...
... but don't forget to specifically assign a System.Drawing.RectangleF
(with the F
again) to $secondLineRect
as well otherwise you're back to square one...
You could also explicitly cast to System.Drawing.Rectangle
and PowerShell will invoke the appropriate conversion operator fine:
$r = new-object System.Drawing.Rectangle(10, 10, 10, 10)
[System.Drawing.RectangleF] $r
Location : {X=10, Y=10}
Size : {Width=10, Height=10}
X : 10
Y : 10
Width : 10
Height : 10
Left : 10
Top : 10
Right : 20
Bottom : 20
IsEmpty : False
but it seems it doesn't consider the implicit conversion operator during overload selection :-(
To use this in your code:
$e.Graphics.DrawString(
"First Line",
$listView1.Font,
[System.Drawing.Brushes]::Black,
[System.Drawing.RectangleF] $firstLineRect, # <-- explicitly convert to RectangleF
$sf
)