wpf MVVM -绑定按钮命令不起作用

a1o7rhls  于 2023-03-09  发布在  其他
关注(0)|答案(1)|浏览(251)

我正在使用MVVM模式开发一个WPF应用程序。但是,我遇到了一些问题,因为这是我第一次尝试使用MVVM模式。我有一个主登录视图,当我按下按钮时,没有任何React,我不知道为什么:
LoginView.xaml

<Window x:Class="Library.View.LoginView"
        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:local="clr-namespace:Library.View"
        xmlns:ViewModel="clr-namespace:Library.ViewModel" xmlns:customcontrol="clr-namespace:Library.CustomControl"
        mc:Ignorable="d"
        Title="LoginView" Height="550" Width="800"
        WindowStyle="None"
        ResizeMode="NoResize"
        WindowStartupLocation="CenterScreen"
        Background="Transparent"
        AllowsTransparency="True"
        MouseDown="Window_MouseDown">

    <Window.DataContext>
        <ViewModel:LoginViewModel/>
    </Window.DataContext>

    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BooleanToVisibility"/>
    </Window.Resources>
    
    <Window.Visibility>
        <Binding Path="IsViewVisible" Mode="TwoWay" Converter="{StaticResource BooleanToVisibility}"/>
    </Window.Visibility>
    

    
        <Border CornerRadius="12">
        <Border.Background>
            <ImageBrush ImageSource="/Image/back_image.jpg" />
        </Border.Background>

        <Border CornerRadius="12"
            BorderThickness="2" 
            Opacity="0.86">

            <Border.BorderBrush>
                
                <LinearGradientBrush StartPoint="0, 0" EndPoint="1,1">
                    <GradientStop Color="#462AD8" Offset="0"/>
                    <GradientStop Color="#DA34AE" Offset="0.75"/>
                    <GradientStop Color="#8A16C1" Offset="1"/>
                </LinearGradientBrush>
                
            </Border.BorderBrush>

            <Border.Background>
                
                <LinearGradientBrush StartPoint="0,1" EndPoint="1,0">
                    <GradientStop Color="#060531" Offset="0"/>
                    <GradientStop Color="#1B1448" Offset="1"/>
                </LinearGradientBrush>
                
            </Border.Background>

            <Grid>

                <Grid.RowDefinitions>
                    <RowDefinition Height="30"/>
                    <RowDefinition />
                </Grid.RowDefinitions>

                <Grid>

                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition Width="25"/>
                        <ColumnDefinition Width="25"/>
                        <ColumnDefinition Width="5"/>
                    </Grid.ColumnDefinitions>

                    <TextBlock Text="ZALOGUJ SIĘ"
                               Foreground="DarkGray"
                               FontSize="10"
                               FontFamily="Montserrat"
                              Grid.Column="0"
                               VerticalAlignment="Center"
                               Margin="10,0,0,0"/>

                    <Button x:Name="btnMinimalize"
                            
                            BorderThickness="0"
                            Content="-"
                            Foreground="White"
                            FontSize="16"
                            FontFamily="Montserrat"
                            Cursor="Hand"
                            Grid.Column="1"
                            Click="btnMinimalize_Click">
                        <Button.Style>
                            <Style TargetType="Button">
                                <Setter Property="Background" Value="#28AEED"/>
                                <Style.Triggers>
                                    <Trigger Property="IsMouseOver" Value="True">
                                        <Setter Property="Background" Value="#278BEF"/>
                                    </Trigger>
                                </Style.Triggers>
                            </Style>
                        </Button.Style>
                        
                        <Button.Template>
                            <ControlTemplate TargetType="Button">
                                <Border Width="18" Height="18"
                                        CornerRadius="9"
                                        Background="{TemplateBinding Background}">
                                    <ContentPresenter VerticalAlignment="Center"
                                                      HorizontalAlignment="Center"/>

                                </Border>
                            </ControlTemplate>
                        </Button.Template>
                        
                    </Button>
                    <Button x:Name="btnClosed"   
                            BorderThickness="0"
                            Content="X"
                            Foreground="White"
                            FontSize="12"
                            FontFamily="Montserrat"
                            Cursor="Hand"
                            Grid.Column="2"
                            Click="btnClosed_Click">
                        
                        <Button.Style>
                            <Style TargetType="Button">
                                <Setter Property="Background" Value="#DA34AE"/>
                                <Style.Triggers>
                                    <Trigger Property="IsMouseOver" Value="True">
                                        <Setter Property="Background" Value="#C62DAE"/>
                                    </Trigger>
                                </Style.Triggers>
                            </Style>
                        </Button.Style>

                        <Button.Template>
                            <ControlTemplate TargetType="Button">
                                <Border Width="18" Height="18"
                                        CornerRadius="9"
                                        Background="{TemplateBinding Background}">
                                    <ContentPresenter VerticalAlignment="Center"
                                                      HorizontalAlignment="Center"/>

                                </Border>
                            </ControlTemplate>
                        </Button.Template>
                    </Button>
                </Grid>

                <StackPanel Width="250"
                            Grid.Row="2"
                            Orientation="Vertical"
                            Margin="0,35,0,0">
                    <Image Source="/Image/Logo_image.jpg" Width="100" Height="100"/>
                    <TextBlock Text="Biblioteka Narodowa"
                               Foreground="White"
                               FontSize="25"
                               FontWeight="Medium"
                               FontFamily="Montserrat"
                               HorizontalAlignment="Center"/>
                    
                    <TextBlock Text="Nazwa użytkownika"
                               Foreground="DarkGray"
                               FontSize="12"
                               FontWeight="Medium"
                               FontFamily="Montserrat"
                               Margin="0,35,0,0"/>

                    <TextBox x:Name="txtUser"
                             Text="{Binding Username,UpdateSourceTrigger=PropertyChanged}"
                             FontSize="13"
                             FontWeight="Medium"
                             FontFamily="Montserrat"
                             Foreground="White"
                             CaretBrush="LightGray"
                             BorderBrush="DarkGray"
                             BorderThickness="0,0,0,1"
                             Height="28"
                             VerticalContentAlignment="Center"
                             Margin="0,5,0,0"
                             Padding="25,0,0,0">
                        
                        <TextBox.Background>
                            <ImageBrush ImageSource="/Image/user-solid.png" Stretch="Uniform" AlignmentX="Left">
                            </ImageBrush>
                        </TextBox.Background>
                        
                    </TextBox>

                    <TextBlock Text="Hasło"
                               Foreground="DarkGray"
                               FontSize="12"
                               FontWeight="Medium"
                               FontFamily="Montserrat"
                               Margin="0,15,0,0"/>

                    <customcontrol:BindablePassword Password="{Binding Password,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
                                                    Height="28"
                                                    Margin="0,5,0,0">
                        
                    </customcontrol:BindablePassword>

                    <TextBlock Text="{Binding ErrorMessage}"
                               Foreground="#D7596D"
                               FontSize="12"
                               FontWeight="Medium"
                               FontFamily="Montserrat"
                               Margin="0,15,0,0"
                               TextWrapping="Wrap"/>

                    <Button x:Name="btnLogin"   
                            Command="{Binding LoginComand}"
                            BorderThickness="0"
                            Content="Zaloguj"
                            Foreground="White"
                            FontSize="12"
                            FontFamily="Montserrat"
                            Cursor="Hand"
                            Margin="0,30,0,0" >

                        <Button.Style>
                            <Style TargetType="Button">
                                <Setter Property="Background" Value="#462AD8"/>
                                <Style.Triggers>
                                    <Trigger Property="IsMouseOver" Value="True">
                                        <Setter Property="Background" Value="#28AEED"/>
                                    </Trigger>
                                </Style.Triggers>
                            </Style>
                        </Button.Style>

                        <Button.Template>
                            <ControlTemplate TargetType="Button">
                                <Border Width="150" Height="40"
                                        CornerRadius="9"
                                        Background="{TemplateBinding Background}">
                                    <ContentPresenter VerticalAlignment="Center"
                                                      HorizontalAlignment="Center"/>

                                </Border>
                            </ControlTemplate>
                        </Button.Template>
                    </Button>
                    <StackPanel Orientation="Horizontal"
                                HorizontalAlignment="Center"
                                Margin="0,15,0,0">
                    <TextBlock Text="Zapomniałeś Hasła?"
                               Foreground="DarkGray"
                               FontSize="12"
                               FontWeight="Medium"
                               FontFamily="Montserrat"/>
                    
                    <TextBlock Text="Reset"
                               Foreground="DarkGray"
                               FontSize="12"
                               FontWeight="Medium"
                               FontFamily="Montserrat"
                               Cursor="Hand"
                               Margin="8,0,0,0"/>
                    </StackPanel>
                </StackPanel>
            </Grid>
        </Border>
    </Border>

