mvvm windows phone 8混乱以及如何在文本更改时捕获

时间:2013-11-18 01:50:18

标签: c# mvvm windows-phone-8

我正在尝试使用mvvm模式编写一个Windows Phone 8应用程序,但我正在努力。

我有一个页面,其中包含绑定到PersonViewModel的人员列表。那部分工作正常。然后我在应用程序栏中有2个按钮,即添加或编辑。当我想编辑一个人时,我从列表中选择该人,然后在我的ViewModel中设置CurrentPerson。这又在我的MainViewModel中设置了一个属性,用于存储当前选定的人,即

App.MainViewModel.CurrentPerson = this.CurrentPerson;

当我想添加一个新人时,我使用相同的主体,但我创建了一个新人模型。

App.MainViewModel.CurrentPerson = new PersonModel();

然后我重定向到一个页面,其中包含处理某个人的字段,无论是添加还是编辑,并将其绑定到名为PersonEntryViewModel的ViewModel

在我解释我的问题之前,我想告诉你我想要实现的目标。我希望我的应用程序栏中的“保存”按钮在满足一定数量的条件后启用,即名称已填写且有x个字符等...

我可以看到我的问题是什么,但我不知道如何解决它。

这是我的PersonEntryViewModel的简化版本:

public class PersonEntryViewModel : BaseViewModel
{
    private PersonModel _currentPerson;
    private bool _isNewPerson;
    private ICommand _savePersonCommand;
    private ICommand _cancelCommand;
    private ICommand _titleTextChanged;

    private bool _enableSaveButton;

    public PersonEntryViewModel()
    {
        this.CurrentPerson = App.MainViewModel.CurrentPerson ?? new PersonModel();
    }

    public ICommand SavePersonCommand
    {
        get
        {
            return this._savePersonCommand ?? (this._savePersonCommand = new DelegateCommand(SavePersonAction));
        }
    }

    public ICommand CancelCommand
    {
        get
        {
            return this._cancelCommand ?? (this._cancelCommand = new DelegateCommand(CancelAction));
        }
    }

    public ICommand NameTextChanged
    {
        get
        {
            return this._nameTextChanged ?? (this._nameTextChanged = new DelegateCommand(NameTextChangedAction));
        }
    }

    private void NameTextChangedAction(object actionParameters)
    {
        if (!string.IsNullOrEmpty(this._currentPerson.Name) && _currentPerson.Name.Length > 2)
        {
            EnableSaveButton = true;
        }            
    }

    private void CancelAction(object actionParameters)
    {
        Console.WriteLine("Cancel");
        INavigationService navigationService = this.GetService<INavigationService>();
        if (navigationService == null)
            return;
        navigationService.GoBack();
        navigationService = null;
    }

    private void SavePersonAction(object actionParameters)
    {
        Console.WriteLine("Saving");
    }

    public PersonModel CurrentPerson
    {
        get { return this._currentPerson; }
        set
        {
            if (this._currentPerson != value)
                this.SetProperty(ref this._currentPerson, value);
        }
    }

    public string PageTitle
    {
        get { return this._pageTitle; }
        set { if (this._pageTitle != value) this.SetProperty(ref this._pageTitle, value); }
    }

    public bool IsNewPerson
    {
        get { return this._isNewPerson; }
        set
        {
            if (this._isNewPerson != value)
            {
                this.SetProperty(ref this._isNewPerson, value);
                if (this._isNewPerson)
                    this.PageTitle = AppResources.PersonEntryPageNewTitle;
                else
                    this.PageTitle = AppResources.PersonEntryPageEditTitle;
            }
        }
    }

    public bool EnableSaveButton
    {
        get { return this._enableSaveButton; }
        set { if (this._enableSaveButton != value) this.SetProperty(ref this._enableSaveButton, value); }
    }
}

以下是我的XAML的一部分:

