C# ListView最终列自动调整大小创建滚动条
我正在实现一个从ListView派生的自定义控件 我想让最后一列填充剩余的空间(这是一项非常常见的任务),我已经通过重写OnResize方法完成了这项工作:C# ListView最终列自动调整大小创建滚动条,c#,winforms,listview,C#,Winforms,Listview,我正在实现一个从ListView派生的自定义控件 我想让最后一列填充剩余的空间(这是一项非常常见的任务),我已经通过重写OnResize方法完成了这项工作: protected override void OnResize(EventArgs e) { base.OnResize(e); if (Columns.Count == 0) return; Columns[Columns.Count - 1].
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
if (Columns.Count == 0)
return;
Columns[Columns.Count - 1].Width = -2; // -2 = Fill remaining space
}
或通过另一种方法:
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
if (!_autoFillLastColumn)
return;
if (Columns.Count == 0)
return;
int TotalWidth = 0;
int i = 0;
for (; i < Columns.Count - 1; i++)
{
TotalWidth += Columns[i].Width;
}
Columns[i].Width = this.DisplayRectangle.Width - TotalWidth;
}
protected override void OnResize(事件参数e)
{
基数(e);
如果(!\u autoFillLastColumn)
返回;
如果(Columns.Count==0)
返回;
int TotalWidth=0;
int i=0;
对于(;i
编辑:
在我将ListView停靠到父容器并通过该控件调整大小之前,这一切都很正常。每当控件大小缩小(即,将边框拖动一个像素)时,底部就会出现一个无法移动的滚动条(甚至一个像素也不能移动)
其结果是,当我拖动父对象的大小时,我在ListView中留下了一个闪烁的滚动条,当拖动停止时,它有50%的可能性出现。在进行更改后,尝试调用
base.OnResize()
。尝试使用ClientSize
属性。正如它所说:
控件的客户端区域是控件的边界,减去非客户端元素,如滚动条、边框、标题栏和菜单
编辑:我添加了一个代码示例,演示如何实现不同的填充行为
public class MyListView : ListView
{
public MyListView()
{
View = View.Details;
Columns.Add(Column1);
Columns.Add(Column2);
Columns.Add(Column3);
}
private readonly ColumnHeader Column1 = new ColumnHeader();
private readonly ColumnHeader Column2 = new ColumnHeader();
private readonly ColumnHeader Column3 = new ColumnHeader();
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
// Constant width
Column1.Width = 200;
// 50% of the ListView's width
Column2.Width = (ClientSize.Width/2);
// Fill the remaining width, but no less than 100 px
Column3.Width = Math.Max(100, ClientSize.Width - (Column1.Width + Column2.Width));
}
}
(围绕.NET WinForms ListView的开源包装)允许“展开最后一列以填充可用空间”。事实上,要做到正确要比第一次出现时困难得多——不要相信任何人告诉你的相反的话:)
如果您使用ObjectListView,您可以免费获得该问题的解决方案以及其他几个问题的解决方案。但如果你想自己完成所有工作,你需要:
- 使用ClientSize而不是DisplayRectangle(正如@zxpro已经说过的)
- 使用布局事件而不是调整事件大小
- 侦听ColumnResized事件并执行相同的工作
- 不要在设计模式下自动调整大小——这会让人非常困惑
protected override void WndProc(ref Message m) {
switch (m.Msg) {
case 0x46: // WM_WINDOWPOSCHANGING
this.HandleWindowPosChanging(ref m);
base.WndProc(ref m);
break;
default:
base.WndProc(ref m);
break;
}
}
protected virtual void HandleWindowPosChanging(ref Message m) {
const int SWP_NOSIZE = 1;
NativeMethods.WINDOWPOS pos = (NativeMethods.WINDOWPOS)m.GetLParam(typeof(NativeMethods.WINDOWPOS));
if ((pos.flags & SWP_NOSIZE) == 0) {
if (pos.cx < this.Bounds.Width) // only when shrinking
// pos.cx is the window width, not the client area width, so we have to subtract the border widths
this.ResizeFreeSpaceFillingColumns(pos.cx - (this.Bounds.Width - this.ClientSize.Width));
}
}
受保护的覆盖无效WndProc(参考消息m){
开关(m.Msg){
案例0x46://WM\u
本手册中所述为手动切换(参考m);
基准WndProc(参考m);
打破
违约:
基准WndProc(参考m);
打破
}
}
受保护的虚拟无效句柄INDOWPOSCHING(参考消息m){
常数int SWP_NOSIZE=1;
NativeMethods.WINDOWPOS=(NativeMethods.WINDOWPOS)m.getLPRAM(typeof(NativeMethods.WINDOWPOS));
如果((位置标志和SWP_NOSIZE)==0){
if(pos.cx
这可能已经很晚了,但我认为最简单的解决方案是添加一个函数来处理窗口大小调整事件。我的listview定位在右侧,因此当我调整窗口大小时,listview也会调整大小。在处理窗口大小调整的函数中,我设置myListView.Columns[lastColumnIndex].Width=-2
这显然会重新绘制listview,将最后一列扩展到控件的边缘。希望这有帮助 这段代码对我来说非常有效:
const int SWP_NOSIZE = 1;
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPOS
{
public IntPtr hwnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public int flags;
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case 0x46: // WM_WINDOWPOSCHANGING
this.HandleWindowPosChanging(ref m);
base.WndProc(ref m);
break;
default:
base.WndProc(ref m);
break;
}
}
protected virtual void HandleWindowPosChanging(ref Message m)
{
try
{
WINDOWPOS pos = new WINDOWPOS();
pos = (WINDOWPOS)System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, pos.GetType());
if (m_EnableAutoColumnResize && this.Columns.Count > 0 && (pos.flags & SWP_NOSIZE) == 0)
{
this.ResizeFreeSpaceFillingColumns(pos.cx - (this.Bounds.Width - this.ClientSize.Width));
}
}
catch (ArgumentException) { }
}
private void ResizeFreeSpaceFillingColumns(int listViewWitdh)
{
int lastColumn = this.Columns.Count - 1;
int sumWidth = 0;
for (int i = 0; i < lastColumn; i++)
{
sumWidth += this.Columns[i].Width;
}
this.Columns[lastColumn].Width = listViewWitdh - sumWidth;
}
常数int SWP_NOSIZE=1;
[StructLayout(LayoutKind.Sequential)]
公共结构WINDOWPOS
{
公共IntPtr hwnd;
公共IntPtr HwnInsertafter;
公共int x;
公共智力;
公共int cx;
公共关系;
公共国旗;
}
受保护的覆盖无效WndProc(参考消息m)
{
开关(m.Msg)
{
案例0x46://WM\u
本手册中所述为手动切换(参考m);
基准WndProc(参考m);
打破
违约:
基准WndProc(参考m);
打破
}
}
受保护的虚拟无效句柄INDOWPOSCHING(参考消息m)
{
尝试
{
WINDOWPOS pos=新的WINDOWPOS();
pos=(WINDOWPOS)System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam,pos.GetType());
如果(m_EnableAutoColumnResize&&this.Columns.Count>0&&(pos.flags&SWP_NOSIZE)==0)
{
this.ResizeFreeSpaceFillingColumns(pos.cx-(this.Bounds.Width-this.ClientSize.Width));
}
}
捕获(参数异常){}
}
私有void ResizeFreeSpaceFillingColumns(int-listViewWitdh)
{
int lastColumn=this.Columns.Count-1;
int sumWidth=0;
对于(int i=0;i
问候
Lary您需要重写受保护的方法
SetBoundsCore()
,并在委托给base.SetBoundsCore()
之前或之后执行列大小调整,具体取决于ListView宽度是缩小还是扩大
以下示例为单列ListView的硬编码示例:
protected override void SetBoundsCore( int x, int y, int width, int height, BoundsSpecified specified )
{
ResizeColumns( width, true );
base.SetBoundsCore( x, y, width, height, specified );
ResizeColumns( width, false );
}
private void ResizeColumns( int controlWidth, bool ifShrinking )
{
if( Columns.Count < 1 || Parent == null )
return;
int borderGap = Width - ClientSize.Width;
int desiredWidth = controlWidth - borderGap;
if( (desiredWidth < Columns[0].Width) == ifShrinking )
Columns[0].Width = desiredWidth;
}
protected override void SetBoundsCore(整数x、整数y、整数宽度、整数高度、指定的边界)
{
调整列的大小(宽度,真值);
基础.立根分数(x,y,宽度,高度,规定);
调整列的大小(宽度,false);
}
私有void ResizeColumns(int-controlWidth,bool-ifresking)
{
if(Columns.Count<1 | | Parent==null)
返回;
int
// Member variable.
private ListView myListView;
// Method to resize last column.
private void ResizeColumn(int width) {
myListView.Columns[myListView.Columns.Count - 1].Width = width;
}
// ListView ClientSizeChanged event handler.
private void Process_ListViewClientSizeChanged(object sender, EventArgs e)
{
// Get the width to resize the last column.
int width = myListView.ClientSize.Width;
for (int i = 0; i < myListView.Columns.Count - 1; i++)
width -= myListView.Columns[i].Width;
// Last column width is growing. Use magic number to resize.
if (width >= myListView.Columns[myListView.Columns.Count - 1].Width)
myListView.Columns[myListView.Columns.Count - 1].Width = -2;
// Last column width is shrinking.
// Asynchronously invoke a method to resize the last column.
else
myListView.BeginInvoke
((MethodInvoker)delegate { ResizeColumn(width); });
}
private void ContentSplit_Panel1_ClientSizeChanged(object sender, EventArgs e)
{
CaseFilter.Bounds = ContentSplit.Panel1.ClientRectangle;
if(CaseFilter.Columns.Count == 1)
CaseFilter.Columns[0].Width = CaseFilter.ClientRectangle.Width;
}