C# WPF INotifyPropertyChanged和ICommand, MVVM

一、案例1(.Net Framework)

1.MainWindow.xaml中

<Window
     <!--添加-->
     xmlns:vm="clr-namespace:INotifyPropertyChanged和ICommand.ViewModel"
/>
<Window.DataContext>
     <vm:MainWindowViewModel/>
 </Window.DataContext>
 
 <Grid>
     <StackPanel HorizontalAlignment="Left">
         <TextBlock Text="名称:" />
         <TextBox Text="{Binding MyName}"/>
         <TextBlock Text="{Binding MyName}"/>

         <TextBlock Text="年龄:"/>
         <TextBox Text="{Binding Age}"/>
         <TextBlock Text="{Binding Age}"/>

         <Button 
             Width="100" 
             Height="30" 
             Content="更改" 
             Command="{Binding SaveCommand}"/>
         <!--  把业务逻辑从.cs中转换到VM中, Binding一个命令,相当于传统的事件  -->
         <!--  Command属性类型是ICommand,所以SaveCommand必须是ICommand类型  -->
     </StackPanel>
 </Grid>

2.ViewModel文件夹中

1)MainWindowViewModel
 public class MainWindowViewModel:ViewModelBase
 {
     public List<Student> Students { get; set; }

     private string myName = "张三";
     public string MyName
     {
         // get获取,主要负责把Model中的数据,给视图渲染。
         get { return myName; }
         
          // set设置,用户在视图上搜集新数据,去更新Model,Model更新之后,会重新通知视图重新渲染
         set { myName = value; OnPropertyChanged(); }
     }

     private int age = 20;
     public int Age
     {
         get { return age; }
         set { 
               age = value; 
               OnPropertyChanged(); // 数据变化,通知视图去重新渲染
             }
     }

     //构造函数中建立 SaveCommand和Save 的联系,并不是让Save去执行的,所以RelayCommand参数不能使用Save()
      //用户点击XAML中的按钮的时候才执行Save
     public MainWindowViewModel()
     {
         SaveCommand = new RelayCommand(Save);
     }

       // 为什么SaveCommand没有set?因为直接在VM构造函数中给SaveCommand赋值了,其他位置不需要给SaveCommand赋值,不需要set
     public ICommand SaveCommand { get;}

     // 不需要对外公开,因为XAML中不直接调用Save(),而是调用SaveCommand
     private void Save()
     {
         MessageBox.Show("更改成功!");
     }
 }
2)ViewModelBase
 // ViewModelBase所有ViewModel的基类,此基类实现了INotifyPropertyChanged接口,将来每个ViewModel继承此基类,就实现了INotifyPropertyChanged接口
public class ViewModelBase:INotifyPropertyChanged
{

    // 事件是委托实例   PropertyChangedEventHandler委托, 主要负责通知
    public event PropertyChangedEventHandler PropertyChanged;
   

    // propertyName属性名,表示是哪个属性发生了变化
    public void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
3)RelayCommand
public class RelayCommand:ICommand
{
    public RelayCommand(Action action)
    {
        CanExecuteChanged += (sender, e) => { action(); };
    }
    public event EventHandler CanExecuteChanged;
    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        if (CanExecuteChanged != null)
            CanExecuteChanged(this, null);
    }
}

二、案例2(.Net Core)

1.App.config 配置字符串,连接数据库

<configuration>
	<connectionStrings>
		<add name="connString" connectionString="server=.;database=db_INPC;uid=sa;pwd=123456;" providerName="System.Data.SqlClient"/>
	</connectionStrings>	
</configuration>

2.MainWindow.xaml 主窗体

<!--<Window>标签中添加-->
 xmlns:vm="clr-namespace:INotifyPC和ICommand案例.ViewModel"
 d:DataContext="{d:DesignInstance vm:MainViewModel}"
 WindowStartupLocation="CenterScreen"
 WindowStyle="SingleBorderWindow"

<!--上下文-->
<Window.DataContext>
    <vm:MainViewModel/>
</Window.DataContext>

