WPF 零基础入门笔记(3):数据绑定详解

文章合集

WPF基础知识博客专栏
WPF微软文档
WPF控件文档

B站对应WPF数据绑定视频教程

数据绑定

我们在之前的文章中,详细解释了数据模版和控件模板。简单来说数据模板和控件模板就是为了解决代码重复的问题。我们可以回顾一下之前的所有内容。

  • 为了不写重复的样式,WPF提供了样式设置
  • 为了减少业务代码和界面之间的沟通,WPF将简单的交互逻辑设计到了触发器
  • 为了重复使用复杂控件,WPF开发了控件模板
    • 控件模板又分为
    • 多标签复合
    • 标签元素拆分
    • 标签子项重写

这个时候,为了数据的高效绑定,为了实现事件驱动到数据驱动的转变,我们要进行数据绑定。

数据绑定实战

在这里插入图片描述
基础样式

        <StackPanel>
            <Slider x:Name="sd" Width="200" />
            <TextBox x:Name="txt" HorizontalAlignment="Center"  Text="50"/>
        </StackPanel >

事件通知型

在这里插入图片描述

xml代码

        <StackPanel>
            <Slider x:Name="sd" Width="200" ValueChanged="sd_ValueChanged"/>
            <TextBox x:Name="txt" HorizontalAlignment="Center"  Text="50"/>
        </StackPanel >

cs代码

        private void sd_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
    
    
            txt.Text = e.NewValue.ToString();
        }

实现效果

在这里插入图片描述

数据驱动,双向绑定

我们这里还是事件驱动,就是一个滑块移动事件来设置属性。WPF为了将这种简单的交互脱离业务逻辑,我们可以直接页面元素之间进行数据绑定。

        <StackPanel>
            <Slider x:Name="sd" Width="200" />
            <TextBox x:Name="txt" HorizontalAlignment="Center"  Text="{Binding ElementName=sd, Path=Value}"/>
        </StackPanel >

这个代码实现了双向绑定,而且没有在CS代码中写逻辑。这个是纯数据驱动事件。让我们的业务代码专心于业务。

在这里插入图片描述
在这里插入图片描述

功能
Default 同TowWay。双向绑定
OneTime 只响应一次
OneWay 单向正绑定
OneWayToSource 单向负绑定
TwoWay 双向绑定

这里推荐大家用双向绑定,而且大部分业务也是双向绑定的业务。如果有特殊需求再更改。

资源绑定

我们还可以绑定静态资源

<Window.Resources>
        <TextBox x:Key="txt">Hello WPF!</TextBox>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <TextBox FontSize="50" Text="{Binding Source={StaticResource txt},Path=Text}"/>
        </StackPanel >
    </Grid>

在这里插入图片描述

数据源绑定

我们之前都是前端的数据交互,我们希望直接绑定后端数据。通过使用dataContext来实现

        <StackPanel>
            <TextBox x:Name="txt" FontSize="50" Text="{Binding Name}"/>
        </StackPanel >
 public partial class MainWindow : Window
    {
    
    
        public MainWindow()
        {
    
    
            InitializeComponent();

            ///获取前端名为txt的数据,传入数据源
            txt.DataContext = new Person()
            {
    
    
                Name = "Hello C#"
            };


        }

    }
     public class Person
    {
    
    
        public string Name {
    
     get; set; }
    }

在这里插入图片描述
注意,这里一定是传入一个类,这样才能绑定这个类的某个值。而且不能用已经存在的关键字命名。

全局数据源

我们一个一个绑定数据源太麻烦,所以我们可以设置全局数据源,把整个窗体的数据源都绑定上去。
新建一个MainViewModel类
在这里插入图片描述

    public class MainViewModel
    {
    
    
        public MainViewModel() {
    
    
            Name = "Hello WPF!";
        }
        public string Name {
    
     get; set; }
    }

在MainWindow.xmal.cs中引入MainViewModel


    public partial class MainWindow : Window
    {
    
    
        public MainWindow()
        {
    
    
            InitializeComponent();
            //给整个窗口设置数据源
            this.DataContext = new MainViewModel();
        }

    }

直接绑定

    <Grid>
        <StackPanel>
            <TextBox FontSize="50"
                     Text="{Binding Name}" />
            <TextBox FontSize="50"
                     Text="{Binding Name}" />
            <TextBox FontSize="50"
                     Text="{Binding Name}" />

            <TextBox FontSize="50"
                     Text="{Binding Name}" />

        </StackPanel >
    </Grid>

