为什么我的 SaveCommand 按钮在 UWP 中不起作用??我使用 MVVM 模式

My SaveCommand Button isnt working in UWP why?? I use MVVM Pattern

好的,我一直在 UWP 上练习 MVVM 模式,现在我创建了一个对象 Customer EditableCustomer,它是我在 VM 的视图和模型之间的助手 属性。 如果我的四个属性 FirstName、LastName、Email 和 Phone 不为空或为空,我的保存命令按钮应该启用。

但我无法在我的 EditableCustomer 上提出我的 属性 更改,这样我就可以提高我的 SaveCommand.RaiseCanExecuteChanged()。

这是我的代码:

<UserControl
x:Class="MVVMHeirarchiesDemo.Views.AddEditCustomerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MVVMHeirarchiesDemo.Views"
xmlns:viewmodel="using:MVVMHeirarchiesDemo.ViewModel"
xmlns:conv="using:MVVMHeirarchiesDemo.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">

<UserControl.DataContext>
    <viewmodel:AddEditCustomerViewModel/>
</UserControl.DataContext>

<UserControl.Resources>
    <conv:ValidationMessageConverter x:Key="ValidationMessageConverter"/>
</UserControl.Resources>

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

    <Grid x:Name="grid1"
          HorizontalAlignment="Left"
          Margin="10,10,0,0"
          VerticalAlignment="Top">

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

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>

        <TextBlock Text="First Name:"
                   Grid.Column="0"
                   Grid.Row="0"
                   HorizontalAlignment="Left"
                   VerticalAlignment="Center"
                   Margin="3"/>
        <StackPanel Orientation="Vertical"
                    Grid.Column="1"
                    Grid.Row="0">

            <TextBox x:Name="firstNameTextBox"
                     Text="{Binding EditableCustomer.FirstName, Mode=TwoWay}"
                     HorizontalAlignment="Left"
                     VerticalAlignment="Center"
                     Margin="3"
                     Height="23"
                     Width="120"/>
            <TextBlock x:Name="firstNameErrorMessage"
                       Text="{Binding EditableCustomer.ValidationMessages[FirstName], Converter={StaticResource ValidationMessageConverter}}"
                       Width="{Binding Width, ElementName=firstNameTextBox, Mode=OneWay}"
                       TextWrapping="Wrap"/>
        </StackPanel>

        <TextBlock Text="Last Name:"
                   Grid.Column="0"
                   Grid.Row="1"
                   HorizontalAlignment="Left"
                   VerticalAlignment="Center"
                   Margin="3"/>

        <StackPanel Orientation="Vertical"
                    Grid.Column="1"
                    Grid.Row="1">

            <TextBox x:Name="lastNameTextBox"
                     Text="{Binding EditableCustomer.LastName, Mode=TwoWay}"
                     HorizontalAlignment="Left"
                     VerticalAlignment="Center"
                     Width="{Binding Width, ElementName=firstNameTextBox, Mode=OneWay}"
                     Height="{Binding Height, ElementName=firstNameTextBox, Mode=OneWay}"
                     Margin="3"/>
            <TextBlock x:Name="lastNameErrorMessage"
                       Text="{Binding EditableCustomer.ValidationMessages[LastName], Converter={StaticResource ValidationMessageConverter}}"
                       Width="{Binding Width, ElementName=firstNameTextBox, Mode=OneWay}"
                       TextWrapping="Wrap"/>
        </StackPanel>

        <TextBlock Text="Email:"
                   Grid.Column="0"
                   Grid.Row="2"
                   HorizontalAlignment="Left"
                   VerticalAlignment="Center"
                   Margin="3"/>
        <StackPanel Orientation="Vertical"
                    Grid.Column="1"
                    Grid.Row="2">

            <TextBox x:Name="emailTextBox"
                     Text="{Binding EditableCustomer.Email, Mode=TwoWay}"
                     HorizontalAlignment="Left"
                     VerticalAlignment="Center"
                     Width="{Binding Width, ElementName=firstNameTextBox, Mode=OneWay}"
                     Height="{Binding Height, ElementName=firstNameTextBox, Mode=OneWay}"
                     Margin="3"/>
            <TextBlock x:Name="emailErrorMessage"
                       Text="{Binding EditableCustomer.ValidationMessages[Email], Converter={StaticResource ValidationMessageConverter}}"
                       Width="{Binding Width, ElementName=firstNameTextBox, Mode=OneWay}"
                       TextWrapping="Wrap"/>
        </StackPanel>

        <TextBlock Text="Phone:"
                   Grid.Column="0"
                   Grid.Row="3"
                   HorizontalAlignment="Left"
                   VerticalAlignment="Center"
                   Margin="3"/>

        <StackPanel Orientation="Vertical"
                    Grid.Column="1"
                    Grid.Row="3">

            <TextBox x:Name="phoneTextBox"
                     Text="{Binding EditableCustomer.Phone, Mode=TwoWay}"
                     HorizontalAlignment="Left"
                     VerticalAlignment="Center"
                     Width="{Binding Width, ElementName=firstNameTextBox, Mode=OneWay}"
                     Height="{Binding Height, ElementName=firstNameTextBox, Mode=OneWay}"
                     Margin="3"/>
            <TextBlock x:Name="phoneErrorMessage"
                       Text="{Binding EditableCustomer.ValidationMessages[Phone], Converter={StaticResource ValidationMessageConverter}}"
                       Width="{Binding Width, ElementName=firstNameTextBox, Mode=OneWay}"
                       TextWrapping="Wrap"/>
        </StackPanel>
    </Grid>

    <Grid Grid.Row="1">
        <StackPanel Orientation="Horizontal">
            <Button x:Name="saveCommandButton"
                Content="Save"
                Command="{Binding SaveCommand}"
                HorizontalAlignment="Left"
                VerticalAlignment="Top"
                Margin="25,5,0,0"
                Width="75"/>

            <Button x:Name="addCommandButton"
                Content="Add"
                Command="{Binding SaveCommand}"
                HorizontalAlignment="Left"
                VerticalAlignment="Top"
                Margin="25,5,0,0"
                Width="{Binding Width, ElementName=saveCommandButton, Mode=OneWay}"/>

            <Button x:Name="cancelCommandButton"
                    Content="Cancel"
                    Command="{Binding CancelCommand}"
                    HorizontalAlignment="Left"
                    VerticalAlignment="Center"
                    Margin="25,5,0,0"
                    Width="{Binding Width, ElementName=saveCommandButton, Mode=OneWay}"/>
        </StackPanel>
    </Grid>
