【咱們一塊兒寫框架】MVVM的WPF框架(四)—DataGrid

前言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上了,而且會持續更新。

相關文章:

【咱們一塊兒寫框架】MVVM的WPF框架(一)—序篇

【咱們一塊兒寫框架】MVVM的WPF框架(二)—綁定

【咱們一塊兒寫框架】MVVM的WPF框架(三)—數據控件

To be continued——DataGrid

Github地址:https://github.com/kiba518/KibaFramework

----------------------------------------------------------------------------------------------------

注:此文章爲原創,任何形式的轉載都請聯繫做者得到受權並註明出處!
若您以爲這篇文章還不錯,請點擊下方的推薦】,很是感謝!

 

相關文章
相關標籤/搜索