<Grid x:Name="LayoutRoot" Background="Transparent" DataContext="{StaticResource PersonEntryViewModel}" >

    <!--ContentPanel - place additional content here-->
    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" DataContext="{Binding CurrentPerson, Mode=TwoWay}">
        <Grid Margin="0,0,0,5">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Border BorderBrush="{StaticResource PhoneAccentBrush}"
                            HorizontalAlignment="Stretch"
                            VerticalAlignment="Stretch"
                            BorderThickness="5"
                            Background="Transparent"
                            CornerRadius="5">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"></RowDefinition>
                        <RowDefinition Height="Auto"></RowDefinition>
                        <RowDefinition Height="Auto"></RowDefinition>
                        <RowDefinition Height="Auto"></RowDefinition>
                        <RowDefinition Height="Auto"></RowDefinition>
                        <RowDefinition></RowDefinition>
                    </Grid.RowDefinitions>
                    <TextBlock Text="Name:" 
                                       Grid.Row="0"
                                       Margin="12,0,0,0"/>
                    <TextBox Text="{Binding Name, Mode=TwoWay}" 
                                     Grid.Row="1">
                        <!--<i:Interaction.Triggers>
                            <i:EventTrigger EventName="TextChanged">
                                <i:InvokeCommandAction Command="{Binding NameTextChanged, Mode=OneWay}" CommandParameter="{Binding}" />
                            </i:EventTrigger>
                        </i:Interaction.Triggers>-->
                    </TextBox>
                    <TextBlock Text="Address:" 
                                       Grid.Row="2"
                                       Margin="12,0,0,0"/>
                    <TextBox Text="{Binding Address, Mode=TwoWay}" 
                             AcceptsReturn="True" 
                             Height="200" 
                             VerticalScrollBarVisibility="Visible" 
                             TextWrapping="Wrap" 
                             Grid.Row="3"/>

如您所见,我的layoutRoot网格绑定到我的ViewModel,即PersonEntryViewModel,包含编辑所需文本框的网格内容面板绑定到CurrentPerson。

这是正确的方法吗?我需要将控件绑定到CurrentPerson属性,该属性将包含正在编辑人员的数据,如果要添加新人,则将包含新的空PersonModel。

目前,该部分正在运作。当我在我的字段中键入一些文本并单击下一个文本时,它会调用设置CurrentPerson相关属性,该属性又调用PersonModel。单击保存按钮,我检查CurrentPerson,我可以看到它设置了所有各种属性。

正如您在PersonEntryViewModel中看到的,我还有其他需要的属性。例如,EnableSaveButton,在技术上应该根据CurrentPerson对象的各种属性的验证设置为true或false但是我需要检查这个,因为用户在各种文本框中键入文本,这就是我所在的位置遇到问题。

如果我启用以下代码:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="TextChanged">
        <i:InvokeCommandAction Command="{Binding NameTextChanged, Mode=OneWay}" CommandParameter="{Binding}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

它不会在我真正需要的PersonEntryViewModel中被触发,因为这是我想设置我的EnableSaveButton属性的地方,但我想这是有意义的,因为这个代码绑定到Name文本框,该文本框被绑定到CurrentPerson属性,这是我的PersonModel。

如果我将代码从PersonEntryViewModel移动到PersonViewModel

private ICommand _personTextChanged;

public ICommand PersonTextChanged
{
   get
   {
       return this._personTextChanged ?? (this._personTextChanged = new DelegateCommand(PersonTextChangedAction));
   }
}

private void PersonTextChangedAction(object actionParameters)
{
    if (!string.IsNullOrEmpty(this._name) && this._name.Length > 2)
    {
        //EnableSaveButton = true;
        Console.WriteLine("");
    }
}

它会被相应地触发,但是如何将这些信息反馈给我的PersonEntryViewModel,该PersonnntryViewModel绑定到我的2个按钮(即保存和取消)所在的视图,并且EnableSaveButton属性负责相应地启用保存按钮设置假设Name有效,例如set和minlen匹配,例如。

是PersonEntryViewModel并使用CurrentPerson属性,当前正在编辑或添加的人员设计是否正确以及我如何处理这种情况?

我希望上述内容有道理,但如果我不清楚某些事情,请告诉我,我会尽力澄清。

感谢。

PS:我发布了另一篇关于如何检测文本更改的帖子,但我想出来了,但显然不是问题。这个问题似乎与设计有关。

1 个答案:

答案 0 :(得分:0)

我的设计不清楚。

如果您想使用当前的设计,我建议您做以下事情。

删除为xaml中的网格分配DataContext。

在后面的代码中添加:

var dataContext = new PersonEntryViewModel();
this.ContentPanel.DataContext = dataContext.CurrentPerson;
// After creating App bar
this.appBar.DataContext = dataContext;
//Your xaml code will look something like this:
<AppBar x:Name="appBar">
    <Button x:Name="saveBtn" IsEnabled={Binding EnableSaveButton} />
<AppBar />