<!--渐变色背景-->
<Window.Background>
    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
        <GradientStop Color="#9db2dc"   Offset="0"/>
        <GradientStop Color="#0e2d6c"  Offset="1"/>
    </LinearGradientBrush>
</Window.Background>


<Window.Resources>
    <Style x:Key="VerticalAlignmentRowStyle" TargetType="DataGridRow">
        <Setter Property="VerticalContentAlignment" Value="Center"/>
    </Style>
</Window.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="60"/>
        <RowDefinition/>
    </Grid.RowDefinitions>

    <WrapPanel VerticalAlignment="Center">
        <TextBlock 
            VerticalAlignment="Center" 
            Text="   用户名:"/>
        <TextBox
            Width="100"
            Height="30"
            Text="{Binding UserName}"
            VerticalContentAlignment="Center"
            Style="{StaticResource DefaultTextBox}"/>

        <Button   
            Background="CornflowerBlue"
            Content="查询"
            Command="{Binding SerachCommand}"
            Style="{StaticResource TwoButton}"/>
        <Button
            Background="CornflowerBlue"
            Content="添加"
            Command="{Binding OpenCommand}"
            Style="{StaticResource TwoButton}"/>
    </WrapPanel>

    <DataGrid
        Grid.Row="1"
        AutoGenerateColumns="False"
        IsReadOnly="True"
        ItemsSource="{Binding Users}"
        Background="Transparent"
        GridLinesVisibility="None"
        RowHeight="30"
        RowHeaderWidth="30"
        >

        <!--定义表头样式-->
        <DataGrid.ColumnHeaderStyle>
            <Style TargetType="DataGridColumnHeader">
                <Setter Property="HorizontalContentAlignment" Value="Center"/>
                <Setter Property="VerticalContentAlignment" Value="Center"/>
                <Setter Property="Height" Value="40"/>
            </Style>
        </DataGrid.ColumnHeaderStyle>

        <DataGrid.Columns>
            <DataGridTextColumn
                Width="100"
                Header="编号"
                Binding="{Binding Id}"
                ElementStyle="{StaticResource DataGridTextBlock}"/>
            <DataGridTextColumn
                Width="*"
                Header="用户名"
                Binding="{Binding UserName}"
                ElementStyle="{StaticResource DataGridTextBlock}"/>
            <DataGridTextColumn
                Width="*"
                Header="密码"
                Binding="{Binding Password}"
                ElementStyle="{StaticResource DataGridTextBlock}"/>
            <DataGridTemplateColumn Header="操作" Width="*">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <WrapPanel VerticalAlignment="Center" HorizontalAlignment="Center">
                            <Button
                                Background="CornflowerBlue"
                                Command="{Binding DataContext.DeleteCommand,RelativeSource={RelativeSource AncestorType=Window}}"
                                CommandParameter="{Binding RelativeSource={RelativeSource self}}"
                                Tag="{Binding Id}"
                                Style="{StaticResource DefaultButton}"
                                >删除</Button>
                            <Button 
                                Background="CornflowerBlue"
                                Command="{Binding DataContext.EditCommand,RelativeSource={RelativeSource AncestorType=Window}}"
                                CommandParameter="{Binding Id}"
                                Style="{StaticResource DefaultButton}"
                                >编辑</Button>
                        </WrapPanel>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</Grid>

3.App.xaml 全局资源

 <Application.Resources>
     <!--单元格内文本样式-->
     <Style x:Key="DataGridTextBlock" TargetType="TextBlock">
         <Setter Property="VerticalAlignment" Value="Center"/>
         <Setter Property="HorizontalAlignment" Value="Center"/>
         <Setter Property="Padding" Value="10,0,0,0"/>
     </Style>

     <!--TextBox标签的样式-->
     <Style x:Key="DefaultTextBox" TargetType="TextBox">
         <Style.Resources>
             <Style TargetType="{x:Type Border}">
                 <Setter Property="CornerRadius" Value="15"/>
             </Style>
         </Style.Resources>
     </Style>

     <!--查询,添加按钮样式-->
     <Style x:Key="TwoButton" TargetType="Button">
         <Style.Resources>
             <Style TargetType="{x:Type Border}">
                 <Setter Property="CornerRadius" Value="15"/>
                 <Setter Property="Width" Value="80"/>
                 <Setter Property="Height" Value="30"/>
                 <Setter Property="Margin" Value="15"/>
             </Style>
         </Style.Resources>
     </Style>

     <!--Button标签的样式-->
     <Style x:Key="DefaultButton" TargetType="Button">
         <Style.Resources>
             <Style TargetType="{x:Type Border}">
                 <Setter Property="CornerRadius" Value="10"/>
                 <Setter Property="Width" Value="70"/>
                 <Setter Property="Height" Value="20"/>
             </Style>
         </Style.Resources>
     </Style>
 </Application.Resources>

