WPF 数据绑定

一些总结

修改对象 = 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"/>
            <!--
                {binding ElementName=xxx,Path=Text}  ElementName 指的是绑定对象  Path 指的是绑定属性
                绑定属性必须是依赖属性,Text属性如何确定是依赖属性呢?
            -->
            <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="窗口的宽度为:"/>
            <!--  直接Binding Width 是一种简便方式-->
            <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界面的没有变化
        }
    }

    /// <summary>
    /// 创建user类  让源头(发生改变的源头)继承接口
    /// </summary>
    public class User : INotifyPropertyChanged
    {
        private string? _name;

        public string? Name
        {
            get { return _name; }
            set { 
                _name = value; 
                OnPropertyChanged(nameof(Name)); 		// 当值每次发生变化的时候
            }
        }


        public event PropertyChangedEventHandler? PropertyChanged;

        /// <summary>
        ///  当指定的某个属性发生变化的时候对界面UI的线程推送更新消息
        /// </summary>
        /// <param name="propertyName"></param>
        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<UserInfo> users = new List<UserInfo>();  
        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();
        }

        /// <summary>
        ///  编写boolean类型的自定义转换器
        /// </summary>
        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">
            <Setter Property="Background" Value="White"/>
            <Setter Property="Foreground" Value="Black"/>
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="True">
                    <Setter Property="Background" Value="#ddd"/>
                    <Setter Property="Foreground" Value="Red"/>
                    <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self},Path=(Validation.Errors)[0].ErrorContent}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <StackPanel Margin="30">
        <!--在里面指定了校验器规则-->
        <!--另一种引用校验的方法 下面可以注释-->
        <StackPanel.DataContext>
            <Binding Source="{StaticResource StudentValidator}"/>
        </StackPanel.DataContext>
        <WrapPanel>
            <TextBlock Text="学生姓名:" Margin="0 0 10 0"/>
            <!--写法1:一个筛选规则-->
            <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));
            }
        }

    }

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值