如何在向 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)会很酷,例如

http://www.sponlytix.com/Images/Others/Loading.gif

这里的实际问题是您添加的每个项目都会发出更改通知。

您需要将 WrapPAnel.Children 数据绑定到实现 AddRangeINotifyCollectionChanged 实例。目标应该是 属性 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>