4.Model模型文件夹 User

public class User
{
    public int Id { get; set; }
    public string UserName { get; set; } = string.Empty;
    public string Password { get; set; } = string.Empty;
}

5.ViewModel 视图模型文件夹

1)MainViewModel
 public class MainViewModel : ViewModelBase
 {
     UserService userService = new UserService();

     public MainViewModel()
     {
         Users = userService.GetUsers();
         OpenCommand = new RelayCommand(Open);   
         SerachCommand = new RelayCommand(Serach);
         DeleteCommand = new RelayCommand(Delete);
         EditCommand = new RelayCommand(Edit);
     }

     private List<User> users = new List<User>();
     public List<User> Users
     {
         get { return users; }
         set { users = value; OnPropertyChanged(); }
     }

     private string userName;
     public string UserName
     {
         get { return userName; }
         set { userName = value; OnPropertyChanged(); }
     }

     public ICommand OpenCommand { get; }
     private void Open(object? obj = null)
     {
         // 毛病:VM对某个Window窗体强依赖
         UserAdd win = new UserAdd();
         if(win.ShowDialog() == true)
         {
             Users = userService.GetUsers();
         }
     }

     public ICommand SerachCommand { get; }
     private void Serach(object? obj = null)
     {
         string strWhere = "";
         if (!string.IsNullOrWhiteSpace(UserName))
         {
             strWhere = $" and UserName like '%{UserName}%' ";
         }
         Users = userService.GetUsers(strWhere);
     }

     public ICommand DeleteCommand { get; }
     private void Delete(object obj)
     {
         MessageBoxResult mbr = MessageBox.Show("确定要删除吗?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Question);
         if(mbr == MessageBoxResult.Yes)
         {
             Button btnDelete = (Button)obj;
             int id = (int)btnDelete.Tag;

             if(userService.Delete(id))
             {
                 Users = userService.GetUsers();
             }
         }
     }

     public ICommand EditCommand { get; }
     private void Edit(object obj)
     {
         int id = (int)obj;
         UserEditViewModel vm = new UserEditViewModel(id);
         UserEdit win = new UserEdit();
         win.DataContext = vm; // 给窗体绑定上下文
         if (win.ShowDialog() == true)
         {
             Users = userService.GetUsers();
         }
     }
 }
2)ViewModelBase 视图模型基类

实现INotifyPropertyChanged接口,以便在属性变化时通知View:

 public class ViewModelBase : INotifyPropertyChanged
 {
     public event PropertyChangedEventHandler? PropertyChanged;

     public void OnPropertyChanged([CallerMemberName]string? propertyName = null)
     {
         if(PropertyChanged != null) 
             PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
     }
 }
3)RelayCommand
 public class RelayCommand : ICommand
 {
     private readonly Action<object> _execute;
     private readonly Func<object, bool> _canExecute;
     public event EventHandler? CanExecuteChanged;

     public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
     {
         _execute = execute;
         _canExecute = canExecute;
         CanExecuteChanged = null;
     }

     public bool CanExecute(object? parameter)
     {
         return _canExecute == null || (parameter != null ? _canExecute(parameter) : true);
     }

     public void Execute(object? parameter)
     {
         if (_execute != null)
             _execute(parameter != null ? parameter : "");
         else
             if (CanExecuteChanged != null) CanExecuteChanged(null, new EventArgs());
     }
 }

