我有以下代码,它基本上从数据库获取值并填充listview。
using (IDataReader reader = cmd.ExecuteReader())
{
lvwMyList.Items.Clear();
while (reader.Read())
{
ListViewItem lvi = lvwMyList.Items.Add(reader["Value1"].ToString());
lvi.SubItems.Add(reader["Value2"].ToString());
}
}
我遇到的问题是,它会以较短的间隔(每秒)重复执行,导致列表视图中的项目不断消失并重新出现。有没有办法阻止列表视图刷新,直到完成更新?如下所示:
using (IDataReader reader = cmd.ExecuteReader())
{
lvwMyList.Items.Freeze(); // Stop the listview updating
lvwMyList.Items.Clear();
while (reader.Read())
{
ListViewItem lvi = lvwMyList.Items.Add(reader["Value1"].ToString());
lvi.SubItems.Add(reader["Value2"].ToString());
}
lvwMyList.Items.UnFreeze(); // Refresh the listview
}
答案 0 :(得分:9)
像这样:
try
{
lvwMyList.BeginUpdate();
//bla bla bla
}
finally
{
lvwMyList.EndUpdate();
}
如果您想在填写前清除列表,请务必在 lvwMyList.Items.Clear()
之后调用BeginUpdate
。
答案 1 :(得分:1)
这是我第一次在StackOverflow上发帖,所以请原谅下面的凌乱代码格式。
为了防止在更新ListView时锁定表单,您可以使用我编写的以下方法来解决此问题。
注意:如果您希望使用超过20,000个项目填充ListView,则不应使用此方法。如果需要向ListView添加超过20k的项目,请考虑在虚拟模式下运行ListView。
public static async void PopulateListView<T>(ListView listView, Func<T, ListViewItem> func,
IEnumerable<T> objects, IProgress<int> progress) where T : class, new()
{
if (listView != null && listView.IsHandleCreated)
{
var conQue = new ConcurrentQueue<ListViewItem>();
// Clear the list view and refresh it
if (listView.InvokeRequired)
{
listView.BeginInvoke(new MethodInvoker(() =>
{
listView.BeginUpdate();
listView.Items.Clear();
listView.Refresh();
listView.EndUpdate();
}));
}
else
{
listView.BeginUpdate();
listView.Items.Clear();
listView.Refresh();
listView.EndUpdate();
}
// Loop over the objects and call the function to generate the list view items
if (objects != null)
{
int objTotalCount = objects.Count();
foreach (T obj in objects)
{
await Task.Run(() =>
{
ListViewItem item = func.Invoke(obj);
if (item != null)
conQue.Enqueue(item);
if (progress != null)
{
double dProgress = ((double)conQue.Count / objTotalCount) * 100.0;
if(dProgress > 0)
progress.Report(dProgress > int.MaxValue ? int.MaxValue : (int)dProgress);
}
});
}
// Perform a mass-add of all the list view items we created
if (listView.InvokeRequired)
{
listView.BeginInvoke(new MethodInvoker(() =>
{
listView.BeginUpdate();
listView.Items.AddRange(conQue.ToArray());
listView.Sort();
listView.EndUpdate();
}));
}
else
{
listView.BeginUpdate();
listView.Items.AddRange(conQue.ToArray());
listView.Sort();
listView.EndUpdate();
}
}
}
if (progress != null)
progress.Report(100);
}
您不必提供IProgress对象,只需使用null,该方法也可以正常工作。
以下是该方法的示例用法。
首先,定义一个包含ListViewItem数据的类。
public class TestListViewItemClass
{
public int TestInt { get; set; }
public string TestString { get; set; }
public DateTime TestDateTime { get; set; }
public TimeSpan TestTimeSpan { get; set; }
public decimal TestDecimal { get; set; }
}
然后,创建一个返回数据项的方法。只要它返回类类型的IEnumerable,此方法就可以查询数据库,调用Web服务API等等。
public IEnumerable<TestListViewItemClass> GetItems()
{
for (int x = 0; x < 15000; x++)
{
yield return new TestListViewItemClass()
{
TestDateTime = DateTime.Now,
TestTimeSpan = TimeSpan.FromDays(x),
TestInt = new Random(DateTime.Now.Millisecond).Next(),
TestDecimal = (decimal)x + new Random(DateTime.Now.Millisecond).Next(),
TestString = "Test string " + x,
};
}
}
最后,在ListView所在的表单上,您可以填充ListView。出于演示目的,我使用表单的Load事件来填充ListView。很可能,你会希望在表格的其他地方这样做。
我已经包含了从我的类TestListViewItemClass的实例生成ListViewItem的函数。在生产场景中,您可能希望在其他地方定义该函数。
private async void TestListViewForm_Load(object sender, EventArgs e)
{
var function = new Func<TestListViewItemClass, ListViewItem>((TestListViewItemClass x) =>
{
var item = new ListViewItem();
if (x != null)
{
item.Text = x.TestString;
item.SubItems.Add(x.TestDecimal.ToString("F4"));
item.SubItems.Add(x.TestDateTime.ToString("G"));
item.SubItems.Add(x.TestTimeSpan.ToString());
item.SubItems.Add(x.TestInt.ToString());
item.Tag = x;
return item;
}
return null;
});
PopulateListView<TestListViewItemClass>(this.listView1, function, GetItems(), progress);
}
在上面的例子中,我在表单的构造函数中创建了一个IProgress对象,如下所示:
progress = new Progress<int>(value =>
{
toolStripProgressBar1.Visible = true;
if (value >= 100)
{
toolStripProgressBar1.Visible = false;
toolStripProgressBar1.Value = 0;
}
else if (value > 0)
{
toolStripProgressBar1.Value = value;
}
});
我已经在ListView中使用这种方法多次填充ListView,我们在ListView中填充了12,000个项目,而且速度非常快。最重要的是,在触摸ListView进行更新之前,您需要从数据库中完全构建对象。
希望这很有帮助。
我在下面包含了该方法的异步版本,该版本调用了此帖子顶部显示的main方法。
public static Task PopulateListViewAsync<T>(ListView listView, Func<T, ListViewItem> func,
IEnumerable<T> objects, IProgress<int> progress) where T : class, new()
{
return Task.Run(() => PopulateListView<T>(listView, func, objects, progress));
}
答案 2 :(得分:0)
您还可以尝试在更新期间将visible或enabled属性设置为false,并查看您是否更喜欢这些结果。 当然,在更新完成后将值重置为true。
另一种方法是创建一个覆盖列表框的面板。将其左,右,高度和宽度属性设置为与列表框相同,并在更新期间将其visible属性设置为true,在完成后将其设置为false。