这个框架写到这里,应该有很多同学发现,框架很多地方的细节,其实是违背了MVVM的设计逻辑的。
没错,它的确是违背了。
但为什么明知道违背设计逻辑,还要这样编写框架呢?
那是因为,我们编写的是框架,是使用MVVM的概念编写框架,而并不是要完美的实现MVVM设计。
两者有什么区别呢?区别就是前者是实战,后者只是个理念。
在实战架构中,并不是UI的东西都一定要放在UI层写,逻辑的东西放在逻辑层写的。因为,架构的目的是让程序员更好的写代码,而不是让代码死死的固定在某一层。
所以,我们在编写框架时,设计模式中该切割的东西,就不要犹豫的切割。因为,架构师是设计模式的使用者,而不是被使用者。
举个例子,当你的逻辑全部提取到某一层中以后,你突然发现,该逻辑执行过程中要弹出提示框,但提示框又是属于UI层的,此时你犹豫了,把提示框移动到逻辑层,不符合设计理念,但不在逻辑层做,开发又很难受。
遇到这样的情况,我们该怎么做呢?
很简单,让设计理念去死吧,不要犹豫,直接把弹出提示框封装到逻辑层中即可。
现实中,设计逻辑永远是要向开发逻辑低头的,因为实战永远高于理论。
框架是什么?
框架就是规则,规则在人类社会被称之为法律;换言之,框架是代码界的法律。
人类社会建立法律之初,是抱着人人守法,秩序稳定的理想的。
可现实是残酷的,总有人,因为各种原因,践踏法律。
事实上,代码界也一样,总是会那不守规矩的程序员触犯法律,他们会让代码跨边界引用类库或者拒绝使用接口声明对象等等。
为什么不能准守规则呢?
因为他们想更快速的完成任务,所以他们不惜触犯法律,也要拼一次一夜暴富。。。
所以,架构师作为代码界的人民警察,一定要做好惩治工作。。。
因为,当一个坏代码出现后,马上就会有若干个类似的坏代码出现,犹如劣币逐良币一样,时间一长,框架就会被破坏。
接着好代码就得依赖着坏代码写。
当坏代码多了到一定程度,好代码就会变成Bug了。。。
所以,任重道远,人民警察还需警惕。。。
为什么要编写数据控件
我们之前编写的数据控件功能相对单一;完全可以用属性和事件代替,所以有些同学会觉得,数据控件好像没什么用。
其实不然,现实中我们要处理的逻辑,并不是简单的对象属性一对一绑定就能处理解决的。
我们需要做很多操作,其中也包括UI操作。而数据控件就是用来应对这种复杂的UI操作的。
因为数据控件通过绑定UI控件后,已经将复杂的UI操作,变成了简单的数据逻辑操作了。
如果没有数据控件,那当我们实现一个控件联动时,就得在Xaml.cs文件中处理了。
如果该控件联动还要触发数据变化,那我们就又得从Xaml.cs文件中,穿越回ViewModel中处理逻辑了;亦或者,我们直接在Xaml.cs文件中处理数据逻辑。
不论哪种模式,都会将我们好容易做的逻辑层与UI层混淆到一起。而这个问题,并不是一个弹出框那么简单的UI越界问题,因为它包含了更多复杂的业务逻辑。
数据控件解决这个烦恼。
我们通过数据控件,实现了控件是控件,数据是数据,清晰的,层次分离;并且通过简洁的绑定,实现了数据变化与控件变化同步。
DataGrid数据控件
DataGrid数据控件可以说是数据控件的精髓了,因为DataGrid相对复杂,不像其他的数据控件那样功能单一。
所以,当然我们学习了DataGrid数据控件后,就可以更好的理解,数据控件的意义了。
下面我们先看下DataGrid数据控件的代码:
public class DataGrid<T> : Control<T> { private Action<T> LoadAction = null; public Action<T> SelectCallBack = null; private Func<object, bool> DataFilter = null; #region 分页 private volatile int _CurrentPage = 1; public int CurrentPage { get { return _CurrentPage; } set { _CurrentPage = value; if (_CurrentPage > PageCount) { _CurrentPage = PageCount; } if (_CurrentPage < 1) { _CurrentPage = 1; } OnPropertyChanged(); } } private int _PageCount = 1; public int PageCount { get { return _PageCount; } set { _PageCount = value; OnPropertyChanged(); } } private int _RecordCount = 0; public int RecordCount { get { return _RecordCount; } set { _RecordCount = value; if (_RecordCount <= SkipNumber) { PageCount = 1; } else { PageCount = int.Parse(Math.Ceiling((double)RecordCount / (double)SkipNumber).ToString()); } if (_CurrentPage > PageCount) { _CurrentPage = PageCount; } OnPropertyChanged(); } } private int _SkipNumber = 30; public int SkipNumber { get { return _SkipNumber; } set { _SkipNumber = value; OnPropertyChanged(); } } private TextBox<string> _JumpTextBox = new TextBox<string>(); public TextBox<string> JumpTextBox { get { return _JumpTextBox; } set { _JumpTextBox = value; OnPropertyChanged(); } } #region 跳页 public BaseCommand JumpCommand { get { return new BaseCommand(JumpCommand_Executed); } } void JumpCommand_Executed(object send) { int pagenum = 0; if (int.TryParse(JumpTextBox.Text, out pagenum)) { if (pagenum <= PageCount && pagenum > 0) { CurrentPage = pagenum; if (LoadAction != null) { LoadAction(Condition); } } else { MessageBox.Show("请正确填写跳转页数。", "提示信息"); } } else { MessageBox.Show("请正确填写跳转页数。", "提示信息"); } } #endregion #region 上一页 public BaseCommand PreviousCommand { get { return new BaseCommand(PreviousCommand_Executed); } } void PreviousCommand_Executed(object send) { if (CurrentPage > 1) { CurrentPage -= 1; if (LoadAction != null) { LoadAction(Condition); } } else { MessageBox.Show("已至首页。", "提示信息"); } } #endregion #region 下一页 public BaseCommand NextCommand { get { return new BaseCommand(NextCommand_Executed); } } void NextCommand_Executed(object send) { if (CurrentPage < PageCount) { CurrentPage += 1; if (LoadAction != null) { LoadAction(Condition); } } else { MessageBox.Show("已至末页。", "提示信息"); } } #endregion #endregion private ObservableCollection<T> _ItemsSource = new ObservableCollection<T>(); public ObservableCollection<T> ItemsSource { get { return _ItemsSource; } set { _ItemsSource = value; if (_ItemsSource != null && _ItemsSource.Count > 0 && SelectedItem == null) { SelectedItem = _ItemsSource.First(); } OnPropertyChanged(); } } public void SetItemsSource(List<T> itemSource) { ItemsSource = new ObservableCollection<T>(itemSource); } public T _SelectedItem; public T SelectedItem { get { return _SelectedItem; } set { _SelectedItem = value; if (SelectCallBack != null) { SelectCallBack(_SelectedItem); } OnPropertyChanged(); } } private ICollectionView _ItemsSourceView; public ICollectionView ItemsSourceView { get { _ItemsSourceView = CollectionViewSource.GetDefaultView(_ItemsSource); return _ItemsSourceView; } set { _ItemsSourceView = value; OnPropertyChanged(); } } private T _Condition = (T)Activator.CreateInstance(typeof(T)); public T Condition { get { return _Condition; } set { _Condition = value; OnPropertyChanged(); } } #region 方法 public DataGrid() { } public void BindSource(Action<T> loadAction, T conditionRow = default(T)) { LoadAction = loadAction; if (LoadAction != null) { CurrentPage = 1; LoadAction(conditionRow); } } public void BindSource(Action loadAction) { LoadAction = new Action<T>((obj) => { loadAction(); }); ; if (LoadAction != null) { CurrentPage = 1; LoadAction(default(T)); } } public void ItemsSourceReBind() { BindSource(LoadAction); } public void SelectedItemReBind() { T newitem = (T)Activator.CreateInstance(typeof(T)); List<System.Reflection.PropertyInfo> plist = typeof(T).GetProperties().ToList(); foreach (var propertyInfo in plist) { propertyInfo.SetValue(newitem, propertyInfo.GetValue(SelectedItem)); } SelectedItem = newitem; } public void SetFilter(Func<object, bool> dataFilter) { try { DataFilter = dataFilter; _ItemsSourceView = CollectionViewSource.GetDefaultView(_ItemsSource); _ItemsSourceView.Filter = new Predicate<object>(DataFilter); } catch(Exception ex) { } } public void Refresh() { if (_ItemsSourceView == null) { _ItemsSourceView = CollectionViewSource.GetDefaultView(this.ItemsSource); } _ItemsSourceView.Refresh(); } #endregion }