6.Service文件夹  实现增删查改逻辑

1)UserService
 public class UserService : IUserService
 {
     string connString = ConfigurationManager.ConnectionStrings["connString"].ConnectionString;
     public bool Add(User user)
     {
         using (SqlConnection conn = new SqlConnection(connString))
         {
             conn.Open();// 打开连接
             var sql = "insert into [User] (UserName,[Password]) values(@UserName,@Password)"; // SQL 插入语句
             int row = conn.Execute(sql, user);// 执行 SQL 语句
             return row > 0;
         }
     }

     public bool Delete(int id)
     {
         using (SqlConnection conn = new SqlConnection(connString))
         {
             conn.Open();
             var sql = "delete form [User] where Id=@Id";
             int row = conn.Execute(sql, id);
             return row > 0;
         }
     }
     public bool Update(User user)
     {
         using (SqlConnection conn = new SqlConnection(connString))
         {
             conn.Open();
             var sql = "update [User] set UserName=@UserName,[Password]=@Password where Id=@Id"; 
             int row = conn.Execute(sql, user);
             return row > 0;
         }
     }

     public List<User> GetUsers(string? strWhere="")
     {
         using(SqlConnection conn = new SqlConnection(connString))
         {
             conn.Open ();
             var sql = "select * from [User] where 1=1  ";
             if (!string.IsNullOrWhiteSpace(strWhere)) sql += strWhere;
             var uaers = conn.Query<User>(sql).ToList();// 执行查询并转换为对象列表
             return uaers;
         }
     }
    
 }
 2)IUserService
public interface IUserService
{
    /// <summary>
    /// 添加一个用户
    /// </summary>
    /// <param name="user">user模型实例</param>
    /// <returns>是否添加成功</returns>
    bool Add(User user);

    /// <summary>
    /// 修改一个用户
    /// </summary>
    /// <param name="user">user模型实例</param>
    /// <returns>是否修改成功</returns>
    bool Update(User user);

    /// <summary>
    /// 删除一个用户
    /// </summary>
    /// <param name="user">id主键</param>
    /// <returns>是否删除成功</returns>
    bool Delete(int id);

    /// <summary>
    /// 查询用户
    /// </summary>
    /// <param name="strWhere">查询条件</param>
    /// <returns>用户列表</returns>
    List<User> GetUsers(string strWhere);
}

7. UserAdd.xaml 添加页面的窗体

<!--<Window>标签中添加-->
 xmlns:vm="clr-namespace:INotifyPC和ICommand案例.ViewModel"
 Name="UserAddWindow"

<Window.DataContext>
    <vm:UserAddViewModel/>
</Window.DataContext>

<Grid>
    <StackPanel>
        <WrapPanel>
            <Label Content="用户:"/>
            <TextBox Width="100" Text="{Binding UserName}"/>
        </WrapPanel>
        <WrapPanel>
            <Label Content="密码:"/>
            <TextBox Width="100" Text="{Binding Password}"/>
        </WrapPanel>
        <WrapPanel>
            <Button 
                Command="{Binding SaveCommand}" 
                CommandParameter="{Binding ElementName=UserAddWindow}" 
                Content="添加"/>
        </WrapPanel>
    </StackPanel>
</Grid>

8.UserEdit.xaml 修改页面的窗体

<!--<Window>标签中添加-->
 xmlns:vm="clr-namespace:INotifyPC和ICommand案例.ViewModel"
 d:DataContext="{d:DesignInstance vm:UserAddViewModel}"
 
<Window.DataContext>
     <vm:UserEditViewModel/>
 </Window.DataContext>
 <Grid>
     <StackPanel>
         <WrapPanel>
             <Label Content="用户名:" />
             <TextBox Width="100" Text="{Binding User.UserName}" />
         </WrapPanel>
         <WrapPanel>
             <Label Content="密码:" />
             <TextBox Width="100" Text="{Binding User.Password}" />
         </WrapPanel>
         <WrapPanel>
             <Button
         Command="{Binding SaveCommand}"
         CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
         Content="保存" />
         </WrapPanel>
     </StackPanel>
 </Grid>

