CPF 入門教程 - 資料繫結和命令繫結(二)
CPF netcore跨平臺UI框架
系列教程
CPF 入門教程(一)
CPF 入門教程 - 資料繫結和命令繫結(二)
資料繫結和Wpf類似,支援雙向繫結。資料繫結和命令繫結是UI和業務邏輯分離的基礎。
首先,你需要定義個MainModel,為了可以有屬性通知,這個類可以繼承CpfObject或者自己實現INotifyPropertyChanged
1 public class MainModel : CpfObject 2 { 3 [PropertyMetadata("預設值")] 4 public string Test 5 { 6 get { return (string)GetValue(); } 7 set { SetValue(value); } 8 } 9 }
這裡定義了一個Test屬性,並且設定預設值為“預設值”
設計個測試介面,加個TextBlock和Button,同時TextBlock設定繫結,繫結定義在Bindings屬性, {nameof(TextBlock.Text),nameof(MainModel.Test) } 表示TextBlock的Text繫結到DataContext的Test屬性
public class Window4 : Window { protected override void InitializeComponent() { Title = "標題"; Width = 344.8f; Height = 126.4f; Background = null; Children.Add(new WindowFrame(this, new Panel { Width = "100%", Height = "100%", Children = { //內容元素放這裡 new Button { MarginLeft = 223.8f, MarginTop = 25.7f, Height = 28f, Width = 67.4f, Content = "Button", }, new TextBlock { MarginLeft = 36.7f, MarginTop = 31.6f, Text = "TextBlock", Bindings = { {nameof(TextBlock.Text),nameof(MainModel.Test) } } }, } })); LoadStyleFile("res://ConsoleApp1.Stylesheet1.css"); //載入樣式檔案,檔案需要設定為內嵌資源 } }
修改program,設定Window的DataContext和CommandContext
var model = new MainModel(); Application.Run(new Window4 { DataContext = model, CommandContext = model });
寫好之後,執行看看效果。TextBlock那邊顯示MainModel那邊定義的預設值
接下來定義命令,通過按鈕點選修改Test值,同時自動更新到TextBlock
MainModel裡增加個Click方法
class MainModel : CpfObject { [PropertyMetadata("預設值")] public string Test { get { return (string)GetValue(); } set { SetValue(value); } } public void Click() { Test += "test"; } }
Button那邊增加命令繫結,Commands裡新增, {nameof(Button.Click),nameof(MainModel.Click) } 表示Button的Click事件繫結到CommandContext的Click方法
new Button { MarginLeft = 223.8f, MarginTop = 25.7f, Height = 28f, Width = 67.4f, Content = "Button", Commands = { {nameof(Button.Click),nameof(MainModel.Click) } } },
執行效果,點選一次增加一次test。這就是最簡單的模型檢視分離的資料繫結
接下來繫結集合
設計介面,新增Button和ListBox
往MainModel里加上Items集合屬性,建構函式裡初始化集合,用 Collection 是為了有集合變化通知,也可以使用 ObservableCollection。 (string, string) 就是元組裡簡化的結構體型別定義,是一種偷懶簡化資料定義的方式,不過這種方式的話,改item就不能更新到UI了,需要可以更新到UI的就需要自定義型別,繼承CpfObject或者繼承INotifyPropertyChanged的型別作為Item
public MainModel() { Items = new Collection<(string, string)>(); } public Collection<(string,string)> Items { get { return (Collection<(string, string)>)GetValue(); } set { SetValue(value); } }
MainModel里加個AddItem方法
public void AddItem() { Items.Add(("test" + Items.Count, Items.Count.ToString())); }
最終程式碼
using CPF; using System; using System.Collections.Generic; using System.Text; namespace ConsoleApp1 { class MainModel : CpfObject { [PropertyMetadata("預設值")] public string Test { get { return (string)GetValue(); } set { SetValue(value); } } public void Click() { Test += "test"; } public MainModel() { Items = new Collection<(string, string)>(); } public Collection<(string, string)> Items { get { return (Collection<(string, string)>)GetValue(); } set { SetValue(value); } } public void AddItem() { Items.Add(("test" + Items.Count, Items.Count.ToString())); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using CPF; using CPF.Drawing; using CPF.Controls; using CPF.Shapes; using CPF.Styling; using CPF.Animation; namespace ConsoleApp1 { public class Window4 : Window { protected override void InitializeComponent() { Title = "標題"; Width = 338.4f; Height = 205.6f; Background = null; Children.Add(new WindowFrame(this, new Panel { Width = "100%", Height = "100%", Children = { //內容元素放這裡 new Button { MarginLeft = 223.8f, MarginTop = 25.7f, Height = 28f, Width = 67.4f, Content = "Button", Commands = { { nameof(Button.Click), nameof(MainModel.Click) } } }, new TextBlock { MarginLeft = 36.7f, MarginTop = 31.6f, Text = "TextBlock", Bindings = { { nameof(TextBlock.Text), nameof(MainModel.Test) } } }, new Button { MarginLeft = 223.8f, MarginTop = 91.6f, Height = 28f, Width = 67.4f, Content = "新增Item", Commands = { {nameof(Button.Click),nameof(MainModel.AddItem) } } }, new ListBox { SelectedValuePath = "Item2",//繫結Item裡的Item1屬性 DisplayMemberPath = "Item1",//繫結Item裡的Item2屬性 BorderStroke = "1,Solid", BorderFill = "#DEDEDE", MarginLeft = 36.7f, MarginTop = 60.8f, Height = 76.5f, Width = 123.2f, Bindings = { {nameof(ListBox.Items),nameof(MainModel.Items) } } }, } })); LoadStyleFile("res://ConsoleApp1.Stylesheet1.css"); //載入樣式檔案,檔案需要設定為內嵌資源 } } }
最終執行效果,點選新增Item的按鈕,ListBox裡會增加Item
資料型別轉換,Test屬性值後面加1。 資料轉換器用方法或者Lambda就行。
{ nameof(TextBlock.Text), nameof(MainModel.Test), null, BindingMode.OneWay, (string a)=>a+"1" }
UI元素之間繫結,TextBox的Text繫結到Button的Content,其中TextBox設定PresenterFor=this,是為了標記TextBox的作用域在當前類,因為Name是可以重複的,元素巢狀如果有相同Name會無法判斷元素是在哪裡的,所以用PresenterFor加標記判斷,而且這樣可以通過FindPresenterByName方法來獲取當前類裡的標記元素來繫結
protected override void InitializeComponent() { Title = "標題"; Width = 338.4f; Height = 205.6f; Background = null; Children.Add(new WindowFrame(this, new Panel { Width = "100%", Height = "100%", Children = { //內容元素放這裡 new Button { MarginLeft = 223.8f, MarginTop = 25.7f, Height = 28f, Width = 67.4f, Content = "Button", Commands = { { nameof(Button.Click), nameof(MainModel.Click) } }, Bindings = { { nameof(Button.Content), nameof(TextBox.Text), FindPresenterByName("textBox") } } }, new TextBlock { MarginLeft = 36.7f, MarginTop = 31.6f, Text = "TextBlock", Bindings = { { nameof(TextBlock.Text), nameof(MainModel.Test), null, BindingMode.OneWay, (string a)=>a+"1" } } }, new Button { MarginLeft = 223.8f, MarginTop = 91.6f, Height = 28f, Width = 67.4f, Content = "新增Item", Commands = { { nameof(Button.Click), nameof(MainModel.AddItem) } } }, new ListBox { SelectedValuePath = "Item2", //繫結Item裡的Item1屬性 DisplayMemberPath = "Item1", //繫結Item裡的Item2屬性 BorderStroke = "1,Solid", BorderFill = "#DEDEDE", MarginLeft = 36.7f, MarginTop = 60.8f, Height = 76.5f, Width = 123.2f, Bindings = { { nameof(ListBox.Items), nameof(MainModel.Items) } } }, new TextBox { Name="textBox", PresenterFor=this, AcceptsReturn= false, HScrollBarVisibility= ScrollBarVisibility.Hidden, VScrollBarVisibility= ScrollBarVisibility.Hidden, MarginLeft = 144.8f, MarginTop = 28.1f, Width = 74.5f }, } })); LoadStyleFile("res://ConsoleApp1.Stylesheet1.css"); //載入樣式檔案,檔案需要設定為內嵌資源 }
TextBox輸入,會自動更新Button的文字
命令繫結除了事件之外,屬性變化也可以繫結為命令,比如,滑鼠移入和移出就呼叫
Commands = { { nameof(Button.IsMouseOver), nameof(MainModel.Click) } }
主要繫結就這些,如果要雙向繫結,命令引數等等,看VS那邊的智慧提示