简体   繁体   English

UI响应性并在WPF中使用“ SelectedItem” ListView / ListBox

[英]UI Responsiveness and working with “SelectedItem” ListView/ListBox in WPF

I have a handful of situations where I am trying to design my WPF .NET 4.0 application to handle a change of a SelectedItem in a ListBox/ListView. 在少数情况下,我试图设计WPF .NET 4.0应用程序以处理ListBox / ListView中SelectedItem的更改。

Essentially I want to query other data to fill an ObservableCollection/DataGrid based on what a user has selected. 本质上,我想根据用户选择的内容查询其他数据以填充ObservableCollection / DataGrid。 My problem is that by having everything on the same thread, the responsiveness suffers inasmuch as the item that is clicked only shows as being Selected once any code started in the "SelectedItem" Setter has finished and this "wait" doesn't respond how I want it to. 我的问题是,通过使所有内容都在同一线程上,响应性受到损害,因为在“ SelectedItem”设置程序中启动的任何代码完成后,被单击的项目仅显示为“已选择”,并且此“等待”不响应我想要它。

The data being queried normally fills an ObservableCollection via LINQ from the same database which is why everything is using the same UI thread. 通常,要查询的数据通过LINQ从同一数据库填充一个ObservableCollection,这就是为什么所有内容都使用同一UI线程的原因。

Ideally I want: 理想情况下,我想要:

  1. A user to click on an item in a list which is selected immediately without the feeling of the application hanging. 用户单击列表中的一项即可立即选择,而不会感到应用程序挂起。
  2. An "IsBusy" property to be set to true (used in bindings to change the enabled status of certain controls). 要设置为true的“ IsBusy”属性(用于绑定中,以更改某些控件的启用状态)。
  3. Start querying other data and cnce the that is finished, a (eg) DataGrid should be populated and the IsBusy is returned to false. 开始查询其他数据,然后完成查询,应填充(例如)DataGrid,并将IsBusy返回为false。

What is the best way to achieve this with secondary data loading "in the background"? 实现“后台”辅助数据加载的最佳方法是什么? I've used BackgroundWorker in the past in WinForms but am eager to learn more about the Dispatcher which I think is the right direction. 我过去在WinForms中使用过BackgroundWorker,但是渴望了解有关Dispatcher的更多信息,我认为这是正确的方向。

My SelectedItem property is as straightforward as this: 我的SelectedItem属性很简单:

    public Employee SelectedEmployee
    {
        get
        {
            return mvSelectedEmployee;
        }

        set
        {
            mvSelectedEmployee = value;
            RaisePropertyChanged();

            IsBusy = true;
            QueryMyEmployeesAddressesAndOtherData(); //which takes sometimes 2 seconds
            IsBusy = false;
        }
    }

I want the user to know something is happening but smoothly despite there being a slight delay. 我希望用户知道有什么事情在发生,但尽管有些延迟,但还是很流畅。

Thank you. 谢谢。

Using async/await is the best way to achieve this. 使用异步/等待是实现此目的的最佳方法。 One issue for your particular scenario is that property setters cannot be marked async . 您的特定情况的一个问题是,属性设置器不能标记为async You can however fire off an async void method from the setter. 但是,您可以从设置器中触发async void方法。 Here's a simple example that will work without you having to change anything else in your code base: 这是一个简单的示例,无需您在代码库中进行任何其他更改即可使用:

public Employee SelectedEmployee
{
    get
    {
        return mvSelectedEmployee;
    }

    set
    {
        mvSelectedEmployee = value;
        RaisePropertyChanged();
        UpdateSelectedEmployeeAsync();
    }
}

private async void UpdateSelectedEmployeeAsync()
{
    IsBusy = true;
    await Task.Run(() => QueryMyEmployeesAddressesAndOtherData());
    IsBusy = false;
}

But probably what you'll really want to do is make QueryMyEmployeesAddressesAndOtherData asynchronous "all the way down". 但是,您可能真正想做的是使QueryMyEmployeesAddressesAndOtherData异步到“一路下行”。 Task.Run is really more for running CPU bound operations in the background, but it sounds like yours is probably mostly IO bound (waiting on some queries to execute). Task.Run实际上更适合在后台运行受CPU约束的操作,但是听起来您的任务可能主要是受IO约束的(等待一些查询执行)。 Whatever persistence framework you are using very likely has async support (if it doesn't, consider updating to one that does). 无论您使用哪种持久性框架,都极有可能支持异步(如果不支持,请考虑更新为可以支持的异步框架)。 Now you might end up with something like this: 现在,您可能会得到如下结果:

private async void UpdateSelectedEmployeeAsync()
{
    IsBusy = true;
    await QueryMyEmployeesAddressesAndOtherDataAsync();
    IsBusy = false;
}

private async Task QueryMyEmployeesAddressesAndOtherDataAsync()
{
    using (var ctx = new MyContext())
    {
        var queryResults = await ctx.EmployeeData.Where(t=>t.EmployeeId == SelectedEmployee.Id).ToArrayAsync();
        SelectedEmployeeDatas.Clear();
        foreach (var data in queryResults)
        {
            SelectedEmployeeDatas.Add(data);
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM