使用WPF ui的多线程

时间:2018-07-09 19:15:46

标签: c# wpf multithreading user-interface

我不确定这是否可行,但是搜索时找不到任何东西。

我在WPF中有一个可视化的计划,可以加载并显示约会。问题在于,加载所有视觉效果会花费一些时间,并且程序在这段时间内变得没有响应。

是否可以在单独的线程中加载约会视觉效果并修改计划网格,同时让主线程保持打开状态以进行其他操作?还是可能将日程表网格永久保留在第二个STA线程中,以便它可以做自己的事情而不会干扰窗口?

编辑:

目前我所拥有的:

    private static void FillWeek()
    { 
        BindingOperations.EnableCollectionSynchronization(ObservableAppointments, _lockobject);
        for (int i = 1; i < 6; i++)
        {
            FillDay(Date.GetFirstDayOfWeek().AddDays(i).Date);
        }
    }
    private static ObservableCollection<AppointmentUIElement> ObservableAppointments = new ObservableCollection<AppointmentUIElement>();
    private static object _lockobject = new object();
    public static async Task FillDay(DateTime date)
    {
        ClearDay(date);
        Appointment[] Appointments;
        var date2 = date.AddDays(1);
        using (var db = new DataBaseEntities())
        {
            Appointments = (from Appointment a in db.GetDailyAppointments(2, date.Date) select a).ToArray();
        }
        await Task.Run(()=>
        {
            foreach (Appointment a in Appointments)
            {
                var b = new AppointmentUIElement(a, Grid);
                ObservableAppointments.Add(b);
            }
        });      
    }
    private static void ObservableAppointments_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
        {
            var a = e.NewItems[0] as AppointmentUIElement;
            a.Display();
        }
    }
    private static void ClearDay(DateTime date)
    {
        var Queue = new Queue<AppointmentUIElement>(Grid.Children.OfType<AppointmentUIElement>().Where(a => a.Appointment.Start.DayOfWeek == date.DayOfWeek));
        while (Queue.Count > 0)
        {
            var x = Queue.Dequeue();
            Grid.Children.Remove(x);
            ObservableAppointments.Remove(x);
        }
        var Queue2 = new Queue<GridCell>(Grid.Children.OfType<GridCell>().Where(g => g.Date.Date == date));
        while (Queue2.Count > 0)
        {
            Queue2.Dequeue().AppointmentUIElements.RemoveAll(a => true);
        }
    }

AppointmentUIElement源自Border

2 个答案:

答案 0 :(得分:2)

现在所有这一切的挑战在于,视觉元素和绑定的ObservableCollections只能由UI线程修改,而无需进行其他工作。不是集合的绑定属性不需要此。

因此,可以说您具有从UI绑定到其中包含约会数据的ObservableCollection的“约会视觉效果”。您可以做的是使您的“搜索约会”功能异步,并如下所示注册您的集合以进行线程同步。为了简洁起见,我将忽略与INotifyPropertyChange相关的任何内容。

  public ObservableCollection<Appointments> Appointments = new ObservableCollection<Appointments>();
  private static object _lockobject = new object();
  public async Task Load()
  {
        await Task.Run(() => { /*load stuff into the Appointments collection here */ });
        ///possibly more code to execute after the task is complete.
  }

  //in constructor or similar, this is REQUIRED because the collection is bound and must be synchronized for mulththreading operations
  BindingOperations.EnableCollectionSynchronization(YourCollection, _lockobject);

还有很多不建议使用的修改UI线程创建的可视元素的方法。

 this.Dispatcher.Invoke(() => {/* do stuff with ui elements or bound things*/});

发生的要点是您从UI线程调用load,并且当它遇到“ await task.run”时,它将在单独的线程中处理任务的内容,同时允许UI线程继续响应用户。一旦任务完成,它将在后台返回ui线程以执行load方法中执行的其他操作。

如果忘记了EnableCollectionSynchronization部分,则任何在task.run中添加或删除项目的尝试都将引发错误,抱怨您无法在与创建该集合相同的线程中更改集合的内容(几乎与尝试直接修改ui元素相同的错误)。

评论回复->您在做什么的问题

  AppointmentUIElement(a,Grid)

您在这里真正应该做的是将网格放入自定义控件中,该控件具有定义的绑定项目模板,该模板绑定到ObservableAppointments中的项目,该项目实际上应该是约会数据,而不是UI元素。所有这些都应该通过上下文的ViewModels发生。仅当只有一个线程来管理一切时,您的操作方式才会起作用,一旦涉及另一个线程,所有这些都将落在您身上。

答案 1 :(得分:0)

  

是否可以在单独的线程中加载约会视觉效果并修改计划网格,同时让主线程保持打开状态以进行其他操作?还是可能将日程表网格永久保留在第二个STA线程中,以便它可以做自己的事情而不会干扰窗口?

您可以在一个专用的调度程序线程上运行的单独窗口中加载并显示计划网格。有关如何在单独的线程中启动WPF窗口的示例,请参阅this博客文章。

请记住,尽管如此,在新线程上创建的元素将无法与在主线程上创建的元素进行交互。因此,您不能简单地将计划加载到另一个线程上,然后再将其带回主线程。视觉元素只能从最初在其上创建的线程访问。