</Window>

loginViewModel.cs

using Library.Model;
using Library.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;

namespace Library.ViewModel
{
    public class LoginViewModel : ViewModelBase
    {
        //Feilds
        private string _username;
        private SecureString _password;
        private string _errorMessage;
        private bool _isViewVisible=true;

        private IUserRepository userRepository;

        public string Username
        {
            get
            {
                return _username;
            }
            set
            {
                _username = value;
                OnPropertyChanged(nameof(Username));
            }
        }
        public SecureString Password 
        {
            get
            {
                return _password;
            }
            set
            {
                _password = value;
                OnPropertyChanged(nameof(Password));   
            }
        }
        public string ErrorMessage { 
            get
            {
                return _errorMessage;
            }
            set
            {
                _errorMessage = value;
                OnPropertyChanged(nameof(ErrorMessage));
            } 
        }
        public bool IsViewVisible 
        { 
            get
            {
                return _isViewVisible;  
            }
            set
            {
                _isViewVisible = value;
                OnPropertyChanged(nameof(IsViewVisible));
            } 
        }

        //->Commands
        public ICommand LoginComand { get;  }
        public ICommand RecoverPasswordCommand { get;  }
        public ICommand ShowPasswordCommand { get;  }
        public ICommand RememberPasswordCommand { get;  }


