CPF netcore跨平台UI框架
系列教程
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那边的智能提示