好的,所以你从服务器加载实体,但你仍然需要从ViewModel 访问集合。让我们将此功能移动到服务中。该服务允许您异步加载实体 ID 列表,或加载特定实体详细信息:
using AsyncLoadingCollection.DTO;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
public class PeopleService
{
private List<PersonDTO> _people;
public PeopleService()
{
InitializePeople();
}
public async Task<IList<int>> GetIds()
{
// simulate async loading delay
await Task.Delay(1000);
var result = _people.Select(p => p.Id).ToList();
return result;
}
public async Task<PersonDTO> GetPersonDetail(int id)
{
// simulate async loading delay
await Task.Delay(3000);
var person = _people.Where(p => p.Id == id).First();
return person;
}
private void InitializePeople()
{
// poor person's database
_people = new List<PersonDTO>();
_people.Add(new PersonDTO { Name = "Homer", Age = 39, Id = 1 });
_people.Add(new PersonDTO { Name = "Marge", Age = 37, Id = 2 });
_people.Add(new PersonDTO { Name = "Bart", Age = 12, Id = 3 });
_people.Add(new PersonDTO { Name = "Lisa", Age = 10, Id = 4 });
}
}
GetPersonDetail 方法返回一个DTO:
public class PersonDTO
{
public string Name { get; set; }
public int Age { get; set; }
public int Id { get; set; }
}
DTO 可以转换为您的ViewModel 所需的对象(我使用 Prism 作为 MVVM 框架):
using Prism.Mvvm;
public class Person : BindableBase
{
private string _name;
public string Name
{
get { return _name; }
set { SetProperty(ref _name, value); }
}
private int _age;
public int Age
{
get { return _age; }
set { SetProperty(ref _age, value); }
}
private int _id;
public int Id
{
get { return _id; }
set { SetProperty(ref _id, value); }
}
private bool _isLoaded;
public bool IsLoaded
{
get { return _isLoaded; }
set { SetProperty(ref _isLoaded, value); }
}
}
您可以像这样将 DTO 对象转换为模型:
using DTO;
using Model;
// we might use AutoMapper instead
public static class PersonConverter
{
public static Person ToModel(this PersonDTO dto)
{
Person result = new Person
{
Id = dto.Id,
Name = dto.Name,
Age = dto.Age
};
return result;
}
}
这就是我们在ViewModel 中定义命令(使用物品检索服务)的方式:
using Helpers;
using Model;
using Prism.Commands;
using Prism.Mvvm;
using Services;
using System.Collections.ObjectModel;
using System.Linq;
public class MainWindowViewModel : BindableBase
{
#region Fields
private PeopleService _peopleService;
#endregion // Fields
#region Constructors
public MainWindowViewModel()
{
// we might use dependency injection instead
_peopleService = new PeopleService();
People = new ObservableCollection<Person>();
LoadListCommand = new DelegateCommand(LoadList);
LoadPersonDetailsCommand = new DelegateCommand(LoadPersonDetails, CanLoadPersonDetails)
.ObservesProperty(() => CurrentPerson)
.ObservesProperty(() => IsBusy);
}
#endregion // Constructors
#region Properties
private string _title = "Prism Unity Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
private Person _currentPerson;
public Person CurrentPerson
{
get { return _currentPerson; }
set { SetProperty(ref _currentPerson, value); }
}
private bool _isBusy;
public bool IsBusy
{
get { return _isBusy; }
set { SetProperty(ref _isBusy, value); }
}
public ObservableCollection<Person> People { get; private set; }
#endregion // Properties
#region Commands
public DelegateCommand LoadListCommand { get; private set; }
private async void LoadList()
{
// reset the collection
People.Clear();
var ids = await _peopleService.GetIds();
var peopleListStub = ids.Select(i => new Person { Id = i, IsLoaded = false, Name = "No details" });
People.AddRange(peopleListStub);
}
public DelegateCommand LoadPersonDetailsCommand { get; private set; }
private bool CanLoadPersonDetails()
{
return ((CurrentPerson != null) && !IsBusy);
}
private async void LoadPersonDetails()
{
IsBusy = true;
var personDTO = await _peopleService.GetPersonDetail(CurrentPerson.Id);
var updatedPerson = personDTO.ToModel();
updatedPerson.IsLoaded = true;
var oldPersonIndex = People.IndexOf(CurrentPerson);
People.RemoveAt(oldPersonIndex);
People.Insert(oldPersonIndex, updatedPerson);
CurrentPerson = updatedPerson;
IsBusy = false;
}
#endregion // Commands
}
最后,View 可能就这么简单:
<Window x:Class="AsyncLoadingCollection.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
Title="{Binding Title}"
Width="525"
Height="350"
prism:ViewModelLocator.AutoWireViewModel="True">
<StackPanel>
<!--<ContentControl prism:RegionManager.RegionName="ContentRegion" />-->
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<Button Width="100"
Margin="10"
Command="{Binding LoadListCommand}"
Content="Load List" />
<Button Width="100"
Margin="10"
Command="{Binding LoadPersonDetailsCommand}"
Content="Load Details" />
</StackPanel>
<TextBlock Text="{Binding CurrentPerson.Name}" />
<DataGrid CanUserAddRows="False"
CanUserDeleteRows="False"
ItemsSource="{Binding People}"
SelectedItem="{Binding CurrentPerson,
Mode=TwoWay}">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<!--<Setter Property="Item" Value="{Binding Item}" />-->
<Style.Triggers>
<DataTrigger Binding="{Binding IsLoaded}" Value="False">
<DataTrigger.Setters>
<Setter Property="Background" Value="DarkGray" />
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
</StackPanel>
</Window>