wpf 双向绑定
十年河东,十年河西,莫欺少年穷
学无止境,精益求精
最近在知乎,看了很多提问,涉及到就业,裁员,经济等,不看不知道,越看越头疼,知乎上很多人提问
毕业生就业如何难,2023年裁员如何严重,35岁的中年危机,程序员被裁员后找不到工作该,经济如何差等话题
哎,这让我这个35岁的老程序员感到莫大的压力,我时常想,如果我离开了现在的公司,我能找到工作吗?多久能找到工作?如果找不到工作,我能干什么?
35岁,本是经验技术相对比较成熟的阶段,更是上有老下有小的年龄段,也是花钱多的时候,如果在这个年龄段没了收入,该多么绝望......,但没办法,世界就是如此,它不会顾及你的感受,生存法则而已
言归正传,在开始正文之前,小伙伴们看下知乎上提的一个话题:35岁了,还有必要继续卷技术吗? 你们认为呢?
本来鄙人也不打算继续内卷了,但中年危机的压迫感,迫使我不得不拿起手中的枪,继续战斗!我本是做web开发的,但C#语言似乎CS开发岗位更多,而且似乎不受年龄限制,大不了40岁进厂做工控机开发呗,再说了,厂里妹子不是多么,挺好的
1、事件驱动模式
这种方式类似于winform,不推荐使用
新建如下页面
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:HandyControl.Controls;assembly=HandyControl"
xmlns:local="clr-namespace:WpfApp"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="1080">
<Window.Resources>
<Style TargetType="Button" x:Key="baseStl">
<Setter Property="Width" Value="200"/>
<Setter Property="Height" Value="80"/>
<Setter Property="Background" Value="Yellow"/>
<Setter Property="FontSize" Value="22"/>
</Style>
<Style TargetType="Button" x:Key="Butn" BasedOn="{StaticResource baseStl}">
<Setter Property="Content" Value="Btu"/>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<Slider x:Name="slider" Maximum="100" Minimum="0" ValueChanged="Slider_ValueChanged" Margin="5" Foreground="red" ></Slider>
<TextBox x:Name="textbox1" Margin="5" Height="30" TextChanged="TextBox_TextChanged"/>
<TextBox x:Name="textbox2" Margin="5" Height="30" TextChanged="TextBox_TextChanged" />
<Button x:Name="button" Style="{StaticResource Butn}" Command="{Binding BtnCommand}" Width="100" Height="100" Content="点我" />
</StackPanel>
</Grid>
</Window>
View Code
注意:Slider_ValueChanged 事件 及 TextBox_TextChanged 事件
代码如下:
private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
textbox1.Text = slider.Value.ToString();
textbox2.Text = slider.Value.ToString();
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (double.TryParse(textbox1.Text, out double result))
{
slider.Value = result;
}
}
View Code
这种模式最大的缺点是前后端混合在一起,不方便维护。下面介绍第二种方式,控件驱动控件。
2、控件驱动控件
新建如下页面
此时后台事件代码去掉,前端驱动事件去掉,xaml如下:
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:HandyControl.Controls;assembly=HandyControl"
xmlns:local="clr-namespace:WpfApp"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="1080">
<Window.Resources>
<Style TargetType="Button" x:Key="baseStl">
<Setter Property="Width" Value="200"/>
<Setter Property="Height" Value="80"/>
<Setter Property="Background" Value="Yellow"/>
<Setter Property="FontSize" Value="22"/>
</Style>
<Style TargetType="Button" x:Key="Butn" BasedOn="{StaticResource baseStl}">
<Setter Property="Content" Value="Btu"/>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<Slider x:Name="slider" Maximum="100" Minimum="0" Margin="5" Value="{Binding ElementName=textbox1,Path=Text,Mode=TwoWay}" ></Slider>
<TextBox x:Name="textbox1" Margin="5" Height="30" Text="{Binding ElementName=slider,Path=Value,Mode=TwoWay}" />
<Button x:Name="button" Style="{StaticResource Butn}" Command="{Binding BtnCommand}" Width="100" Height="100" Content="点我" />
</StackPanel>
</Grid>
</Window>
View Code
重点解读
<Slider x:Name="slider" Maximum="100" Minimum="0" Margin="5" Value="{Binding ElementName=textbox1,Path=Text,Mode=TwoWay}" ></Slider>
及
<TextBox x:Name="textbox1" Margin="5" Height="30" Text="{Binding ElementName=slider,Path=Value,Mode=TwoWay}" />
Slider 的 value 的取值为绑定模式,绑定的是元素Element的Name为 textbox1,取的是textbox1的text值,Mode模式为双向绑定绑定,(WPF默认模式为单向绑定模式)
TextBox 的 Text 的取值为绑定模式,绑定的是元素Element的Name为 slider,取的是slider的value值,Mode模式为双向绑定绑定,(WPF默认模式为单向绑定模式)
3、数据驱动模式(数据上下文模式)
需求要求:进度条初始值为20,点击按钮后,进度条的值变为88,且弹框提示
3.1、按钮点击需要用到Wpf的Command命令
新建BaseCommand类,并实现接口 ICommand
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace WpfApp.Models
{
public class BaseCommand : ICommand
{
public Action action;
public BaseCommand(Action action)
{
this.action = action;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
action();
}
}
}
View Code
3.2、先建viewModel如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Windows.Input;
using System.Messaging;
using System.Windows;
namespace WpfApp.Models
{
public class MainWindowModel : INotifyPropertyChanged
{
public ICommand BtnCommand { get; set; }
public MainWindowModel()
{
//进度条模式为20
this.wendu = 20;
BtnCommand = new BaseCommand(DoBtnCommand);
}
public void DoBtnCommand()
{
this.wendu = 88;
MessageBox.Show("进度条的值修改为88,进度条向前欢动了。");
}
private ushort _wendu;
public ushort wendu
{
get { return this._wendu; }
set
{
this._wendu = value;
this.OnPropertyChanged("wendu");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
View Code
3.3、修改xaml的数据上下文
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowModel();
}
3.4、xaml如下
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:HandyControl.Controls;assembly=HandyControl"
xmlns:local="clr-namespace:WpfApp"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="1080">
<Window.Resources>
<Style TargetType="Button" x:Key="baseStl">
<Setter Property="Width" Value="200"/>
<Setter Property="Height" Value="80"/>
<Setter Property="Background" Value="Yellow"/>
<Setter Property="FontSize" Value="22"/>
</Style>
<Style TargetType="Button" x:Key="Butn" BasedOn="{StaticResource baseStl}">
<Setter Property="Content" Value="Btu"/>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<Slider x:Name="slider" Maximum="100" Minimum="0" Margin="5" Value="{Binding wendu}" ></Slider>
<TextBox x:Name="textbox1" Margin="5" Height="30" Text="{Binding wendu}" />
<Button x:Name="button" Style="{StaticResource Butn}" Command="{Binding BtnCommand}" Width="100" Height="100" Content="点我" />
</StackPanel>
</Grid>
</Window>
View Code
3.5、另一种实现模式(主要是Command实现类)
ICommand的实现类(通过属性赋值Action委托,上面方法是通过构造函数赋值Action委托)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace WpfApp.Models
{
/// <summary>
/// 属性赋值action模式
/// </summary>
public class CommandBase : ICommand
{
public event EventHandler CanExecuteChanged;
/// <summary>
/// 是否可执行
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
action.Invoke(parameter);
}
public Action<object> action { get; set; }
}
}
View Code
viewModel如下
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace WpfApp.Models
{
public class MainWindowMod : INotifyPropertyChanged
{
//新建按钮点击事件
public ICommand BtnCommand { get; set; }
public MainWindowMod()
{
this.wendu = 20;
//执行委托
BtnCommand = new CommandBase() { action = new Action<object>(DoBtnCommand) };
}
public void DoBtnCommand(object obj)
{
this.wendu = 88;
MessageBox.Show("进度条的值修改为88,进度条向前欢动了。");
}
private ushort _wendu;
public ushort wendu
{
get { return this._wendu; }
set
{
this._wendu = value;
this.OnPropertyChanged("wendu");
}
}
/// <summary>
/// 用于双向绑定
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
View Code
优化如下:
如果每次都需要写 "wendu" ,有可能会写错
this.OnPropertyChanged("wendu");
优化代码如下
public class MainWindowMod : INotifyPropertyChanged
{
//新建按钮点击事件
public ICommand BtnCommand { get; set; }
public MainWindowMod()
{
this.wendu = 20;
//执行委托
BtnCommand = new CommandBase() { action = new Action<object>(DoBtnCommand) };
}
public void DoBtnCommand(object obj)
{
this.wendu = 88;
MessageBox.Show("进度条的值修改为88,进度条向前欢动了。");
}
private ushort _wendu;
public ushort wendu
{
get { return this._wendu; }
set
{
this._wendu = value;
this.OnPropertyChanged();
}
}
/// <summary>
/// 用于双向绑定
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName="")
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
View Code
@天才卧龙的波尔卡