        //Constructor
        public LoginViewModel()
        {
            userRepository= new UserRepository();
            LoginComand = new ViewModelCommand(ExecuteLoginCommand, CanExecuteLoginCommand);
            RecoverPasswordCommand = new ViewModelCommand(p=>ExecuteRecoverPassCommand("",""));
        }


        private bool CanExecuteLoginCommand(object obj)
        {
            bool vaildDate;
            if (string.IsNullOrWhiteSpace(Username) || Username.Length < 3 || Password == null ||
                Password.Length < 3)
                vaildDate = false;
            else 
                vaildDate = true;
            return vaildDate;
        }

        private void ExecuteLoginCommand(object obj)
        {

            var isValidUser = userRepository.AuthenticateUser(new NetworkCredential(Username, Password));
            if (isValidUser)
            {
                Thread.CurrentPrincipal = new GenericPrincipal(
                    new GenericIdentity(Username), null);
              isValidUser= false;
            }
            else
            {
                ErrorMessage = "* Błąd w nazwie użtkownika lub błędne hasło";
            }
        }

        private void ExecuteRecoverPassCommand(string username,string email)
        {
            throw new NotImplementedException();
        }
    }
}

ViewModelbase.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;

namespace Library.ViewModel
{
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

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

ViewModelCommand.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace Library.ViewModel
{
    public class ViewModelCommand : ICommand
    {
        //Feilds
        private readonly Action<object> _executeAction;
        private readonly Predicate<object>? _canExecuteAction;

        //Constructos
        public ViewModelCommand(Action<object> executeAction)
        {
            _executeAction = executeAction;
            _canExecuteAction = null;
        }

        public ViewModelCommand(Action<object> executeAction, Predicate<object> canExecuteAction)
        {
            _executeAction = executeAction;
            _canExecuteAction = canExecuteAction;
        }

        //Events
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value;}
        }

        //Methods
        public bool CanExecute(object parameter) => _canExecuteAction == null ? true : _canExecuteAction(parameter);

        public void Execute(object parameter) => _executeAction(parameter);
    }
}

我检查了代码,看看是否有什么遗漏,我已经调整了DataContext,因为当我检查它,一切都通过了,没有错误显示,即使连接到数据库一切正常。我在App.xaml中更改了我的第一个窗口的调用,它在子条件下工作。
App.xaml

<Application x:Class="Library.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:Library"
             Startup="ApplicationStart">
    <Application.Resources>
         
    </Application.Resources>
</Application>

App.xaml.cs

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using Library.View;

namespace Library
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        protected void ApplicationStart(object sender, StartupEventArgs e)
        {
            var loginView= new LoginView();
            loginView.Show();
            loginView.IsVisibleChanged += (s, ev) =>
            {
                if (loginView.IsVisible == false && loginView.IsLoaded)
                {
                    var mainView = new MainWindow();
                    mainView.Show();
                    loginView.Close();
                }
            };
        }
    }
}
imzjd6km

imzjd6km1#

这里是一个最小的例子,演示了使用社区工具包mvvm修改命令和canexecutechanged。
我的主窗口:

<Window.DataContext>
    <local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
    <StackPanel>
        <TextBox Text="{Binding UserName, UpdateSourceTrigger=PropertyChanged}"/>
        <Button Content="Login"
                Command="{Binding LoginCommand}"/>
    </StackPanel>
</Grid>
</Window>

视图模型

public partial class MainWindowViewModel : ObservableObject
{
    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(LoginCommand))]
    private string userName = string.Empty;

    [RelayCommand(CanExecute = nameof(IsLoginExecutable))]
    private void Login()
    {
        MessageBox.Show("Hello from Login");
    }
    private bool IsLoginExecutable() 
    { 
        return userName.Length > 3; 
    }
}

运行时,按钮最初禁用并变灰。

当我键入第四个字母时,按钮被启用:

属性基本上是不言自明的。但是...
有一个由代码生成器生成的分部类。
为私有变量创建公共属性。
notifycanexecutechanged属性将在键入每个字符时引发canexecutechanged,并将其传输到视图模型用户名。
relaycommand属性的canexecute部分设置了IsLoginExecutable布尔值,当然在键入每个字符时都会调用它。
https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/
https://blogs.msmvps.com/bsonnino/2022/08/06/the-mvvm-pattern-revisited-with-the-mvvm-community-toolkit-8-0/

相关问题