我的SaveCommand Button不能在UWP中工作为什么?我使用MVVM模式

时间:2017-04-13 01:41:16

标签: c# mvvm uwp uwp-xaml

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

但我无法在我的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>

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

我的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是一个我应用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);
    }
}

我希望有人可以让我高兴,同时我会继续深入研究这个问题。

1 个答案:

答案 0 :(得分:1)

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

如果你打算拆分这样的视图模型,你有一个管理输入的封装类,那么它需要通知父视图模型的变化。您的SaveCommand不知道属性何时发生变化,因此无法AddEditCustomerViewModel

<强>更新

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

RaiseCanExecuteChanged()

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

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

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