一些总结
修改对象 = binding 数据源
数据绑定
数据绑定涉及到两个方面:一个是绑定源,再一个是绑定目标。
绑定源即控件绑定所使用的源数据
绑定目标即数据显示的控件
绑定源
1、CLR对象(实体):可以绑定到CLR类的公开的属性、子属性、索引器上
2、ADO.Net对象(数据库):例如DataTable、DataView等
3、XML文件:使用xPath进行解析
4、DependencyObject(依赖对象):绑定到其依赖项属性上,即控件绑定控件
绑定目标
对于绑定目标,必须是WPF中的DependencyObject,将数据绑定到其依赖项属性上。
(依赖属性里自带数据更新功能)
绑定属性
创建新的窗口的时候,记得在app.xaml里面的StartupUrl里修改位置
这段代码结果为,在上面的TextBlock输入后,自动会显示在下面的TextBlock里
<StackPanel Margin="10">
<TextBox Name="txtBingSource"/>
<WrapPanel>
<TextBlock Text="Value:" FontWeight="Bold"/>
<TextBox Text="{Binding ElementName=txtBingSource,Path=Text}"/>
WrapPanel>
StackPanel>
DataContext属性
1、创建一个新的窗体(这个title、width、height是界面的大小)
2、在窗体的cs文件里加上DataContext就可以更便捷的使用了
<StackPanel>
<WrapPanel>
<TextBlock Text="窗口的标题为:"/>
<TextBox Text="{Binding Path=Title}" Width="150"/>
WrapPanel>
<WrapPanel Margin="0 10 0 0 ">
<TextBlock Text="窗口的宽度为:"/>
<TextBox Text="{Binding Width}" Width="50"/>
<TextBlock Text="窗口的高度为:"/>
<TextBox Text="{Binding Height}" Width="50"/>
WrapPanel>
StackPanel>
public partial class DataContextSample : Window
{
public DataContextSample()
{
InitializeComponent();
// 设置窗口的数据上下文对象
this.DataContext = this;
}
}
后台代码数据绑定
1、创建一个新的窗口
2、在后台内绑定数据
<StackPanel Margin="10">
<TextBox Name="txtBindingSource"/>
<WrapPanel>
<TextBlock Text="Value:" FontWeight="Bold"/>
<TextBlock Name="lbValue"/>
WrapPanel>
StackPanel>
public BindingBackend()
{
InitializeComponent();
// 绑定前端控件显示数据
BindingData();
}
private void BindingData()
{
// 创建绑定对象
var binding = new Binding("Text");
// 设置绑定源
binding.Source = txtBindingSource;
// 设置绑定目标
lbValue.SetBinding(TextBlock.TextProperty,binding);
}
UpdateSourcetrigger属性
个人理解是及时更新
Explicit–必须手动更新数据源
LostFocus–失去焦点后就修改数值
PropertyChanged–时刻在变化
<StackPanel Margin="10">
<WrapPanel>
<TextBlock Text="窗口标题:"/>
<TextBox Name="txtWindowTitle" Text="{Binding Title,UpdateSourceTrigger=Explicit}" Width="150"/>
<Button Name="BtnUpdateSource" Content="更新源" Click="BtnUpdateSource_Click"/>
WrapPanel>
<WrapPanel Margin="0 10 0 0">
<TextBlock Text="窗口尺寸:"/>
<TextBox Text="{Binding Width,UpdateSourceTrigger=LostFocus}" Width="50"/>
<TextBlock Text="x"/>
<TextBox Text="{Binding Height,UpdateSourceTrigger=PropertyChanged}" Width="50"/>
WrapPanel>
StackPanel>
public UpdateSourceTriggerSamples()
{
InitializeComponent();
this.DataContext = this;
}
private void BtnUpdateSource_Click(object sender, RoutedEventArgs e)
{
// 通过文本框对象获取绑定表达式对象
var binding = txtWindowTitle.GetBindingExpression(TextBox.TextProperty);
// 手动更行绑定对象
binding.UpdateSource();
}
数据更新
<StackPanel Margin="20">
<TextBox Width="150" Text="{Binding Name}"/>
<Button Margin="0 10 0 0" Content="更新源" Name="BtnUpdateSource" Click="BtnUpdateSource_Click" Width="150"/>
StackPanel>
public partial class DataUpdateDemo : Window
{
User user = new User();
public DataUpdateDemo()
{
InitializeComponent();
// 创建user对象
user.Name = "Gerry";
// 指定窗口DataContext窗口
this.DataContext= user;
}
private void BtnUpdateSource_Click(object sender, RoutedEventArgs e)
{
this.user.Name = "Update userInfo"; // 后台的user数据已经发生改变了,但是ui界面的没有变化
}
}
///
/// 创建user类 让源头(发生改变的源头)继承接口
///
public class User : INotifyPropertyChanged
{
private string? _name;
public string? Name
{
get { return _name; }
set {
_name = value;
OnPropertyChanged(nameof(Name)); // 当值每次发生变化的时候
}
}
public event PropertyChangedEventHandler? PropertyChanged;
///
/// 当指定的某个属性发生变化的时候对界面UI的线程推送更新消息
///
///
public void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
ObservableCollection完成列表更新
<DockPanel Margin="20">
<StackPanel DockPanel.Dock="Right" Margin="10 0 0 0">
<Button Name="BtnAdd" Content="新增用户" Click="BtnAdd_Click"/>
<Button Name="BtnUpdate" Content="修改用户" Click="BtnUpdate_Click" Margin="0 5 0 5"/>
<Button Name="BtnDelete" Content="删除用户" Click="BtnDelete_Click"/>
StackPanel>
<ListBox Name="lbUsers" DisplayMemberPath="Name"/>
DockPanel>
public partial class DataUpdateDemo2 : Window
{
//private List users = new List();
private ObservableCollection<UserInfo> users = new ObservableCollection<UserInfo>();
public DataUpdateDemo2()
{
InitializeComponent();
//初始化集合中的元素 一个新的方法
InitializeUsers();
// 绑定用户集合到列表框
lbUsers.ItemsSource = users;
}
private void InitializeUsers()
{
users.Add(new UserInfo { Name = ".Net Core" });
users.Add(new UserInfo { Name = ".Net Framework" });
}
private void BtnAdd_Click(object sender, RoutedEventArgs e)
{
users.Add(new UserInfo { Name = "Python" });
}
private void BtnUpdate_Click(object sender, RoutedEventArgs e)
{
if(lbUsers.SelectedItems != null)
{
(lbUsers.SelectedItem as UserInfo)!.Name = "Golang";
}
}
private void BtnDelete_Click(object sender, RoutedEventArgs e)
{
if(lbUsers.SelectedItem != null)
{
users.Remove(lbUsers.SelectedItem as UserInfo);
}
}
}
public class UserInfo : INotifyPropertyChanged
{
private string? _name;
public string? Name
{
get { return _name; }
set {
_name = value;
OnPropertyChanged(nameof(Name));
}
}
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
类型转换器(自定义转换器)
加入输入yes,就和输入true一样
//使用方法,先放在资源里面,再在下面调用
<Window.Resources>
<local:BooleanConverter x:Key="booleanConverter"/>
Window.Resources>
<StackPanel Margin="20">
<TextBox Name="txtValue"/>
<WrapPanel Margin="0 20">
<TextBlock Text="输入的值为:"/>
<TextBlock Text="{Binding ElementName=txtValue,Path=Text,Converter={StaticResource booleanConverter}}"/>
WrapPanel>
<CheckBox Content="YES" IsChecked="{Binding ElementName=txtValue,Path=Text,Converter={StaticResource booleanConverter}}"/>
StackPanel>
public partial class BindingConverter : Window
{
public BindingConverter()
{
InitializeComponent();
}
///
/// 编写boolean类型的自定义转换器
///
public class BooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//获取绑定的值
var val = value.ToString()!.ToLower();
switch(val)
{
case "yes":
return true;
case "no":
return false;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
IDataErrorInfo绑定数据校验
<Window.Resources>
<local:StudentInfo x:Key="StudentValidator"/>
<Style TargetType="TextBox">
"Background" Value="White"/>
"Foreground" Value="Black"/>
"Validation.HasError" Value="True">
"Background" Value="#ddd"/>
"Foreground" Value="Red"/>
"ToolTip" Value="{Binding RelativeSource={RelativeSource Self},Path=(Validation.Errors)[0].ErrorContent}"/>
Style>
Window.Resources>
<StackPanel Margin="30">
<StackPanel.DataContext>
<Binding Source="{StaticResource StudentValidator}"/>
StackPanel.DataContext>
<WrapPanel>
<TextBlock Text="学生姓名:" Margin="0 0 10 0"/>
<TextBox Name="txtStuName" Text="{Binding StuName,ValidatesOnDataErrors=True}" Width="120"/>
WrapPanel>
<WrapPanel Margin="0 10 0 10">
<TextBlock Text="考试分数:" Margin="0 0 10 0"/>
<TextBox Name="txtScore" Width="120">
<TextBox.Text>
<Binding Path="StuScore" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<DataErrorValidationRule/>
Binding.ValidationRules>
Binding>
TextBox.Text>
TextBox>
WrapPanel>
StackPanel>
创建一个StudentInfo类
public class StudentInfo:IDataErrorInfo
{
public StudentInfo()
{
StuName = "Tester";
StuScore = 78;
}
public string this[string columnName]
{
get
{
string result = null;
switch(columnName)
{
case "StuName":
// 设置学生姓名校验规则
var len = StuName!.Length;
if (len <= 4 || len >= 10)
result = "姓名长度必须在4~10位字符";
break;
case "StuScore":
if (StuScore < 0 || StuScore > 150)
result = "分数的取值必须是0~150之间的数字";
break;
}
return result;
}
}
public string? StuName { get; set; }
public double StuScore { get; set; }
public string Error => throw new NotImplementedException();
}
public partial class DataVaildateDemo : Window
{
public DataVaildateDemo()
{
InitializeComponent();
this.DataContext = new StudentInfo();
}
}
StringFormat数据格式化
类似C#直接写format
绑定数据调试
数据绑定的四种模式介绍
WPF的绑定模式是枚举的 枚举值共有5个
1、OneWay(源变就更新目标属性)
2、TwoWay(源变就更新目标并且目标变得更新源)
3、OneTime(值根据源来设置目标,以后都不会变)
4、OneWayToSource(与OneWay相反)
5、Default(可以单向或双向,是靠被值定的源或目标是否有get或set来指定的)
所以绑定的话是需要选上面5个中的一个模式的,根据你的需求来选择,不选的话就会默认自动选择第5个。
OneTime
<StackPanel>
<TextBox Text="{Binding Name,Mode=OneTime}"/>
<Button Content="修改Name的值" Click="Button_Click"/>
<Button Content="获取Name的值" Click="Button_Click_1"/>
StackPanel>
public partial class OneTimeBinding : Window
{
PersonData source = new PersonData();
public OneTimeBinding()
{
InitializeComponent();
this.DataContext = source;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//修改源数据
source.Name = "Update Name Value";
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
MessageBox.Show($"Name Value: {source.Name}");
}
}
创建了一个PersonData的实体类
public class PersonData : INotifyPropertyChanged
{
public PersonData()
{
Name = "Gerry";
}
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
}
}
private string? _name;
public string? Name
{
get { return _name; }
set {
_name = value;
OnPropertyChanged(nameof(Name));
}
}
}
OneWay
直接修改上面的代码,将OneTime修改成OneWay
TwoWay
OneWayToSource
练习:接收TextBox里面的数据
<StackPanel>
<WrapPanel Margin="10">
<TextBlock Text="请输入标题: "/>
<TextBox Name="txtWindowTitle" Width="150"/>
<Button Content="复制到下一行" Name="NextTitle" Click="NextTitle_Click"/>
WrapPanel>
<WrapPanel Margin="10">
<TextBlock Text="这是复制的这行: "/>
<TextBox Name="copyWindowTitle" Text="{Binding Txt}" Width="150"/>
WrapPanel>
StackPanel>
public partial class TestFront : Window
{
Information information= new Information();
public TestFront()
{
InitializeComponent();
this.DataContext = information;
}
private void NextTitle_Click(object sender, RoutedEventArgs e)
{
information.Txt = txtWindowTitle.Text;
}
}
public class Information : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
private string? _txt;
public string? Txt
{
get { return _txt; }
set {
_txt = value;
OnPropertyChanged(nameof(Txt));
}
}
}