寫在前面 本文將會介紹WPF如何實現前後端數據綁定和在進行數據綁定時常用的方法和類以及對於DataGrid、ListView這樣的控制項重寫數據模板後控制項如何進行數據綁定。 本文主要針對於數據綁定的基礎實現進行介紹,通過此博文你將會有能力編寫一個MVVM設計模式的C#、WPF項目。如果您是C#及WPF ...
寫在前面
本文將會介紹WPF如何實現前後端數據綁定和在進行數據綁定時常用的方法和類以及對於DataGrid、ListView這樣的控制項重寫數據模板後控制項如何進行數據綁定。
本文主要針對於數據綁定的基礎實現進行介紹,通過此博文你將會有能力編寫一個MVVM設計模式的C#、WPF項目。如果您是C#及WPF的資深開發人員本文可能對您沒有太大的幫助,但如果你是一個正在學習和瞭解C#、WPF的開發人員來說本文可以幫助你認識MVVM設計模式和數據綁定。
一、實現前後端數據綁定:
說到前後端的數據綁定,就需要先說一下WPF的MVVM設計模式,它是由傳統的MVC設計模式改進而來,不同點在於MVVM數據源更新不需要一個Controller控制器來向前臺同步數據,同時前臺數據更改也不需要控制器向後臺同步。如果想深入詳細的瞭解MVVM設計模式百度百科對這部分的講解和說明我認為非常的詳細和系統。也可以閱讀下方的實例,相信通過代碼實例更能夠讓你對MVVM有一個更深入的認識。
實例:
源代碼地址(碼雲):https://gitee.com/hkb1202/csharp-wpf-data-binding-demo
實例基於.Net Core 3.1平臺,為博主編寫並親測可用的,通過實例相信你可以更好的理解這部分內容。
新建一個WPF項目,並且添加Command類和MainWindowsViewModel類
Command.cs代碼:
1 using System; 2 using System.Windows.Input; 3 4 namespace WpfExample 5 { 6 public class Command : ICommand 7 { 8 9 /// <summary> 10 /// 檢查命令是否可以執行的事件,在UI事件發生導致控制項狀態或數據發生變化時觸發 11 /// </summary> 12 public event EventHandler CanExecuteChanged 13 { 14 add 15 { 16 if (_canExecute != null) 17 { 18 CommandManager.RequerySuggested += value; 19 } 20 } 21 remove 22 { 23 if (_canExecute != null) 24 { 25 CommandManager.RequerySuggested -= value; 26 } 27 } 28 } 29 30 /// <summary> 31 /// 判斷命令是否可以執行的方法 32 /// </summary> 33 private Func<object, bool> _canExecute; 34 35 /// <summary> 36 /// 命令需要執行的方法 37 /// </summary> 38 private Action<object> _execute; 39 40 /// <summary> 41 /// 創建一個命令 42 /// </summary> 43 /// <param name="execute">命令要執行的方法</param> 44 public Command(Action<object> execute) : this(execute, null) 45 { 46 47 } 48 49 /// <summary> 50 /// 創建一個命令 51 /// </summary> 52 /// <param name="execute">命令要執行的方法</param> 53 /// <param name="canExecute">判斷命令是否能夠執行的方法</param> 54 public Command(Action<object> execute, Func<object, bool> canExecute) 55 { 56 _execute = execute; 57 _canExecute = canExecute; 58 } 59 60 /// <summary> 61 /// 判斷命令是否可以執行 62 /// </summary> 63 /// <param name="parameter">命令傳入的參數</param> 64 /// <returns>是否可以執行</returns> 65 public bool CanExecute(object parameter) 66 { 67 if (_canExecute == null) return true; 68 return _canExecute(parameter); 69 } 70 71 /// <summary> 72 /// 執行命令 73 /// </summary> 74 /// <param name="parameter"></param> 75 public void Execute(object parameter) 76 { 77 if (_execute != null && CanExecute(parameter)) 78 { 79 _execute(parameter); 80 } 81 } 82 } 83 }Command.cs
Command類是實現ICommand介面,從而實現將前臺的命令註冊到後臺的ViewModel中,在此不詳細展開講解,請先照抄代碼,如果對這部分感興趣可以查閱博客園中關於ICommand介面的詳解。暫時不理解這裡並不影響後續的編碼。
MainWindowViewModel.cs代碼:
1 using System.Collections.ObjectModel; 2 using System.ComponentModel; 3 using System.Windows; 4 using System.Windows.Controls; 5 6 namespace WpfExample 7 { 8 public class MainWindowViewModel : INotifyPropertyChanged 9 { 10 //實現介面當數據源變動通知前臺UI 11 public event PropertyChangedEventHandler? PropertyChanged; 12 13 public void RaisePropertyChanged(string propName) 14 { 15 if (PropertyChanged != null) 16 { 17 PropertyChanged(this, new PropertyChangedEventArgs(propName)); 18 } 19 } 20 21 /// <summary> 22 /// 前臺DataGrid綁定的People集合 23 /// </summary> 24 public ObservableCollection<Person> People { get; set; } 25 26 /// <summary> 27 /// 綁定前臺DataGrid控制項SelectedItem欄位上,用於保存當前選中的Item所對應的數據源 28 /// </summary> 29 public Person SelectItem 30 { 31 get 32 { 33 return m_SelectItem; 34 } 35 set 36 { 37 m_SelectItem = value; 38 RaisePropertyChanged("SelectItem"); 39 } 40 } 41 42 /// <summary> 43 /// DataGrid控制項中刪除按鈕命令 44 /// </summary> 45 public Command DelClick 46 { 47 get 48 { 49 if (m_DelClick == null) 50 m_DelClick = new Command(DeleteEvent); 51 52 return m_DelClick; 53 } 54 } 55 56 /// <summary> 57 /// 前臺添加小剛按鈕命令 58 /// </summary> 59 public Command AddClick 60 { 61 get 62 { 63 if (m_AddClick == null) 64 m_AddClick = new Command(AdditionEvent); 65 66 return m_AddClick; 67 } 68 } 69 70 /// <summary> 71 /// 前臺修改Text按鈕命令 72 /// </summary> 73 public Command ReviseClick 74 { 75 get 76 { 77 if (m_ReviseClick == null) 78 m_ReviseClick = new Command(ReviseEvent); 79 80 return m_ReviseClick; 81 } 82 } 83 84 /// <summary> 85 /// 前臺TextBlock控制項顯示的文本 86 /// </summary> 87 public string TextInfo 88 { 89 get 90 { 91 return m_TextInfo; 92 } 93 set 94 { 95 m_TextInfo = value; 96 //數據源更新調用更新前臺UI方法 97 RaisePropertyChanged("TextInfo"); 98 } 99 } 100 101 /// <summary> 102 /// DataGrid控制項電話信息的TextBox鍵盤按下回車命令 103 /// </summary> 104 public Command PressEnterKey 105 { 106 get 107 { 108 if (m_PressEnterKey == null) 109 m_PressEnterKey = new Command(PressEnterKeyEvent); 110 111 return m_PressEnterKey; 112 } 113 } 114 115 private Person m_SelectItem; 116 private Command m_DelClick; 117 private Command m_AddClick; 118 private Command m_ReviseClick; 119 private string m_TextInfo; 120 private Command m_PressEnterKey; 121 122 /// <summary> 123 /// 構造方法 124 /// </summary> 125 public MainWindowViewModel() 126 { 127 People = new ObservableCollection<Person>(); 128 129 Person person1 = new Person() { Name = "小明", Age = 12, Sex = "男", Phone = "110" }; 130 Person person2 = new Person() { Name = "小紅", Age = 13, Sex = "女", Phone = "119" }; 131 Person person3 = new Person() { Name = "小王", Age = 15, Sex = "男", Phone = "120" }; 132 133 People.Add(person1); 134 People.Add(person2); 135 People.Add(person3); 136 137 TextInfo = "點擊右側按鈕這裡內容將會變化!"; 138 } 139 140 /// <summary> 141 /// DataGrid控制項中刪除按鈕事件 142 /// </summary> 143 /// <param name="obj">可傳入前臺控制項</param> 144 private void DeleteEvent(object obj) 145 { 146 if (MessageBox.Show($"是否刪除{SelectItem.Name}的數據?", "提示", MessageBoxButton.YesNo) == MessageBoxResult.Yes) 147 { 148 People.Remove(SelectItem); 149 } 150 } 151 152 /// <summary> 153 /// 前臺添加小剛按鈕事件 154 /// </summary> 155 /// <param name="obj">可傳入前臺控制項</param> 156 private void AdditionEvent(object obj) 157 { 158 if (MessageBox.Show("是否添加“姓名:小剛,年齡:18,性別:女,電話:123”?", "提示", MessageBoxButton.YesNo) == MessageBoxResult.Yes) 159 { 160 People.Add(new Person() { Name = "小剛", Age = 18, Sex = "女", Phone = "123" }); 161 } 162 } 163 164 /// <summary> 165 /// 前臺修改Text按鈕事件 166 /// </summary> 167 /// <param name="obj">可傳入前臺控制項</param> 168 private void ReviseEvent(object obj) 169 { 170 if (TextInfo == "點擊右側按鈕這裡內容將會變化!") 171 { 172 TextInfo = "點擊了右側按鈕!!!!!!!!!"; 173 } 174 else 175 { 176 TextInfo = "點擊右側按鈕這裡內容將會變化!"; 177 } 178 } 179 180 /// <summary> 181 /// DataGrid控制項電話信息的TextBox鍵盤按下回車事件 182 /// </summary> 183 /// <param name="obj">可傳入前臺控制項</param> 184 private void PressEnterKeyEvent(object obj) 185 { 186 TextBox textBox = (TextBox)obj; 187 MessageBox.Show($"點擊了回車!控制項內容為:{textBox.Text}"); 188 } 189 190 /// <summary> 191 /// 數據結構 192 /// </summary> 193 public class Person 194 { 195 public string Name { get; set; } 196 public int Age { get; set; } 197 public string Sex { get; set; } 198 public string Phone { get; set; } 199 } 200 } 201 }MainWindowViewModel.cs
MainWindowsViewModel類中編寫業務邏輯代碼。
MainWindow.xaml代碼:
1 <Window x:Class="WpfExample.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WpfExample" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="450" Width="800"> 9 <Grid> 10 <DataGrid Margin="10,10,10,50" ItemsSource="{Binding People, Mode=TwoWay}" SelectedItem="{Binding SelectItem}" AutoGenerateColumns="False" CanUserAddRows="False"> 11 12 <DataGrid.Columns> 13 <DataGridTextColumn Header="姓名" Binding="{Binding Name}" Width="*" /> 14 <DataGridTextColumn Header="年齡" Binding="{Binding Age}" Width="*" /> 15 <DataGridTextColumn Header="性別" Binding="{Binding Sex}" Width="*" /> 16 <DataGridTemplateColumn Header="電話" Width="*"> 17 <DataGridTemplateColumn.CellTemplate> 18 <DataTemplate> 19 <TextBox x:Name="TextBox_Phone" Text="{Binding Phone}"> 20 <TextBox.InputBindings> 21 <KeyBinding Key="Return" Command="{Binding Path=DataContext.PressEnterKey, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=DataGrid}}" CommandParameter="{Binding ElementName=TextBox_Phone}"></KeyBinding> 22 </TextBox.InputBindings> 23 </TextBox> 24 </DataTemplate> 25 </DataGridTemplateColumn.CellTemplate> 26 </DataGridTemplateColumn> 27 <DataGridTemplateColumn Header="刪除" Width="*"> 28 <DataGridTemplateColumn.CellTemplate> 29 <DataTemplate> 30 <Button Content="刪除" Command="{Binding Path=DataContext.DelClick, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=DataGrid}}" /> 31 </DataTemplate> 32 </DataGridTemplateColumn.CellTemplate> 33 </DataGridTemplateColumn> 34 </DataGrid.Columns> 35 </DataGrid> 36 <Button Margin="10,0,10,10" VerticalAlignment="Bottom" HorizontalAlignment="Left" Height="30" Width="100" Content="添加學生小剛" Command="{Binding AddClick}"></Button> 37 <TextBlock Margin="0,0,140,10" VerticalAlignment="Bottom" HorizontalAlignment="Right" Text="{Binding TextInfo}" FontSize="24"></TextBlock> 38 <Button Margin="0,0,10,10" VerticalAlignment="Bottom" HorizontalAlignment="Right" Height="30" Width="100" Content="修改Text