在这里插入图片描述

后端和前端绑定问题

这里是单向绑定,后端代码更新是不影响前端的,要主动渲染。前端代码更新影响后端。

前端不重新渲染示例代码

    <Grid>
        <StackPanel>
            <TextBox FontSize="50"
                     Text="{Binding Name}" />
            <TextBox FontSize="50"
                     Text="{Binding Name}" />
            <TextBox FontSize="50"
                     Text="{Binding Name}" />

            <TextBox FontSize="50"
                     Text="{Binding Name}" />
            <Button Click="Button_Click" Height="100" FontSize="50" Content="按钮"/>

        </StackPanel >
    </Grid>
    public partial class MainWindow : Window
    {
    
    
        MainViewModel data = new MainViewModel();

        public MainWindow()
        {
    
    
            InitializeComponent();
            //给整个窗口设置数据源
            this.DataContext = data;



        }
		//我们添加按钮事件同时打印值
        private void Button_Click(object sender, RoutedEventArgs e)
        {
    
    
            data.Name = "data change!";
            MessageBox.Show(data.Name);
        }
    }

在这里插入图片描述

继承事件通知,刷新数据

在原来的基础上

    /// <summary>
    /// 继承INotifyPropertyChanged
    /// </summary>
    public class MainViewModel: INotifyPropertyChanged
    {
    
    
        public MainViewModel() {
    
    
            Name = "Hello WPF!";
        }


        /// <summary>
        /// 使用public类,不然无法双向绑定
        /// </summary>
        public string Name {
    
     get;set; }

        /// <summary>
        /// 重载事件类,不然会报错
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;


    }

成功改变

在这里插入图片描述

我们将MainWindow.xmal.cs的点击按钮改造一下,看看多次点击会不会改变


   public partial class MainWindow : Window
    {
    
    
        MainViewModel data = new MainViewModel();

        public MainWindow()
        {
    
    
            InitializeComponent();
            //给整个窗口设置数据源
            this.DataContext = data;



        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
    
    
            data.Name += "!";
            MessageBox.Show(data.Name);
        }
    }

事件通知强制刷新(无效)

我把代码修改了一下,发现并不是强制通知,而是触发了事件,或者有点点击事件之后才会触发通知

MainViewModel代码

        public MainViewModel() {
    
    
            Name = "Hello WPF!";
            Task.Run(async () =>
            {
    
    
                await Task.Delay(3000);
                Name = "I have been changed!";

                MessageBox.Show(Name);

            });
        }


        /// <summary>
        /// 使用public类,不然无法双向绑定
        /// </summary>
        public string Name {
    
     get;set; }

        /// <summary>
        /// 重载事件类,不然会报错
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

在这里插入图片描述
如果你鼠标不动,则最多停留10s钟才会刷新,如果鼠标动一动就立刻刷新

 public class MainViewModel: INotifyPropertyChanged
    {
    
    
        public MainViewModel() {
    
    
            Name = "Hello WPF!";
            //Name = "I have been changed!";

            Task.Run(async () =>
            {
    
    
                await Task.Delay(3000);
                Name = "I have been changed!";

                //MessageBox.Show(Name);

            });
        }


        /// <summary>
        /// 使用public类,不然无法双向绑定
        /// </summary>
        private string _name;

        /// <summary>
        /// 当修改时触发
        /// </summary>
        public string Name
        {
    
    
            get {
    
     return _name; }
            set {
    
     _name = value; OnPropertyChanged("Name"); }
        }


        /// <summary>
        /// 重载事件类,不然会报错
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        /// <summary>
        /// 强制事件通知
        /// </summary>
        /// <param name="propertyName"></param>

        protected void OnPropertyChanged(string propertyName)
        {
    
    
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }


    }

结果还是一样。我估计是懒重载吧,就是WPF认为这个不是活动窗口,所以刷新频率低一点,这个其实是个优化吧,多窗口默认优化。

结论:

继承INotifyPropertyChanged,扩不扩展public属性,感觉效果差不多。出于简洁的目的,我们在WPF项目中就不用扩展了。

猜你喜欢

转载自blog.csdn.net/qq_44695769/article/details/131404494
今日推荐