Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# WPF-为什么SetProperty()在XAML中设置{Binding BindableObject.Text}时不触发CanExecute?_C#_Wpf_Xaml_Mvvm_Command - Fatal编程技术网

C# WPF-为什么SetProperty()在XAML中设置{Binding BindableObject.Text}时不触发CanExecute?

C# WPF-为什么SetProperty()在XAML中设置{Binding BindableObject.Text}时不触发CanExecute?,c#,wpf,xaml,mvvm,command,C#,Wpf,Xaml,Mvvm,Command,我使用以下新的BindableViewModel类(我也在本主题底部发布了一个旧类)使对象可观察(来自MS示例): 然后,在我的页面视图模型中,我使用TextBoxModel定义字段: public class Page1ViewModel : BindableBase { private TextBoxModel _firstName = null; public TextBoxModel FirstName { get { return _firstName; } set {

我使用以下新的BindableViewModel类(我也在本主题底部发布了一个旧类)使对象可观察(来自MS示例):

然后,在我的页面视图模型中,我使用TextBoxModel定义字段:

public class Page1ViewModel : BindableBase
{
    private TextBoxModel _firstName = null;
    public TextBoxModel FirstName { get { return _firstName; } set { 
        if (SetProperty(ref _firstName, value))
            {
                SubmitCommand.RaiseCanExecuteChanged();
            } } }

    private TextBoxModel _surname = null;
    public TextBoxModel Surname { get { return _surname; } set { 
        if (SetProperty(ref _surname, value))
            {
                SubmitCommand.RaiseCanExecuteChanged();
            } } }

    private DelegateCommand _submitCommand = null;
    public DelegateCommand SubmitCommand { get { return _submitCommand??(_submitCommand=new DelegateCommand(SubmitExecute, SubmitCanExecute)); } }
    private void SubmitExecute()
    {
        MessageBox.Show($"{FirstName.Text} {Surname.Text}");
    }
    private bool SubmitCanExecute()
    {
        if(string.IsNullOrEmpty(FirstName.Text))
            return false;
        else if(string.IsNullOrEmpty(Surname.Text))
            return false;
        else
            return true;
    }
}
在我的XAML视图中,我像往常一样设置文本框绑定:

<TextBox Text="{Binding FirstName.Text, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding FirstName.IsEnabled}"/>
<TextBox Text="{Binding Surname.Text, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding Surname.IsEnabled}"/>
<Button Content="Submit" Command{Binding SubmitCommand} />

当我运行此程序时,我发现文本更改不起作用。它没有在setter中触发if(SetProperty(ref _firstName,value))或if(SetProperty(ref _lasname,value))。如果我不在TextBoxModel中组合属性,那么一切都可以正常工作。如果我使用旧的ViewModelBase,TextBoxModel也可以正常工作

所以我想我在使用新的BindableBase类时一定错过了什么?期待您的帮助,谢谢

旧的ViewModelBase:

