I have a listview with some rows, and all have a progressBar.
Adjusting column width works fine (as by design), however as it easily moves text within rows, it does NOT move the progressbar, resize it or even move it around.. without writing this:
private void fileList_ColumnWidthChanged(object sender, ColumnWidthChangedEventArgs e)
{
foreach (CustomProgressBar p in fileList.Controls.OfType<CustomProgressBar>().ToList())
{
if (e.ColumnIndex == 0)
{
p.Width = fileList.Columns[3].Width;
p.Left = fileList.Columns[e.ColumnIndex].Width 400;
}
else if (e.ColumnIndex == 1)
{
p.Width = fileList.Columns[3].Width;
p.Left = fileList.Columns[e.ColumnIndex].Width 227;
}
}
}
I couldn't figure it out, so that was my only solution.. which "partially works".
General column resizing (no particular order)
Is there something I am missing about automatically aligning items correctly according to the column position or width?
The code I wrote has zero effect after the 2nd column, as I can not confirm if it is working correctly for column1/column2 to begin with. So I dont see a point implementing the rest of it, if its set to fail anyway.
Any help is appreciated.
.NET Framework 4.8 / WinForms
--
NOTE: ProgressBar responds to all typical properties as would a regular progressBar. The difference is just the overlay shade and color. Nothing more.
CodePudding user response:
Example
The highlights of the code:
- Set the OwnerDraw = true.
- Handle DrawColumnHeaders and set e.DrawDefault = true.
- Handle DrawSubItems and for the progress column, draw a ProgressBar.
- Enable
Example
Highlights of the code:
- You need to adjust control size, at least in the following events: ColumnWidthChanged, ColumnWidthChanging, ColumnReordered, SizeChanged. There might be more.
- You also need to subclass ListView and handle MouseWheel, VerticalScroll and HorizontalScroll events. (By handling WM_VSCROLL, WM_HSCROLL, and WM_MOUSEWHEEL messages.)
- In the subclass, you need to hide the controls which are behind the header. (By sending LVM_GETHEADER message and getting the header rectangle using GetWindowRect.)
- When you adjust control position and size, you need to consider AutoScrollPosition as well.
Here is the code:
using System; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; public class ListViewProgressBarExample : Form { private MyListView listView1; private void InitializeComponent() { listView1 = new MyListView(); listView1.Columns.Add("#", 50); listView1.Columns.Add("File name", 150); listView1.Columns.Add("Progress", 200); listView1.View = View.Details; listView1.Dock = DockStyle.Fill; listView1.FullRowSelect = true; Controls.Add(listView1); Text = "ListView ProgressBar Example"; Size = new Size(500, 300); } public ListViewProgressBarExample() { InitializeComponent(); listView1.Items.Add(new ListViewItem( new[] { "1", "lorem ipsum.txt", "10" })); listView1.Items.Add(new ListViewItem( new[] { "2", "dolor sit.txt", "0" })); listView1.Items.Add(new ListViewItem( new[] { "3", "amet.txt", "30" })); listView1.Items.Add(new ListViewItem( new[] { "4", "consectetur adipiscing.txt", "70" })); listView1.Items.Add(new ListViewItem( new[] { "5", "celit.txt", "50" })); listView1.Items.Add(new ListViewItem( new[] { "6", "adipiscing elit.txt", "100" })); foreach (ListViewItem item in listView1.Items) { var p = new ProgressBar(); p.Value = int.Parse(item.SubItems[2].Text); item.SubItems[2].Tag = p; if (p != null) { p.Bounds = item.SubItems[2].Bounds; listView1.Controls.Add(p); p.SendToBack(); } } listView1.ColumnWidthChanged = (sender, args) => AdjustControls(); listView1.ColumnReordered = (sender, args) => AdjustControls(); listView1.ColumnWidthChanging = (sender, args) => AdjustControls(); listView1.Scrolled = (sender, args) => AdjustControls(); listView1.SizeChanged = (sender, args) => AdjustControls(); } private void AdjustControls() { foreach (ListViewItem item in listView1.Items) { foreach (ListViewItem.ListViewSubItem subItem in item.SubItems) { if (subItem.Tag is Control) { var bound = subItem.Bounds; bound.Offset(listView1.AutoScrollOffset); var c = (Control)subItem.Tag; c.Bounds = subItem.Bounds; c.Visible = bound.Top >= listView1.GetHeaderHeight(); } } } } } public class MyListView : ListView { private const int WM_HSCROLL = 0x114; private const int WM_VSCROLL = 0x115; private const int WM_MOUSEWHEEL = 0x020A; public event EventHandler Scrolled; private const int LVM_GETHEADER = 0x1000 31; [StructLayout(LayoutKind.Sequential)] private struct RECT { public int Left; public int Top; public int Right; public int Bottom; } [DllImport("user32.dll")] private static extern IntPtr SendMessage( IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] private static extern bool GetWindowRect( IntPtr hwnd, out RECT lpRect); public int GetHeaderHeight() { var r = new RECT(); var hwnd = SendMessage(Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero); if (GetWindowRect(hwnd, out r)) return r.Bottom - r.Top; return -1; } protected override void WndProc(ref Message m) { base.WndProc(ref m); if (m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL || m.Msg == WM_MOUSEWHEEL) Scrolled?.Invoke(this, EventArgs.Empty); } }