9.ViewModel

1)UserAddViewModel
public class UserAddViewModel : ViewModelBase
{
    UserService userService = new UserService();
    public UserAddViewModel()
    {
        SaveCommand = new RelayCommand(Save);
    }

    private string userName;
    public string UserName
    {
        get { return userName; }
        set { userName = value;OnPropertyChanged(); }
    }

    private string password;
    public string Password
    {
        get { return password; }
        set { password = value; OnPropertyChanged(); }
    }

    public ICommand SaveCommand {  get; }
    private void Save(object obj)
    {
        if (string.IsNullOrEmpty(UserName))
        {
            MessageBox.Show("请填写用户名");
            return;
        }
        if (string.IsNullOrEmpty(Password))
        {
            MessageBox.Show("请填写密码");
            return;
        }

        User user = new User()
        {
            UserName = UserName,
            Password = Password
        };

        if(userService.Add(user))
        {
            var win = (UserAdd)obj;
            win.DialogResult = true;
        }
    }

}

2)UserEditViewModel

public class UserEditViewModel:ViewModelBase
{
    UserService userService = new UserService();
    public UserEditViewModel(int id)
    {
        User = userService.GetUsers($" and Id={id}")[0];
        SaveCommand = new RelayCommand(Save);
    }
    public UserEditViewModel(){ }

    #region 属性
    private User? user;
    public User? User
    {
        get { return user; }
        set { user = value; OnPropertyChanged(); }
    }
    #endregion

    #region 事件
    public ICommand? SaveCommand { get;}
    private void Save(object obj)
    {
        UserEdit win = (UserEdit)obj;   
        if(User != null)
        {
            if (userService.Update(User))
            {
                win.DialogResult = true;
            }
        }
    }
    #endregion
}

React Hooks 是 React 16.8 中新增的特性,它可以让你在函数组件中使用 state、生命周期钩子等 React 特性。使用 Hooks 可以让你写出更简洁、可复用且易于测试的代码。 React Hooks 提供了一系列的 Hook 函数,包括 useState、useEffect、useContext、useReducer、useCallback、useMemo、useRef、useImperativeHandle、useLayoutEffect useDebugValue。每个 Hook 都有特定的用途,可以帮助你处理不同的问题。 下面是 React Hooks 的一些常用 Hook 函数: 1. useState useState 是最常用的 Hook 之一,它可以让你在函数组件中使用 state。useState 接受一个初始状态值,并返回一个数组,数组的第一个值是当前 state 值,第二个值是更新 state 值的函数。 ``` const [count, setCount] = useState(0); ``` 2. useEffect useEffect 可以让你在组件渲染后执行一些副作用操作,比如订阅事件、异步请求数据等。useEffect 接受两个参数,第一个参数是一个回调函数,第二个参数是一个数组,用于控制 useEffect 的执行时机。 ``` useEffect(() => { // 这里可以执行副作用操作 }, [dependencies]); ``` 3. useContext useContext 可以让你在组件树中获取 context 的值。它接受一个 context 对象,并返回该 context 的当前值。 ``` const value = useContext(MyContext); ``` 4. useRef useRef 可以让你在组件之间共享一个可变的引用。它返回一个对象,该对象的 current 属性可以存储任何值,并在组件的生命周期中保持不变。 ``` const ref = useRef(initialValue); ref.current = value; ``` 5. useCallback useCallback 可以让你缓存一个函数,以避免在每次渲染时都创建一个新的函数实例。它接受一个回调函数一个依赖数组,并返回一个 memoized 的回调函数。 ``` const memoizedCallback = useCallback(() => { // 这里是回调函数的逻辑 }, [dependencies]); ``` 6. useMemo useMemo 可以让你缓存一个计算结果,以避免在每次渲染时都重新计算。它接受一个计算函数一个依赖数组,并返回一个 memoized 的计算结果。 ``` const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]); ``` 以上就是 React Hooks 的一些常用 Hook 函数,它们可以帮助你更好地处理组件状态、副作用、上下文性能优化等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值