C# 在详细信息视图中隐藏Windows窗体ListView列,而不删除列或将其大小调整为零
我在C#NET2.0Windows中创建了一个列表视图,其中大约有十列在应用程序启动时填充了数据。数据量巨大,因此不能在其间快速重新加载。My ListView上有详细信息视图,允许用户分别调整每个列的大小 用户应能够一次隐藏十列中的任何一列或多列,并在非特定行中随时再次取消隐藏列。隐藏列时不应删除数据 我尝试了以下几件事,但结果并不令人满意: 1) 调整大小为0的列的大小将使其在用户开始播放之前有所消失 与列一起。用户会意外地重新调整它们的大小,因为在我的ListView中,允许用户手动调整每个列的大小 2) 只删除列会出现以下问题:当我尝试重新添加列时,列不记得其位置和大小。位置是主要问题,我将尝试解释原因。如果我的用户决定先隐藏“第2列”,然后隐藏“第3列”,然后用户在第2列之前取消隐藏第3列,那么“索引2”就不存在,因此我无法在索引3处插入,出现异常。即使我记得删除前的索引位置,我也不能简单地将列放回该索引,因为我不知道以前的列是否也已隐藏,或者之前和之后缺少了哪一列,或者是否隐藏了。 所以场景可能是这样的:1个显示,2个隐藏,3个隐藏,4个显示,5个隐藏,6个隐藏,7个隐藏,8个显示,9个隐藏,10个隐藏 可能的解决方案“1”和“2”在场景中自动排除。 更好的方法是将列的宽度设置为零,但是因为我的用户可以调整大小 列可以根据需要随时显示,如果大小调整为零,则无法向用户隐藏。用户将通过调整大小将其取消隐藏,我的系统将认为它仍然是隐藏的,等等。如果隐藏的列可以“重新调整大小”,或者我们如何命名其他列,则看起来不专业 有人有更好的主意吗?我想知道为什么listView列没有“可见”或“隐藏”属性? 如果有人之前已经这样做了,请发布解决方案C# 在详细信息视图中隐藏Windows窗体ListView列,而不删除列或将其大小调整为零,c#,winforms,listview,hide,columnheader,C#,Winforms,Listview,Hide,Columnheader,我在C#NET2.0Windows中创建了一个列表视图,其中大约有十列在应用程序启动时填充了数据。数据量巨大,因此不能在其间快速重新加载。My ListView上有详细信息视图,允许用户分别调整每个列的大小 用户应能够一次隐藏十列中的任何一列或多列,并在非特定行中随时再次取消隐藏列。隐藏列时不应删除数据 我尝试了以下几件事,但结果并不令人满意: 1) 调整大小为0的列的大小将使其在用户开始播放之前有所消失 与列一起。用户会意外地重新调整它们的大小,因为在我的ListView中,允许用户手动调整每
我必须补充一点,在添加数据时,我会自动调整listview中所有列的大小。由于这个原因,下面的答案不起作用。调整大小事件无法检测到-1的宽度。添加数据时,宽度为0的“所有不可见列”将重新调整大小。由于listview删除了与列长度重叠的数据,因此我需要永久地自动调整其大小。Explorer没有这个问题,因为它使列适合数据的长度。C#没有这样的高级listview,在这里,每次添加数据时,我都必须将列设置为-1。在这个概念中,column.width=0隐藏列的想法不起作用。好的,您的问题实际上是如何隐藏ListView列?。这是许多人在网上提出的问题。我试着搜索了很多,但是什么也找不到。我得到了唯一的解决方案:将列宽设置为零。这将在这里使用一些技巧:
//This will try hiding the column at index 1
listView1.Columns[1].Width = 0;
//ColumnWidthChanging event handler of your ListView
private void listView1_ColumnWidthChanging(object sender, ColumnWidthChangingEventArgs e){
if(e.ColumnIndex == 1){
e.Cancel = true;
e.NewWidth = 0;
}
}
它几乎可以完美地工作。但是,当用户将鼠标移动到隐藏列位置的管道上时,会出现一个光标指示器,通知用户此处有一个宽度为零的列,只需按住鼠标并拖动即可调整其大小。当然,用户不能从零调整它的大小,因为我们取消它并使新宽度=0
(就像上面的代码那样)。但通知此类操作的光标
有点令人讨厌,下面是演示问题的屏幕截图:
解决这个问题并不容易。至少我是这么想的。我想到了这个解决方案,似乎效果不错。想法是我们必须检测鼠标是否在隐藏列的管道附近,我们必须设置光标=光标。箭头。以下是我认为对你非常有用的整个课程:
public class CustomListView : ListView
{
[DllImport("user32")]
private static extern bool EnumChildWindows(IntPtr parentHwnd, EnumChildProc proc, object lParam);
delegate bool EnumChildProc(IntPtr childHwnd, object lParam);
public CustomListView()
{
VisibleChanged += (s, e) =>
{
if (Visible && headerHandle == IntPtr.Zero&&!DesignMode)
{
EnumChildWindows(Handle, EnumChild, null);
headerProc = new HeaderProc(this);
headerProc.AssignHandle(headerHandle);
}
};
columnPipeLefts[0] = 0;
}
//Save the Handle to the Column Headers, a ListView has only child Window which is used to render Column headers
IntPtr headerHandle;
//This is used use to hook into the message loop of the Column Headers
HeaderProc headerProc;
private bool EnumChild(IntPtr childHwnd, object lParam)
{
headerHandle = childHwnd;
return true;
}
//Updated code
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x101e&&hiddenColumnIndices.Contains(m.WParam.ToInt32()))//WM_SETCOLUMNWIDTH = 0x101e
{
if(m.LParam.ToInt32() > 0) hiddenColumnWidths[m.WParam.ToInt32()] = m.LParam.ToInt32();
return;//Discard the message changing hidden column width so that it won't be shown again.
}
base.WndProc(ref m);
}
//Save the column indices which are hidden
List<int> hiddenColumnIndices = new List<int>();
//Save the width of hidden columns
Dictionary<int, int> hiddenColumnWidths = new Dictionary<int, int>();
//Save the Left (X-Position) of the Pipes which separate Column Headers.
Dictionary<int, int> columnPipeLefts = new Dictionary<int, int>();
protected override void OnColumnWidthChanging(ColumnWidthChangingEventArgs e)
{
if (hiddenColumnIndices.Contains(e.ColumnIndex))
{
e.Cancel = true;
e.NewWidth = 0;
}
base.OnColumnWidthChanging(e);
}
//We need to update columnPipeLefts whenever the width of any column changes
protected override void OnColumnWidthChanged(ColumnWidthChangedEventArgs e)
{
base.OnColumnWidthChanged(e);
UpdateColumnPipeLefts(Columns[e.ColumnIndex].DisplayIndex + 1);
}
int index = -1;
protected override void OnColumnReordered(ColumnReorderedEventArgs e)
{
int i = Math.Min(e.NewDisplayIndex, e.OldDisplayIndex);
index = index != -1 ? Math.Min(i + 1, index) : i + 1;
base.OnColumnReordered(e);
}
//This is used to update the columnPipeLefts every reordering columns or resizing columns.
private void UpdateColumnPipeLefts(int fromIndex)
{
int w = fromIndex > 0 ? columnPipeLefts[fromIndex - 1] : 0;
for (int i = fromIndex; i < Columns.Count; i++)
{
w += i > 0 ? Columns.OfType<ColumnHeader>().Where(k=>k.DisplayIndex == i - 1).Single().Width : 0;
columnPipeLefts[i] = w;
}
}
//This is used to hide a column with ColumnHeader passed in
public void HideColumn(ColumnHeader col)
{
if (!hiddenColumnIndices.Contains(col.Index))
{
hiddenColumnWidths[col.Index] = col.Width;//Save the current width to restore later
col.Width = 0;//Hide the column
hiddenColumnIndices.Add(col.Index);
}
}
//This is used to hide a column with column index passed in
public void HideColumn(int columnIndex)
{
if (columnIndex < 0 || columnIndex >= Columns.Count) return;
if (!hiddenColumnIndices.Contains(columnIndex))
{
hiddenColumnWidths[columnIndex] = Columns[columnIndex].Width;//Save the current width to restore later
Columns[columnIndex].Width = 0;//Hide the column
hiddenColumnIndices.Add(columnIndex);
}
}
//This is used to show a column with ColumnHeader passed in
public void ShowColumn(ColumnHeader col)
{
hiddenColumnIndices.Remove(col.Index);
if(hiddenColumnWidths.ContainsKey(col.Index))
col.Width = hiddenColumnWidths[col.Index];//Restore the Width to show the column
hiddenColumnWidths.Remove(col.Index);
}
//This is used to show a column with column index passed in
public void ShowColumn(int columnIndex)
{
if (columnIndex < 0 || columnIndex >= Columns.Count) return;
hiddenColumnIndices.Remove(columnIndex);
if(hiddenColumnWidths.ContainsKey(columnIndex))
Columns[columnIndex].Width = hiddenColumnWidths[columnIndex];//Restore the Width to show the column
hiddenColumnWidths.Remove(columnIndex);
}
//The helper class allows us to hook into the message loop of the Column Headers
private class HeaderProc : NativeWindow
{
[DllImport("user32")]
private static extern int SetCursor(IntPtr hCursor);
public HeaderProc(CustomListView listView)
{
this.listView = listView;
}
bool mouseDown;
CustomListView listView;
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x200 && listView!=null && !mouseDown)
{
int x = (m.LParam.ToInt32() << 16) >> 16;
if (IsSpottedOnAnyHiddenColumnPipe(x)) return;
}
if (m.Msg == 0x201) {
mouseDown = true;
int x = (m.LParam.ToInt32() << 16) >> 16;
IsSpottedOnAnyHiddenColumnPipe(x);
}
if (m.Msg == 0x202) mouseDown = false;
if (m.Msg == 0xf && listView.index != -1 && MouseButtons == MouseButtons.None) { //WM_PAINT = 0xf
listView.UpdateColumnPipeLefts(listView.index);
listView.index = -1;
};
base.WndProc(ref m);
}
private bool IsSpottedOnAnyHiddenColumnPipe(int x)
{
foreach (int i in listView.hiddenColumnIndices.Select(j=>listView.Columns[j].DisplayIndex))
{
if (x > listView.columnPipeLefts[i] - 1 && x < listView.columnPipeLefts[i] + 15)
{
SetCursor(Cursors.Arrow.Handle);
return true;
}
}
return false;
}
}
}
公共类CustomListView:ListView
{
[DllImport(“user32”)]
私有静态外部bool EnumChildWindows(IntPtr parentHwnd、EnumChildProc proc、object lParam);
委托bool EnumChildProc(IntPtr childHwnd,对象lParam);
公共CustomListView()
{
VisibleChanged+=(s,e)=>
{
if(可视和标题句柄==IntPtr.Zero&&!设计模式)
{
EnumChildWindows(句柄,EnumChild,null);
headerProc=新的headerProc(本);
headerProc.AssignHandle(headerHandle);
}
};
ColumnPiplefts[0]=0;
}
//将句柄保存到列标题,ListView只有一个子窗口用于呈现列标题
IntPtr头手柄;
//这用于钩住列标题的消息循环
HeaderProc HeaderProc;
私有bool EnumChild(IntPtr childHwnd,对象lParam)
{
头把手=儿童手;
返回true;
}
//更新代码
受保护的覆盖无效WndProc(参考消息m)
{
如果(m.Msg==0x101e&&hiddenColumnIndexs.Contains(m.WParam.ToInt32())//WM\u SETCOLUMNWIDTH=0x101e
{
如果(m.LParam.ToInt32()>0)hiddenColumnWidths[m.WParam.ToInt32()]=m.LParam.ToInt32();
return;//放弃更改隐藏列宽的消息,使其不再显示。
}
基准WndProc(参考m);
}
//保存隐藏的列索引
List HiddenColumnIndexes=新列表();
//保存隐藏列的宽度
Dictionary hiddenColumnWidths=新字典();
//保存分隔列标题的管道的左侧(X位置)。
辞典
[DllImport("user32.dll", EntryPoint = "SendMessage")]
private static extern IntPtr SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
const int LVM_FIRST = 0x1000;
const int LVM_GETHEADER = (LVM_FIRST + 31);
public CustomListView()
{
VisibleChanged += (s, e) =>
{
if (Visible && headerHandle == IntPtr.Zero && !DesignMode)
{
IntPtr hwnd = SendMessage(this.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
if (hwnd != null)
{
headerProc = new HeaderProc(this);
headerHandle = hwnd;
headerProc.AssignHandle(hwnd);
}
}
};
columnPipeLefts[0] = 0;
}