我一直在构建一个应用程序,它使用LoadOperation的实体返回一个IEnumerable,它成为我的View Model中CollectionViewSource的源代码。我现在发现这种方法的潜在缺陷,当我在Silverlight客户端中添加实体时,我看不到这些实体,除非我提交新实体,然后重新加载,或维护我绑定的单独的项目集合。
我真正看到的是我的选择:
如果有人对每个人的利弊有提示或想法,我将不胜感激。特别是,我想知道,如果有一个或另一个有利于表现和/或编程的好处吗?
答案 0 :(得分:1)
我一次采用这种方法。首先,我将展示一个参考点,然后我将重点介绍支持每种方法所需的不同变化。
我的演示的基础是一个经过身份验证的单一域服务,它返回一个Resource实体。我将公开4个命令(保存,撤消,添加和删除),加上一个Collection,以及一个Property来保存SelectedResource。
2个不同的类实现此接口(1用于混合,1用于生产)。我将在这里讨论唯一的制作。注意GetMyResources函数中的动作(lo.Entities):
public class WorkProvider
{
static WorkContext workContext;
public WorkProvider()
{
if (workContext == null)
workContext = new WorkContext();
}
public void AddResource(Resource resource)
{
workContext.Resources.Add(resource);
}
public void DelResource(Resource resource)
{
workContext.Resources.Remove(resource);
}
public void UndoChanges()
{
workContext.RejectChanges();
}
public void SaveChanges(Action action)
{
workContext.SubmitChanges(so =>
{
if (so.HasError)
// Handle Error
throw so.Error;
else
action();
}, null);
}
public void GetMyResources(Action<IEnumerable<Resource>> action)
{
var query = workContext.GetResourcesQuery()
.Where(r => r.UserName == WebContext.Current.User.Name);
workContext.Load(query, LoadBehavior.MergeIntoCurrent, lo =>
{
if (lo.HasError)
// Handle Error
throw lo.Error;
else
action(lo.Entities);
}, null);
}
}
在ViewModel中,我有以下实现:
public class HomeViewModel : ViewModelBase
{
WorkProvider workProvider;
public HomeViewModel()
{
workProvider = new WorkProvider();
}
// _Source is required when returning IEnumerable<T>
ObservableCollection<Resource> _Source;
public CollectionViewSource Resources { get; private set; }
void setupCollections()
{
Resources = new CollectionViewSource();
using (Resources.DeferRefresh())
{
_Source = new ObservableCollection<Resource>();
Resources.Source = _Source;
Resources.GroupDescriptions.Add(new PropertyGroupDescription("Title"));
Resources.SortDescriptions.Add(new SortDescription("Title", ListSortDirection.Ascending));
Resources.SortDescriptions.Add(new SortDescription("Rate", ListSortDirection.Ascending));
}
}
void loadMyResources()
{
workProvider.GetMyResources(results =>
{
using (Resources.DeferRefresh())
{
// This is required when returning IEnumerable<T>
_Source.Clear();
foreach (var result in results)
{
if (!_Source.Contains(result))
_Source.Add(result);
}
}
});
}
Resource _SelectedResource;
public Resource SelectedResource
{
get { return _SelectedResource; }
set
{
if (_SelectedResource != value)
{
_SelectedResource = value;
RaisePropertyChanged("SelectedResource");
}
}
}
public RelayCommand CmdSave { get; private set; }
public RelayCommand CmdUndo { get; private set; }
public RelayCommand CmdAdd { get; private set; }
public RelayCommand CmdDelete { get; private set; }
void setupCommands()
{
CmdSave = new RelayCommand(() =>
{
workProvider.SaveChanges(() =>
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
System.Windows.MessageBox.Show("Saved");
});
});
});
CmdUndo = new RelayCommand(() =>
{
workProvider.UndoChanges();
// This is required when returning IEnumerable<T>
loadMyResources();
});
CmdAdd = new RelayCommand(() =>
{
Resource newResource = new Resource()
{
ResourceID = Guid.NewGuid(),
Rate = 125,
Title = "Staff",
UserName = "jsmith"
};
// This is required when returning IEnumerable<T>
_Source.Add(newResource);
workProvider.AddResource(newResource);
});
CmdDelete = new RelayCommand(() =>
{
// This is required when returning IEnumerable<T>
_Source.Remove(_SelectedResource);
workProvider.DelResource(_SelectedResource);
});
}
}
备用方法将涉及如下更改WorkProvider类(请注意返回的操作(workContext.Resources):
public void GetMyResources(Action<IEnumerable<Resource>> action)
{
var query = workContext.GetResourcesQuery()
.Where(r => r.UserName == WebContext.Current.User.Name);
workContext.Load(query, LoadBehavior.MergeIntoCurrent, lo =>
{
if (lo.HasError)
// Handle Error
throw lo.Error;
else
// Notice Changed Enumeration
action(workContext.Resources);
}, null);
}
对viewmodel的更改如下(注意删除_Source ObservableCollection):
public class HomeViewModel : ViewModelBase
{
WorkProvider workProvider;
public HomeViewModel()
{
workProvider = new WorkProvider();
}
public CollectionViewSource Resources { get; private set; }
void setupCollections()
{
Resources = new CollectionViewSource();
using (Resources.DeferRefresh())
{
Resources.Filter += (s,a) =>
{
a.Accepted = false;
if (s is Resource)
{
Resource res = s as Resource;
if (res.UserName == WebContext.Current.User.Name)
a.Accepted = true;
}
};
Resources.GroupDescriptions.Add(new PropertyGroupDescription("Title"));
Resources.SortDescriptions.Add(new SortDescription("Title", ListSortDirection.Ascending));
Resources.SortDescriptions.Add(new SortDescription("Rate", ListSortDirection.Ascending));
}
}
void loadMyResources()
{
workProvider.GetMyResources(results =>
{
using (Resources.DeferRefresh())
{
Resources.Source = results;
}
});
}
Resource _SelectedResource;
public Resource SelectedResource
{
get { return _SelectedResource; }
set
{
if (_SelectedResource != value)
{
_SelectedResource = value;
RaisePropertyChanged("SelectedResource");
}
}
}
public RelayCommand CmdSave { get; private set; }
public RelayCommand CmdUndo { get; private set; }
public RelayCommand CmdAdd { get; private set; }
public RelayCommand CmdDelete { get; private set; }
void setupCommands()
{
CmdSave = new RelayCommand(() =>
{
workProvider.SaveChanges(() =>
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
System.Windows.MessageBox.Show("Saved");
});
});
});
CmdUndo = new RelayCommand(() =>
{
workProvider.UndoChanges();
Resources.View.Refresh();
});
CmdAdd = new RelayCommand(() =>
{
Resource newResource = new Resource()
{
ResourceID = Guid.NewGuid(),
Rate = 125,
Title = "Staff",
UserName = "jsmith"
};
workProvider.AddResource(newResource);
});
CmdDelete = new RelayCommand(() =>
{
workProvider.DelResource(_SelectedResource);
});
}
}
虽然第二种方法肯定需要在CollectionViewSource的配置中添加过滤器事件处理程序,并且可以看作过滤数据2次(在服务器上一次,而第二次由CollectionViewSource过滤),它会关闭以下好处:有一个集合 - 这使得集合通知的管理更简单,更容易。该集合是将提交给服务器的实际集合,这使得管理添加/删除更简单,因为没有机会忘记在正确的集合中添加/删除实体以在提交时启动添加/删除功能。 / p>
我需要确认的最后一件事情如下:在collectviewsource上,我理解在进行影响视图的多个更改时应该使用DeferRefresh()。这可以防止在内部更改可能导致刷新(例如配置排序,分组等)时发生不必要的刷新。当我们期望UI处理某些更新更改时,调用.View.Refresh()也很重要。 .View.Refresh()可能比DeferRefresh()更重要,因为它实际上会导致UI更新,而不是防止意外的UI更新。
我不知道这是否会对其他人有所帮助,但我希望如此。我肯定花了一些时间来完成这些并试图理解这一点。如果您有澄清或其他要添加的内容,请随时这样做。
答案 1 :(得分:0)