</Grid>

正如您在我的文本框中看到的那样,文本 属性 已绑定到我的 EditableCustomer。(属性名称)

我的 ViewModel 是这个:

public class AddEditCustomerViewModel : BindableBase
{
    public AddEditCustomerViewModel()
    {
        CancelCommand = new MyCommand(OnCancel);
        SaveCommand = new MyCommand(OnSave, CanSave);
        EditableCustomer = new Customer();
    }

    private Customer _editableCustomer;

    public Customer EditableCustomer
    {
        get
        {
            return _editableCustomer;
        }
        set
        {
            SetProperty(ref _editableCustomer, value);
            SaveCommand.RaiseCanExecuteChanged();
        }
    }

    public MyCommand CancelCommand { get; private set; }
    public MyCommand SaveCommand { get; private set; }

    public event Action Done = delegate { };

    private void OnCancel()
    {
        Done();
    }

    private void OnSave()
    {
        Done();
    }

    private bool CanSave()
    {
        if (HasEmptyFields())
            return false;
        return true;
    }

    private bool HasEmptyFields()
    {
        return string.IsNullOrEmpty(EditableCustomer.FirstName) || string.IsNullOrEmpty(EditableCustomer.LastName)
                        || string.IsNullOrEmpty(EditableCustomer.Phone) || string.IsNullOrEmpty(EditableCustomer.Email);
    }
}

我需要做的就是能够为我的 EditableCustomer 属性 触发 setter,但我已经能够用它做到这一点。 我在这里错过了什么??

我看到了我的代码,我认为每次每个字段都有文本时都应该引发它,直到我的所有文本框上都有文本时它变为真。

但什么也没发生。

只是为了理解这是我的 BindableBase 是一个 class 我应用我的 INotifyPropertyChanged 的​​地方。

public class BindableBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = delegate { };

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

    protected virtual void SetProperty<T>(ref T member, T val, [CallerMemberName]string propertyName = null)
    {
        if (object.Equals(member, val))
            return;

        member = val;
        OnPropertyChanged(propertyName);
    }
}

希望大家多多指教,同时我会继续深入研究这个问题

看起来 SaveCommand.RaiseCanExecuteChanged(); 仅在您设置 EditableCustomer 时调用,但是当您修改该对象内的值(即姓名、电子邮件等)时,没有任何通知 UI SaveCommand 可以执行。

如果您要像那样分解您的视图模型,您有一个封装的 class 来管理输入,那么它将需要通知父视图模型更改。您的 AddEditCustomerViewModel 不知道属性何时更改,因此无法 RaiseCanExecuteChanged()


更新

对于您的情况,我建议让视图模型路由所有可以通过 UI 表单修改的属性。例如:

public class AddEditCustomerViewModel : BindableBase
{
    public string FirstName
    {
        get { return _editableCustomer.FirstName; }
        set
        {
            _editableCustomer.FirstName = value;
            OnPropertyChanged(nameof(FirstName));

            if (!HasEmptyFields())
            {
                SaveCommand.RaiseCanExecuteChanged();
            }
        }
    }
}

并且您的 XAML 应该直接绑定到视图模型,而不是模型:

<TextBox Text="{Binding FirstName, Mode=TwoWay}" />