如何在向 UI 添加内容时阻止 UI 被锁定
How to stop UI being locked when adding things to the UI
我有 250 个可视对象需要添加到 WrapPanel
。当我这样做时 UI 锁定几秒钟,这不是我想要的。有办法阻止吗?
private void ButtonClick()
{
Foreach(Visual item in ListOfVisuals)
{
WrapPAnel.Children.Add(item);
}
}
据我所知,我无法创建一个新的 Task.Run() => ButtonClick
来执行此操作,因为任务将无法访问 UI。
试试这个:
this.Dispatcher.BeginInvoke(new Action(delegate
{
foreach(Visual item in ListOfVisuals)
{
WrapPAnel.Children.Add(item);
}
}), DispatcherPriority.Background);
更有可能出现问题,因为每次 Add
调用都会导致重新布局和重绘。
在winforms
中,解决方案是在添加多个控件时使用SuspendLayout
/ResumeLayout
,并且通常启用双缓冲以避免闪烁。
你可以搜索它是如何在 wpf 中完成的(例如,here 是什么)。但我建议您简单地隐藏 (Visibility.Collapsed
) 容器,直到您完成添加 250 个控件。显示一些 进度指示器 (矢量视觉动画或简单的 gif)会很酷,例如
这里的实际问题是您添加的每个项目都会发出更改通知。
您需要将 WrapPAnel.Children
数据绑定到实现 AddRange
的 INotifyCollectionChanged
实例。目标应该是 属性 changed 事件对整个集合只引发一次。
The obvious answer is, of course, just write a method which iterates
over the input collection and calls Add for each. But this really
isn’t the answer, because the question really isn’t about AddRange –
the question is really about raising a single CollectionChanged event
when adding multiple items
你是如何实现的?
public void AddRange(IEnumerable<T> dataToAdd)
{
this.CheckReentrancy();
//
// We need the starting index later
//
int startingIndex = this.Count;
//
// Add the items directly to the inner collection
//
foreach (var data in dataToAdd)
{
this.Items.Add(data);
}
//
// Now raise the changed events
//
this.OnPropertyChanged("Count");
this.OnPropertyChanged("Item[]");
//
// We have to change our input of new items into an IList since that is what the
// event args require.
//
var changedItems = new List<T>(dataToAdd);
this.OnCollectionChanged(changedItems, startingIndex);
}
来源: http://blogs.msdn.com/b/nathannesbit/archive/2009/04/20/addrange-and-observablecollection.aspx
但是等等,你不能那样绑定 Wrappanel
!您将需要使用 ItemsControl
,其 ItemsPanelTemplate
设置为 WrapPanel
。请参阅此示例 http://tech.pro/tutorial/830/wpf-tutorial-using-an-itemspanel
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<Image Source="Images\Aquarium.jpg" Width="100"/>
<Image Source="Images\Ascent.jpg" Width="50"/>
<Image Source="Images\Autumn.jpg" Width="200"/>
<Image Source="Images\Crystal.jpg" Width="75"/>
<Image Source="Images\DaVinci.jpg" Width="125"/>
<Image Source="Images\Follow.jpg" Width="100"/>
<Image Source="Images\Friend.jpg" Width="50"/>
<Image Source="Images\Home.jpg" Width="150"/>
<Image Source="Images\Moon flower.jpg" Width="100"/>
</ItemsControl>
我有 250 个可视对象需要添加到 WrapPanel
。当我这样做时 UI 锁定几秒钟,这不是我想要的。有办法阻止吗?
private void ButtonClick()
{
Foreach(Visual item in ListOfVisuals)
{
WrapPAnel.Children.Add(item);
}
}
据我所知,我无法创建一个新的 Task.Run() => ButtonClick
来执行此操作,因为任务将无法访问 UI。
试试这个:
this.Dispatcher.BeginInvoke(new Action(delegate
{
foreach(Visual item in ListOfVisuals)
{
WrapPAnel.Children.Add(item);
}
}), DispatcherPriority.Background);
更有可能出现问题,因为每次 Add
调用都会导致重新布局和重绘。
在winforms
中,解决方案是在添加多个控件时使用SuspendLayout
/ResumeLayout
,并且通常启用双缓冲以避免闪烁。
你可以搜索它是如何在 wpf 中完成的(例如,here 是什么)。但我建议您简单地隐藏 (Visibility.Collapsed
) 容器,直到您完成添加 250 个控件。显示一些 进度指示器 (矢量视觉动画或简单的 gif)会很酷,例如
这里的实际问题是您添加的每个项目都会发出更改通知。
您需要将 WrapPAnel.Children
数据绑定到实现 AddRange
的 INotifyCollectionChanged
实例。目标应该是 属性 changed 事件对整个集合只引发一次。
The obvious answer is, of course, just write a method which iterates over the input collection and calls Add for each. But this really isn’t the answer, because the question really isn’t about AddRange – the question is really about raising a single CollectionChanged event when adding multiple items
你是如何实现的?
public void AddRange(IEnumerable<T> dataToAdd)
{
this.CheckReentrancy();
//
// We need the starting index later
//
int startingIndex = this.Count;
//
// Add the items directly to the inner collection
//
foreach (var data in dataToAdd)
{
this.Items.Add(data);
}
//
// Now raise the changed events
//
this.OnPropertyChanged("Count");
this.OnPropertyChanged("Item[]");
//
// We have to change our input of new items into an IList since that is what the
// event args require.
//
var changedItems = new List<T>(dataToAdd);
this.OnCollectionChanged(changedItems, startingIndex);
}
来源: http://blogs.msdn.com/b/nathannesbit/archive/2009/04/20/addrange-and-observablecollection.aspx
但是等等,你不能那样绑定 Wrappanel
!您将需要使用 ItemsControl
,其 ItemsPanelTemplate
设置为 WrapPanel
。请参阅此示例 http://tech.pro/tutorial/830/wpf-tutorial-using-an-itemspanel
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<Image Source="Images\Aquarium.jpg" Width="100"/>
<Image Source="Images\Ascent.jpg" Width="50"/>
<Image Source="Images\Autumn.jpg" Width="200"/>
<Image Source="Images\Crystal.jpg" Width="75"/>
<Image Source="Images\DaVinci.jpg" Width="125"/>
<Image Source="Images\Follow.jpg" Width="100"/>
<Image Source="Images\Friend.jpg" Width="50"/>
<Image Source="Images\Home.jpg" Width="150"/>
<Image Source="Images\Moon flower.jpg" Width="100"/>
</ItemsControl>