向 ListView 添加列时自动水平滚动
Auto scroll horizontally while adding columns to a ListView
我想在每次添加 ColumnHeader 时自动水平滚动 ListView 到最后一列。
我只需要滚动到最后一列,而不是项目。
listView1.Alignment
设置为左
listView1.View
设置为 View.Details
我试过了,但没有任何效果:
listView1.AutoScrollOffset = new Point(listView1.AutoScrollOffset.X-10, 0);`
这仅在我仅添加项目时有效:
listView1.EnsureVisible(0);
string rowstr = "Test,";
for (var i = 0; i < 10; i++)
{
Debug.WriteLine(i);
ColumnHeader head = new ColumnHeader();
head.Text = i.toString();
listView1.Columns.Add(head);
listView1.Columns[i].Width = 65;
rowstr += "Test"+",";
string[] row = rowstr.Split(",");
var listViewItem = new ListViewItem(row);
listViewItem.Font = new Font("Consolas", 10f);
listView1.Items.Insert(0, listViewItem);
//listView1.EnsureVisible(0);
listView1.AutoScrollOffset = new Point(listView1.AutoScrollOffset.X-10, 0);
}
我没有尝试覆盖 WndProc 或调用 User32 函数,但如果这是唯一的选择,我们将不胜感激。
如果不用PInvoking也可以,那就更好了。
要水平滚动 ListView,您可以向控件发送 LVM_SCROLL 消息,将 wParam
设置为与要滚动到的位置(以像素为单位)相对应的值。
位置是相对于当前的偏移量。
设置 lParam
垂直滚动。
因为你想滚动到最后一列并且 ListView 处于 Details
模式,你可以只传递 int.MaxValue
作为偏移量:Win32 控件将进行调整(它会这样做无论如何)。
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, int lParam);
private const int LVM_SCROLL = 0x1014;
SendMessage(listView1.Handle, LVM_SCROLL, int.MaxValue, 0);
编辑:
修改您的代码,在每个 Column/ListItem 插入之间添加一个间隔,以慢动作查看它。您可以使用 Button.Click
处理程序。
private async void SomeButton_Click(object sender, EventArgs e)
{
for (int idx = 0; idx < 10; idx++) {
var head = new ColumnHeader() {
Text = idx.ToString(),
Width = 65
};
listView1.Columns.Add(head);
SendMessage(listView1.Handle, LVM_SCROLL, int.MaxValue, 0);
var rowArray = new List<string>(Enumerable.Range(0, idx + 1).Select(n => $"Test{n}"));
var listViewItem = new ListViewItem(rowArray.ToArray());
listView1.Items.Insert(0, listViewItem);
SendMessage(listView1.Handle, LVM_SCROLL, int.MaxValue, 0);
await Task.Delay(500);
}
}
删除 async
内容以使其免费。
要获取当前滚动位置,如果需要,请使用 GetScrollInfo()。例如,
var scrollInfo = new SCROLLINFO(SBInfoMask.SIF_ALL);
bool result = GetScrollInfo(listView1.Handle, SBParam.SB_HORZ, ref scrollInfo);
SCROLLINFO 结构的 nPos
成员 returns 当前滚动位置。 nMax
最大滚动值。减去nPage
,对应Control的ClientSize.Width
,得到最大可能的滚动值。
当拇指滚动到末尾时,它应该等于nPos
。
Win32 声明:
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool GetScrollInfo(IntPtr hwnd, SBParam nBar, [In, Out] ref Point lpsi);
[StructLayout(LayoutKind.Sequential)]
internal struct SCROLLINFO
{
public uint cbSize;
public SBInfoMask fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
public SCROLLINFO(SBInfoMask mask)
{
cbSize = (uint)Marshal.SizeOf<SCROLLINFO>();
fMask = mask;
nMin = 0; nMax = 0; nPage = 0; nPos = 0; nTrackPos = 0;
}
}
internal enum SBInfoMask : uint
{
SIF_RANGE = 0x1,
SIF_PAGE = 0x2,
SIF_POS = 0x4,
SIF_DISABLENOSCROLL = 0x8,
SIF_TRACKPOS = 0x10,
SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS),
SIF_POSRANGE = (SIF_RANGE | SIF_POS | SIF_PAGE)
}
public enum SBParam : int
{
SB_HORZ = 0x0,
SB_VERT = 0x1,
SB_CTL = 0x2,
SB_BOTH = 0x3
}
您可以创建 CustomListView
和 Re_Define
EnsureVisible
方法
class DataFlowFilterListView : ListView
{
/// <summary>
/// margin from the selected column to the border of listview.
/// </summary>
const int MARGIN = 20;
/// <summary>
/// native windows message to scroll the listview.
/// </summary>
const Int32 LVM_FIRST = 0x1000;
const Int32 LVM_SCROLL = LVM_FIRST + 20;
[DllImport("user32")]
static extern IntPtr SendMessage(IntPtr Handle, Int32 msg, IntPtr wParam,
IntPtr lParam);
private void ScrollHorizontal(int pixelsToScroll)
{
SendMessage(this.Handle, LVM_SCROLL, (IntPtr)pixelsToScroll,
IntPtr.Zero);
}
/// <summary>
/// Ensure visible of a ListViewItem and SubItem Index.
///
///
/// </summary>
/// <param name="item"></param>
/// <param name="subItemIndex"></param>
public void EnsureVisible(ListViewItem item, int subItemIndex)
{
if (item == null || subItemIndex > item.SubItems.Count - 1)
{
throw new ArgumentException();
}
// scroll to the item row.
item.EnsureVisible();
ScrollToRectangle(item.SubItems[subItemIndex].Bounds.Width);
}
/// <summary>
/// Scrolls the listview.
/// </summary>
/// <param name="bounds"></param>
private void ScrollToRectangle(int width)
{
this.ScrollHorizontal(width);
}
}
现在您可以使用 DataFlowFilterListView
代替 ListView
。
每次向列表中添加一列时执行以下代码
listView1.EnsureVisible(listView1.Items[0], 0);
我的解决方案是使用 user32 函数和 SendMessage()
。
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
private const int WM_HSCROLL = 0x114; //for horizontal scroll
private const int WPARAM = 7; //to scroll to end
实施:
string rowstr = "Code,"
for (var i = 0; i < 10; i++)
{
ColumnHeader head = new ColumnHeader();
head.Text = i.toString();
listView1.Columns.Add(head);
listView1.Columns[i].Width = 65;
rowstr += "Test"+",";
string[] row = rowstr.Split(",");
var listViewItem = new ListViewItem(row);
listViewItem.Font = new Font("Consolas", 10f);
listView1.Items.Insert(0, listViewItem);
SendMessage(listView1.Handle, WM_HSCROLL, WPARAM, 0);
}
我想在每次添加 ColumnHeader 时自动水平滚动 ListView 到最后一列。
我只需要滚动到最后一列,而不是项目。
listView1.Alignment
设置为左listView1.View
设置为View.Details
我试过了,但没有任何效果:
listView1.AutoScrollOffset = new Point(listView1.AutoScrollOffset.X-10, 0);`
这仅在我仅添加项目时有效:
listView1.EnsureVisible(0);
string rowstr = "Test,";
for (var i = 0; i < 10; i++)
{
Debug.WriteLine(i);
ColumnHeader head = new ColumnHeader();
head.Text = i.toString();
listView1.Columns.Add(head);
listView1.Columns[i].Width = 65;
rowstr += "Test"+",";
string[] row = rowstr.Split(",");
var listViewItem = new ListViewItem(row);
listViewItem.Font = new Font("Consolas", 10f);
listView1.Items.Insert(0, listViewItem);
//listView1.EnsureVisible(0);
listView1.AutoScrollOffset = new Point(listView1.AutoScrollOffset.X-10, 0);
}
我没有尝试覆盖 WndProc 或调用 User32 函数,但如果这是唯一的选择,我们将不胜感激。
如果不用PInvoking也可以,那就更好了。
要水平滚动 ListView,您可以向控件发送 LVM_SCROLL 消息,将 wParam
设置为与要滚动到的位置(以像素为单位)相对应的值。
位置是相对于当前的偏移量。
设置 lParam
垂直滚动。
因为你想滚动到最后一列并且 ListView 处于 Details
模式,你可以只传递 int.MaxValue
作为偏移量:Win32 控件将进行调整(它会这样做无论如何)。
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, int lParam);
private const int LVM_SCROLL = 0x1014;
SendMessage(listView1.Handle, LVM_SCROLL, int.MaxValue, 0);
编辑:
修改您的代码,在每个 Column/ListItem 插入之间添加一个间隔,以慢动作查看它。您可以使用 Button.Click
处理程序。
private async void SomeButton_Click(object sender, EventArgs e)
{
for (int idx = 0; idx < 10; idx++) {
var head = new ColumnHeader() {
Text = idx.ToString(),
Width = 65
};
listView1.Columns.Add(head);
SendMessage(listView1.Handle, LVM_SCROLL, int.MaxValue, 0);
var rowArray = new List<string>(Enumerable.Range(0, idx + 1).Select(n => $"Test{n}"));
var listViewItem = new ListViewItem(rowArray.ToArray());
listView1.Items.Insert(0, listViewItem);
SendMessage(listView1.Handle, LVM_SCROLL, int.MaxValue, 0);
await Task.Delay(500);
}
}
删除 async
内容以使其免费。
要获取当前滚动位置,如果需要,请使用 GetScrollInfo()。例如,
var scrollInfo = new SCROLLINFO(SBInfoMask.SIF_ALL);
bool result = GetScrollInfo(listView1.Handle, SBParam.SB_HORZ, ref scrollInfo);
SCROLLINFO 结构的 nPos
成员 returns 当前滚动位置。 nMax
最大滚动值。减去nPage
,对应Control的ClientSize.Width
,得到最大可能的滚动值。
当拇指滚动到末尾时,它应该等于nPos
。
Win32 声明:
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool GetScrollInfo(IntPtr hwnd, SBParam nBar, [In, Out] ref Point lpsi);
[StructLayout(LayoutKind.Sequential)]
internal struct SCROLLINFO
{
public uint cbSize;
public SBInfoMask fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
public SCROLLINFO(SBInfoMask mask)
{
cbSize = (uint)Marshal.SizeOf<SCROLLINFO>();
fMask = mask;
nMin = 0; nMax = 0; nPage = 0; nPos = 0; nTrackPos = 0;
}
}
internal enum SBInfoMask : uint
{
SIF_RANGE = 0x1,
SIF_PAGE = 0x2,
SIF_POS = 0x4,
SIF_DISABLENOSCROLL = 0x8,
SIF_TRACKPOS = 0x10,
SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS),
SIF_POSRANGE = (SIF_RANGE | SIF_POS | SIF_PAGE)
}
public enum SBParam : int
{
SB_HORZ = 0x0,
SB_VERT = 0x1,
SB_CTL = 0x2,
SB_BOTH = 0x3
}
您可以创建 CustomListView
和 Re_Define
EnsureVisible
方法
class DataFlowFilterListView : ListView
{
/// <summary>
/// margin from the selected column to the border of listview.
/// </summary>
const int MARGIN = 20;
/// <summary>
/// native windows message to scroll the listview.
/// </summary>
const Int32 LVM_FIRST = 0x1000;
const Int32 LVM_SCROLL = LVM_FIRST + 20;
[DllImport("user32")]
static extern IntPtr SendMessage(IntPtr Handle, Int32 msg, IntPtr wParam,
IntPtr lParam);
private void ScrollHorizontal(int pixelsToScroll)
{
SendMessage(this.Handle, LVM_SCROLL, (IntPtr)pixelsToScroll,
IntPtr.Zero);
}
/// <summary>
/// Ensure visible of a ListViewItem and SubItem Index.
///
///
/// </summary>
/// <param name="item"></param>
/// <param name="subItemIndex"></param>
public void EnsureVisible(ListViewItem item, int subItemIndex)
{
if (item == null || subItemIndex > item.SubItems.Count - 1)
{
throw new ArgumentException();
}
// scroll to the item row.
item.EnsureVisible();
ScrollToRectangle(item.SubItems[subItemIndex].Bounds.Width);
}
/// <summary>
/// Scrolls the listview.
/// </summary>
/// <param name="bounds"></param>
private void ScrollToRectangle(int width)
{
this.ScrollHorizontal(width);
}
}
现在您可以使用 DataFlowFilterListView
代替 ListView
。
每次向列表中添加一列时执行以下代码
listView1.EnsureVisible(listView1.Items[0], 0);
我的解决方案是使用 user32 函数和 SendMessage()
。
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
private const int WM_HSCROLL = 0x114; //for horizontal scroll
private const int WPARAM = 7; //to scroll to end
实施:
string rowstr = "Code,"
for (var i = 0; i < 10; i++)
{
ColumnHeader head = new ColumnHeader();
head.Text = i.toString();
listView1.Columns.Add(head);
listView1.Columns[i].Width = 65;
rowstr += "Test"+",";
string[] row = rowstr.Split(",");
var listViewItem = new ListViewItem(row);
listViewItem.Font = new Font("Consolas", 10f);
listView1.Items.Insert(0, listViewItem);
SendMessage(listView1.Handle, WM_HSCROLL, WPARAM, 0);
}