wpf 在 MVVM 中实现文本框丢失焦点事件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26353893/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 12:36:31  来源:igfitidea点击:

Implementing textbox lostfocus event in MVVM

c#wpfxamlmvvm

提问by nostafict

I want to accomplish a simple task. Need to implement textbox lostfocus, As the user puts in data, as soon as one field is filled and he reaches on to the next, it should fire a validation function on the previous field. Also, I am using MVVM pattern.

我想完成一个简单的任务。需要实现 textbox lostfocus,当用户输入数据时,只要一个字段被填充并进入下一个字段,它应该在前一个字段上触发一个验证函数。另外,我正在使用 MVVM 模式。

So I have this class

所以我有这门课

public class data : INotifyPropertyChanged
{

    public string name;
    public string Name
    {
        get
        {
            return name;
        }

        set
        {
            name = value;
            OnPropertyChanged("Name");
        }
    }
    public string firstname;
    public string FirstName
    {
        get
        {
            return firstname;
        }

        set
        {
            firstname = value;
            OnPropertyChanged("FirstName");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            // Raise the PropertyChanged event
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
} 

In the Viewmodel I got this

在 Viewmodel 我得到了这个

data1 = new data() { name = "Eddie Vedder", firstname = "Eddie" }; //this line in initialization 
public data _data1;
public data data1
{
    get { return _data1; }
    set 
    {

        _data1 = value;
        ValidateThis();
        NotifyPropertyChanged(new PropertyChangedEventArgs("data1"));
    }
}

In Xaml:

在 Xml 中:

<StackPanel Orientation="Horizontal" >
    <Label Width="90" Content="Name" Height="28" HorizontalAlignment="Left" Name="lblName" VerticalAlignment="Top" />
    <TextBox Text="{Binding Path=data1.name, UpdateSourceTrigger=LostFocus, Mode=TwoWay}"   MaxLength="40" TabIndex="2" Height="25" Margin="0,3,0,0" HorizontalAlignment="Left" Name="txtName" VerticalAlignment="Top" Width="200" />
</StackPanel>
<StackPanel Orientation="Horizontal" >
    <Label Width="90" Content="First Name" Height="28" HorizontalAlignment="Left" Name="lblFirstName" VerticalAlignment="Top" />
    <TextBox Text="{Binding  Path=data1.firstname, UpdateSourceTrigger=LostFocus, Mode=TwoWay}" MaxLength="40" TabIndex="3" Name="txtFirstName" Height="25" Margin="0,3,0,0" VerticalAlignment="Top" Width="200" >
    </TextBox>
</StackPanel>

My binding is working as it shoes the default name Eddie Vedder when I execute it. When I debug it, it doesn't enter the class data.

我的绑定正在工作,因为它在我执行它时使用默认名称 Eddie Vedder。当我调试它时,它不输入​​类数据。

采纳答案by nostafict

Well this kinda did the trick

嗯,这有点成功了

public class Validate
{
    public static ErrorProperties ep = new ErrorProperties();
    public static bool ValidateThis(string PropertyName, string PropertyValue)
    {
        if (PropertyValue.Length > 10)
        {
            ep.ErrorPropertyName = PropertyName;
            return true;
        }
        return false;
    }
}

public class ErrorProperties
{
    public string ErrorPropertyName { get; set; }
    public string Error { get; set; }
}

public class data : INotifyPropertyChanged
{
    private ObservableCollection<ErrorProperties> _ErrorList = new ObservableCollection<ErrorProperties>();
    public ObservableCollection<ErrorProperties> ErrorList
    {
        get
        {
            return _ErrorList;
        }
        set
        {
            if (_ErrorList != value)
            {
                _ErrorList = value;
                OnPropertyChanged("ErrorList");
            }
        }
    }


    private string _Name;
    public string Name
    {
        get
        {
            return _Name;
        }
        set
        {
            if (_Name != value)
            {
                _Name = value;
                if (Validate.ValidateThis("Name", value))
                    ErrorList.Add(Validate.ep);
                OnPropertyChanged("Name");
            }
        }
    }


    private string _FirstName;
    public string FirstName
    {
        get
        {
            return _FirstName;
        }
        set
        {
            if (_FirstName != value)
            {
                _FirstName = value;
                if (Validate.ValidateThis("FirstName", value))
                    ErrorList.Add(Validate.ep);
                OnPropertyChanged("FirstName");

            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            // Raise the PropertyChanged event
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

回答by Alexander.Ermolaev

As you use MVVM pattern I assume that you have some binding to view model property and it looks like: Xaml:

当您使用 MVVM 模式时,我假设您对视图模型属性有一些绑定,它看起来像:Xaml:

<StackPanel>
    <!--Pay attention on UpdateSourceTrigger-->
    <TextBox Text="{Binding Text, UpdateSourceTrigger=LostFocus}" />
    <TextBox />
</StackPanel>

c#:

C#:

private string _text;
public string Text
{
    get { return _text; }
    set
    {
        _text = value;
        Validate(); // Desired validation
        OnPropertyChanged();
    }
}

If you set UpdateSourceTrigger to LostFocus, property changed will be fired when you lost focus.

如果将 UpdateSourceTrigger 设置为 LostFocus,则在失去焦点时会触发已更改的属性。

回答by blfuentes

There is a very nice article for this: MVVM WPF commands

有一篇非常好的文章:MVVM WPF commands

First create a class: the DelegateCommand.cs

首先创建一个类: DelegateCommand.cs

public class DelegateCommand<T> : System.Windows.Input.ICommand where T : class
{
    private readonly Predicate<T> _canExecute;
    private readonly Action<T> _execute;

    public DelegateCommand(Action<T> execute)
     : this(execute, null)
    {
    }

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

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

        return _canExecute((T)parameter);
    }

    public void Execute(object parameter)
    {
        _execute((T)parameter);
    }

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

Add the delegate into your ViewModel:

将委托添加到您的 ViewModel 中:

  private readonly DelegateCommand<string> _lostFocusCommand;

  public DelegateCommand<string> LostFocusCommand
  {
     get { return _lostFocusCommand; }
  }
  private string _input;
  public string Input
  {
     get { return _input; }
     set
     {
        _input = value;
     }
  }

And initialize it in the constructor of the ViewModel:

并在 ViewModel 的构造函数中对其进行初始化:

// _input will be the property you have with a binding to the textbox control in the view.
// in the canExecute part add the conditions you want to use to check if the lostfocus command will be raised
 _lostFocusCommand = new DelegateCommand<string>(
  (s) => { /* perform some action */
     MessageBox.Show("The lostfocuscommand works!");
  }, //Execute
  (s) => { return !string.IsNullOrEmpty(_input); } //CanExecute
  );

View: you need to add the following namespace

查看:需要添加如下命名空间

xmlns:b="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"  

And the control

和控制

<TextBox Grid.Column="0"
    Text="{Binding Input, UpdateSourceTrigger=PropertyChanged}">
    <b:Interaction.Triggers>
        <b:EventTrigger EventName="LostFocus">
            <b:InvokeCommandAction  Command="{Binding LostFocusCommand}" CommandParameter="{Binding Input}"/>
        </b:EventTrigger>
     </b:Interaction.Triggers>
  </TextBox>