[Obsolete("Please use BindableBase。")]
public abstract class ObservableModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public static string GetPropertyName<T>(System.Linq.Expressions.Expression<Func<T>> e)
    {
        var member = (System.Linq.Expressions.MemberExpression)e.Body;
        return member.Member.Name;
    }

    protected virtual void RaisePropertyChanged<T>
        (System.Linq.Expressions.Expression<Func<T>> propertyExpression)
    {
        RaisePropertyChanged(GetPropertyName(propertyExpression));
    }

    protected void RaisePropertyChanged(String propertyName)
    {
        System.ComponentModel.PropertyChangedEventHandler temp = PropertyChanged;
        if (temp != null)
        {
            temp(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
    }
}
[过时(“请使用BindableBase”)]
公共抽象类ObservableModel:INotifyPropertyChanged
{
公共事件属性更改事件处理程序属性更改;
公共静态字符串GetPropertyName(System.Linq.Expressions.Expression e)
{
var member=(System.Linq.Expressions.MemberExpression)e.Body;
返回member.member.Name;
}
受保护的虚拟空RaisePropertyChanged
(System.Linq.Expressions.Expression propertyExpression)
{
RaisePropertyChanged(GetPropertyName(propertyExpression));
}
受保护的void RaisePropertyChanged(字符串propertyName)
{
System.ComponentModel.PropertyChangedEventHandler温度=PropertyChanged;
如果(温度!=null)
{
temp(这是新的System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}

您的绑定将永远不会设置
FirstName
LastName
,并且您显示的其他内容也不会设置。它们都将是
null
。绑定到
Text
只会设置该值,不会创建新的
TextBoxModel
并设置该值

如果您希望以这种方式组织工作,您需要执行以下操作:

public class Page1ViewModel : BindableBase
{
    private readonly TextBoxModel _firstName = new TextBoxModel();

    public Page1ViewModel()
    {
        _firstName.PropertyChanged += 
            (sender, args) => SubmitCommand.RaiseCanExecuteChanged();
    }

    public TextBoxModel FirstName 
    {
        get { return _firstName; }
    }
}
public class PersonViewModel : PropertyChangedBase
{
    private PersonModel _Person;

    public string FirstName
    {
        get { return _Person.FirstName; }
        set
        {
            _Person.FirstName = value;
            NotifyOfPropertyChange();
        }
    }

    public string LastName
    {
        get { return _Person.LastName; }
        set
        {
            _Person.LastName = value;
            NotifyOfPropertyChange();
        }
    }

    //TODO: Your command goes here

    public PersonViewModel()
    {
        //TODO: Get your model from somewhere.
        _Person = new PersonModel();
    }
}

您的绑定将永远不会设置
FirstName
LastName
,您显示的其他内容也不会设置。它们都将是
null
。绑定到
Text
只会设置该值,不会创建新的
TextBoxModel
并设置该值

如果您希望以这种方式组织工作,您需要执行以下操作:

public class Page1ViewModel : BindableBase
{
    private readonly TextBoxModel _firstName = new TextBoxModel();

    public Page1ViewModel()
    {
        _firstName.PropertyChanged += 
            (sender, args) => SubmitCommand.RaiseCanExecuteChanged();
    }

    public TextBoxModel FirstName 
    {
        get { return _firstName; }
    }
}
public class PersonViewModel : PropertyChangedBase
{
    private PersonModel _Person;

    public string FirstName
    {
        get { return _Person.FirstName; }
        set
        {
            _Person.FirstName = value;
            NotifyOfPropertyChange();
        }
    }

    public string LastName
    {
        get { return _Person.LastName; }
        set
        {
            _Person.LastName = value;
            NotifyOfPropertyChange();
        }
    }

    //TODO: Your command goes here

    public PersonViewModel()
    {
        //TODO: Get your model from somewhere.
        _Person = new PersonModel();
    }
}

你想做什么是有道理的,但你如何做却不必要地复杂。你有一个
TextBoxModel
,它试图模仿一个真正的
TextBox
,你根本不需要这样做,我认为你的方法是完全错误的

首先,模型表示的是数据,而不是UI。与其创建表示
文本框的
TextBoxModel
,不如创建表示数据结构的模型,例如
PersonModel
UserModel
。以下是我的意思的一个例子:

public class PersonModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
注意:您可以将
INotifyPropertyChanged
内容放入模型中,但这并不是真正的UI问题,它只是数据。属性更改实现的最佳位置是视图模型,我将在下面进一步解释

好的,现在数据层已排序,您只需通过视图模型将该模型暴露给视图即可。首先,这里是一个简单的属性更改基础,我将在本例中使用它:

public abstract class PropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyOfPropertyChange([CallerMemberName]string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
如下所示公开模型属性:

public class Page1ViewModel : BindableBase
{
    private readonly TextBoxModel _firstName = new TextBoxModel();

    public Page1ViewModel()
    {
        _firstName.PropertyChanged += 
            (sender, args) => SubmitCommand.RaiseCanExecuteChanged();
    }

    public TextBoxModel FirstName 
    {
        get { return _firstName; }
    }
}
public class PersonViewModel : PropertyChangedBase
{
    private PersonModel _Person;

    public string FirstName
    {
        get { return _Person.FirstName; }
        set
        {
            _Person.FirstName = value;
            NotifyOfPropertyChange();
        }
    }

    public string LastName
    {
        get { return _Person.LastName; }
        set
        {
            _Person.LastName = value;
            NotifyOfPropertyChange();
        }
    }

    //TODO: Your command goes here

    public PersonViewModel()
    {
        //TODO: Get your model from somewhere.
        _Person = new PersonModel();
    }
}
现在,您只需将
文本框
绑定到
FirstName
LastName
查看模型属性:

<TextBox Text="{Binding FirstName}" ... />

其实就这么简单。您不需要在数据层中重新创建
文本框
,事实上,所有UI关注点都应该与模型层完全分离

使用此方法,您也不必担心引发
CanExecuteChanged
方法,因为它已经由
INotifyPropertyChanged
处理


请记住,用户界面就是用户界面,数据就是数据

您试图做的事情是有意义的,但您如何做却毫无必要地复杂。你有一个
TextBoxModel
,它试图模仿一个真正的
TextBox
,你根本不需要这样做,我认为你的方法是完全错误的

首先,模型表示的是数据,而不是UI。与其创建表示
文本框的
TextBoxModel
,不如创建表示数据结构的模型,例如
PersonModel
UserModel
。以下是我的意思的一个例子:

public class PersonModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
注意:您可以将
INotifyPropertyChanged
内容放入模型中,但这并不是真正的UI问题,它只是数据。属性更改实现的最佳位置是视图模型,我将在下面进一步解释

好的,现在数据层已排序,您只需通过视图模型将该模型暴露给视图即可。首先,这里是一个简单的属性更改基础,我将在本例中使用它:

public abstract class PropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyOfPropertyChange([CallerMemberName]string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
如下所示公开模型属性:

public class Page1ViewModel : BindableBase
{
    private readonly TextBoxModel _firstName = new TextBoxModel();

    public Page1ViewModel()
    {
        _firstName.PropertyChanged += 
            (sender, args) => SubmitCommand.RaiseCanExecuteChanged();
    }

    public TextBoxModel FirstName 
    {
        get { return _firstName; }
    }
}
public class PersonViewModel : PropertyChangedBase
{
    private PersonModel _Person;

    public string FirstName
    {
        get { return _Person.FirstName; }
        set
        {
            _Person.FirstName = value;
            NotifyOfPropertyChange();
        }
    }

    public string LastName
    {
        get { return _Person.LastName; }
        set
        {
            _Person.LastName = value;
            NotifyOfPropertyChange();
        }
    }

    //TODO: Your command goes here

    public PersonViewModel()
    {
        //TODO: Get your model from somewhere.
        _Person = new PersonModel();
    }
}
现在,您只需将
文本框
绑定到
FirstName
LastName
查看模型属性:

<TextBox Text="{Binding FirstName}" ... />

其实就这么简单。您不需要在数据层中重新创建
文本框
,事实上,所有UI关注点都应该与模型层完全分离

使用此方法,您也不必担心引发
CanExecuteChanged
方法,因为它已经由
INotifyPropertyChanged
处理


记住,用户界面就是用户界面,数据就是数据

我不明白这是怎么回事。当文本发生更改时(或者实际上根本没有更改!),您没有设置
FirstName
姓氏,因此不会调用
SetProperty
。你