关于c#:WPF DataGrid绑定到ItemsSource项目属性 |
您所在的位置:网站首页 › wpfdatagrid异步绑定 › 关于c#:WPF DataGrid绑定到ItemsSource项目属性 |
我有实现IList的集合,该集合从服务器异步加载数据。 当索引访问该集合时,它将返回一个存根对象,并在后台启动数据请求。 请求完成后,集合将刷新内部状态并调用PropertyChanged事件。 现在,集合返回的项目如下所示: 12345678public class VirtualCollectionItem { // actual entity public object Item { get; } // some metadata properties public bool IsLoading { get; } }问题是我无法意识到如何将此类集合绑定到DataGrid。 我想以某种方式将DataGrid.ItemsSource设置为VirtualCollectionItem的集合,使DataGrid显示实际项目(也在SelectedItem中),并保留使用元数据的可能性(即使用IsLoading可视化数据加载)。 我试图在DataGrid.RowStyle中设置DataGridRow.Item绑定,但是没有用。 123456789101112另一个选择是将VirtualCollectionItem属性展平为VirtualCollection本身: 1234567891011class VirtualCollection { // ... // wrapper around VirtualCollectionItem public IList Items { get; } public IList IsLoadingItems { get; } // ... }并在DataGrid中使用这些属性,但是我还没有意识到如何使它起作用。 好的,因此您可以从服务器加载实体,但是仍然需要从ViewModel访问集合。让我们将此功能移至服务。该服务允许您异步加载实体ID列表,或加载特定的实体详细信息: 1234567891011121314151617181920212223242526272829303132333435363738using AsyncLoadingCollection.DTO; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; public class PeopleService { private List _people; public PeopleService() { InitializePeople(); } public async Task GetIds() { // simulate async loading delay await Task.Delay(1000); var result = _people.Select(p => p.Id).ToList(); return result; } public async Task 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(); _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: 1234567public class PersonDTO { public string Name { get; set; } public int Age { get; set; } public int Id { get; set; } }可以将DTO转换为您的ViewModel所需的对象(并且我将Prism用作MVVM框架): 1234567891011121314151617181920212223242526272829303132using 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对象转换为Model: 1234567891011121314151617using 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中定义命令(使用项目检索服务)的方式: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091using 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(); 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 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可能像这样简单: 1234567891011121314151617181920212223242526272829303132333435363738394041我决定用ViewModel包装DataGrid: 1234567891011121314151617181920public class DataGridAsyncViewModel : Notifier { public VirtualCollection ItemsProvider { get; } private VirtualCollectionItem _selectedGridItem; public VirtualCollectionItem SelectedGridItem { get { return _selectedGridItem; } set { Set(ref _selectedGridItem, value); } } public object SelectedItem => SelectedGridItem?.IsLoading == false ? SelectedGridItem?.Item : null; public DataGridAsyncViewModel([NotNull] VirtualCollection itemsProvider) { if (itemsProvider == null) throw new ArgumentNullException(nameof(itemsProvider)); ItemsProvider = itemsProvider; } }并将其绑定到DataGrid: 12345678910111213141516您可以将VirtualCollection直接绑定到DataGrid.ItemsSource属性。然后也绑定SelectedItem属性: 1然后是ViewModel: 123456789101112131415161718192021222324252627282930313233public class MyViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private VirtualCollection _myVirtualCollectionList; public VirtualCollection MyVirtualCollectionList { get { return _myVirtualCollectionList; } set { _myVirtualCollectionList = value; OnPropertyChanged(); } } private VirtualCollectionItem _selectedItem; public VirtualCollectionItem SelectedItem { get { return _selectedItem; } set { _selectedItem = value; OnPropertyChanged(); } } }您必须在加载列表后触发OnPropertyChanged事件,并且必须从MyViewModel对象执行此操作(我认为您不这样做)!您也可以使用ObservableCollection(可以扩展它)。然后,您不需要OnPropertyChange事件。从集合中添加/删除项目会自动通知UI。 SelectedItem与列表无关。在数据模板的一行中,您将使用{Binding IsLoading}和{Binding Item.SomeProperty}。 相关讨论 在我的情况下,DataGrids Items / SelectedItem(s)必须是从服务器加载的实际实体,因此我可以使用它而无需重写大量现有代码库。 我还在需要时调用PropertyChanged和CollectionChanged事件。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |