MVVM CanExecute返回false,Button保持禁用状态

时间:2018-04-22 08:57:32

标签: c# wpf xaml

XAML:

     <TextBox x:Name="User" Text="{Binding Username}" Margin="0,0,62,0">
   <TextBox x:Name="Pass" Text="{Binding Password}"  Margin="0,0,62,0">
 <Button Command="{Binding Path= SaveCommand}"  IsDefault="True" Margin="10,0,1,9" Height="42" VerticalAlignment="Bottom">

如果我删除按钮上的Binding路径,该按钮将再次启用。

视图模型:

  public class XViewModel : INotifyPropertyChanged
{
    private string _username;
    private string _password;


    public XViewModel()
    {
        SaveCommand = new DelegateCommand(Save, () => CanSave);

    }

    public string Username
    {
        get { return _username; }
        set
        {
            _username = value;
            NotifyOfPropertyChange("Username");
        }
    }
    public string Password
    {
        get { return _password; }
        set
        {
            _password = value;
            NotifyOfPropertyChange("Password");
        }
    }

    protected void NotifyOfPropertyChange(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
    public ICommand SaveCommand { get; private set; }


    public bool CanSave
    {
        get { return !string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password); }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public async void Save()
    {

        SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["StringConnexion"].ConnectionString);
        SqlCommand cmd = con.CreateCommand();
        cmd.CommandText = "INSERT INTO Users(Login,password)VALUES(@Username,@Password)";
        cmd.Parameters.AddWithValue("@ID", Username);
        cmd.Parameters.AddWithValue("@FirstName", Password);

        try
        {
          await  con.OpenAsync();
            cmd.ExecuteNonQuery();
        }
        catch (SqlException ex)
        {
            throw ex;
        }
        finally
        {
            con.Close();
        }

        var sampleMessageDialog1 = new SampleMessageDialog
        {
            Message = { Text = "Connected" }
        };


           await DialogHost.Show(sampleMessageDialog1, "RootDialog");

    }

    public class DelegateCommand : ICommand
    {
        private readonly Action _execute;
        private readonly Func<bool> _canExecute;

        public DelegateCommand(Action execute)
            : this(execute, () => true)
        {
            _execute = execute;
        }

        public DelegateCommand(Action execute, Func<bool> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }

        public void Execute(object parameter)
        {

            _execute();
        }

        public bool CanExecute(object parameter)
        {
            return _canExecute();
        }

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

我认为它已被禁用,因为一开始没有在TExtbox上写任何内容,但显然情况并非如此。 我基本上使用https://www.codeproject.com/Articles/694908/Connecting-to-SQL-Server-using-MVVM-Pattern进行了一些更改。

修改

如果我使用DataContext执行此操作:

  <Button Command="{Binding Path= SaveCommand}"  Margin="10,0,1,9" Height="42" Grid.Column="1" Grid.Row="3" VerticalAlignment="Bottom">
                <Button.DataContext>
                    <local:ConnexionViewModel Password="something" Username="sdfsdf" />
                </Button.DataContext>

该按钮将启用,somethingsdfsdf将被添加到数据库中,但又一次,这不是我想要的行为,我希望用户能够通过文本框键入登录名和密码。

2 个答案:

答案 0 :(得分:1)

当TextBox的焦点丢失时,TextBox的Text属性会更新。如果在键入内容后从文本框中丢失焦点,则将启用“保存”按钮。如果您希望在键入Text属性后立即更新它,则需要将Binding.UpdateSourceTrigger属性设置为PropertyChanged。

<TextBox x:Name="User" Text="{Binding Username, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,62,0"/>
<TextBox x:Name="Pass" Text="{Binding Password, UpdateSourceTrigger=PropertyChanged}"  Margin="0,0,62,0"/>
<Button Command="{Binding Path= SaveCommand}"  Content="Save" IsDefault="True" Margin="10,0,1,9" Height="42" VerticalAlignment="Bottom"/>

答案 1 :(得分:0)

CanExecuteChanged

中添加可访问的方法来举起DelegateCommand事件

例如

public class DelegateCommand : ICommand {

    //...code removed for brevity


    public bool CanExecute(object parameter) {
        if(_canExecute == null)
            return true;
        return _canExecute();
    }

    public void RaiseCanExecuteChanged() {
        if( CanExecuteChanged != null ) {
            CanExecuteChanged(this, EventArgs.Empty);
        }
    }
}

以便UI知道视图模型的命令中发生了某些变化,因此它会知道检查/调用CanExecute并做出相应的反应。

public string Username {
    get { return _username; }
    set {
        _username = value;
        NotifyOfPropertyChange("Username");
        SaveCommand.RaiseCanExecuteChanged();
    }
}

public string Password {
    get { return _password; }
    set {
        _password = value;
        NotifyOfPropertyChange("Password");
        SaveCommand.RaiseCanExecuteChanged();
    }
}

为避免重复代码,您可以使用NotifyOfPropertyChange方法将代码移至

protected void NotifyOfPropertyChange(string name) {
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null) {
        handler(this, new PropertyChangedEventArgs(name));
        SaveCommand.RaiseCanExecuteChanged();
    }
}

这样命令也会在属性更改时引发其事件。由于您的属性很少,因此该建议特定于此方案。