前言html
這個框架寫到這裏,應該有不少同窗發現,框架不少地方的細節,實際上是違背了MVVM的設計邏輯的。git
沒錯,它的確是違背了。程序員
但爲何明知道違背設計邏輯,還要這樣編寫框架呢?github
那是由於,咱們編寫的是框架,是使用MVVM的概念編寫框架,而並非要完美的實現MVVM設計。設計模式
二者有什麼區別呢?區別就是前者是實戰,後者只是個理念。架構
在實戰架構中,並非UI的東西都必定要放在UI層寫,邏輯的東西放在邏輯層寫的。由於,架構的目的是讓程序員更好的寫代碼,而不是讓代碼死死的固定在某一層。框架
因此,咱們在編寫框架時,設計模式中該切割的東西,就不要猶豫的切割。由於,架構師是設計模式的使用者,而不是被使用者。學習
舉個例子,當你的邏輯所有提取到某一層中之後,你忽然發現,該邏輯執行過程當中要彈出提示框,但提示框又是屬於UI層的,此時你猶豫了,把提示框移動到邏輯層,不符合設計理念,但不在邏輯層作,開發又很難受。this
遇到這樣的狀況,咱們該怎麼作呢?spa
很簡單,讓設計理念去死吧,不要猶豫,直接把彈出提示框封裝到邏輯層中便可。
現實中,設計邏輯永遠是要向開發邏輯低頭的,由於實戰永遠高於理論。
框架是什麼?
框架就是規則,規則在人類社會被稱之爲法律;換言之,框架是代碼界的法律。
人類社會創建法律之初,是抱着人人守法,秩序穩定的理想的。
可現實是殘酷的,總有人,由於各類緣由,踐踏法律。
事實上,代碼界也同樣,老是會那不守規矩的程序員觸犯法律,他們會讓代碼跨邊界引用類庫,或者拒絕使用接口聲明對象等等。
爲何不能準守規則呢?
由於他們想更快速的完成任務,因此他們不惜觸犯法律,也要拼一次一晚上暴富。。。
因此,架構師做爲代碼界的人民警察,必定要作好懲治工做。。。
由於,當一個壞代碼出現後,立刻就會有若干個相似的壞代碼出現,猶如劣幣逐良幣同樣,時間一長,框架就會被破壞。
接着好代碼就得依賴着壞代碼寫。
當壞代碼多了到必定程度,好代碼就會變成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 }
從代碼中咱們能夠看到,DataGrid控件不只包含了基礎屬性,還包含了上一頁,下一頁,刷新,甚至過濾的功能。
下面,咱們看下一下DataGrid控件的基礎應用。
Xaml頁面代碼以下:
<DataGrid Margin="5" FontSize="12" ItemsSource="{Binding TestDataGrid.ItemsSource}" AutoGenerateColumns="True" SelectedItem="{Binding TestDataGrid.SelectedItem}" > </DataGrid>
ViewModel頁面代碼以下:
public DataGrid<User> TestDataGrid { get; set; } TestDataProxy proxy = new TestDataProxy(); public VM_PageDataGrid() { TestDataGrid = new DataGrid<User>(); int currentPage = TestDataGrid.CurrentPage; int skipNumber = TestDataGrid.SkipNumber; proxy.GeDataGridData(null, currentPage, skipNumber, (list, count, msg) => { TestDataGrid.SetItemsSource(list); TestDataGrid.RecordCount = count; }); TestDataGrid.SelectCallBack = (user) => { MessageBox(user.Name); }; }
咱們能夠看到,基礎的DataGrid應用很簡單,只要設置好綁定,而後將讀取的數據賦值給數據控件的ItemSource屬性便可。(這裏咱們使用SetItemSource方法爲ItemSource賦值)
而後咱們會發現,只要咱們操做數據控件的ItemSource,不管是增長數據,刪除數據,變動數據,頁面都會自動的同步刷新。
DataGrid的中級應用
咱們在上面的代碼中能夠看到,DataGrid數據控件還包含了分頁功能。那麼如何實現分頁功能呢。
很簡單,咱們只須要在Xaml頁面多綁定幾個屬性便可實現。
Xaml代碼以下:
<StackPanel DataContext="{Binding TestDataGrid}" Orientation="Horizontal" DockPanel.Dock="Bottom" HorizontalAlignment="Right" Margin="0,10,0,0"> <Button Content="上一頁" Width="60" Command="{Binding PreviousCommand}" Height="20" Margin="20,0,0,0" VerticalAlignment="Top" /> <Button Content="下一頁" Width="60" Command="{Binding NextCommand}" Height="20" Margin="20,0,0,0" VerticalAlignment="Top" /> <TextBlock VerticalAlignment="Center" Text="每頁" Margin="20,0,0,0"></TextBlock> <TextBlock VerticalAlignment="Center" Text="{Binding SkipNumber}" Margin="0,0,0,0"></TextBlock> <TextBlock VerticalAlignment="Center" Text="條"></TextBlock> <TextBlock VerticalAlignment="Center" Text="{Binding CurrentPage}" Margin="20,0,0,0"></TextBlock> <TextBlock VerticalAlignment="Center" Text="/"></TextBlock> <TextBlock VerticalAlignment="Center" Text="{Binding PageCount}"></TextBlock> <TextBlock VerticalAlignment="Center" Text="總記錄數:" Margin="20,0,0,0"></TextBlock> <TextBlock VerticalAlignment="Center" Text="{Binding RecordCount}"></TextBlock> <TextBox VerticalAlignment="Center" Width="40" Height="20" Margin="40,0,0,0" Text="{Binding JumpTextBox.Text}" ></TextBox> <Button Content="GO" Command="{Binding JumpCommand}" Width="40" Height="20" Margin="5,0,0,0" VerticalAlignment="Top" /> </StackPanel> <GroupBox DockPanel.Dock="Top" Header="DataGrid" Margin="10,0,0,0" > <DataGrid Margin="5" FontSize="12" ItemsSource="{Binding TestDataGrid.ItemsSource}" AutoGenerateColumns="True" SelectedItem="{Binding TestDataGrid.SelectedItem}" > </DataGrid> </GroupBox>
這樣咱們就實現了分頁功能,代碼很簡單,而且完全分割了UI和ViewModel。
可是那麼複雜的UI,就這樣簡單的被完全搞定了嗎?
固然是不可能的!UI很複雜,僅僅靠數據控件是沒法完全搞定的。
那麼咱們應該怎麼辦呢?
很簡單,咱們去編寫UI控件就好啦。
固然,咱們要編寫的UI控件不是普通的UI控件,而是配合數據控件應用的UI控件。
這種定製UI控件在功能上與其餘自定義控件是同樣,但好處就在於,編寫方便,易於理解和二次開發。
----------------------------------------------------------------------------------------------------
本篇文章就先講到這了,下一篇文章咱們將一塊兒爲框架編寫UI控件。
框架代碼已經傳到Github上了,而且會持續更新。
相關文章:
To be continued——DataGrid
Github地址:https://github.com/kiba518/KibaFramework
----------------------------------------------------------------------------------------------------
注:此文章爲原創,任何形式的轉載都請聯繫做者得到受權並註明出處!
若您以爲這篇文章還不錯,請點擊下方的【推薦】,很是感謝!