Mencoba mengikat kotak teks dengan model tampilan tetapi tidak ada pembaruan yang terjadi

Saya telah menyiapkan proyek pengujian kecil dan membuat halaman login yang menggunakan nama pengguna dan kata sandi. Saya menggunakan pendekatan MVVM dan kerangka Prism. Saya mengikuti video ini (https://www.youtube.com/watch?v=ZfBy2nfykqY).

Ini xamlnya:

<Page x:Class="Control_Center.Views.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:Control_Center.Views"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <StackPanel VerticalAlignment="Center"
                    HorizontalAlignment="Center"
                    Grid.Row="0">
            <StackPanel Orientation="Horizontal"
                        HorizontalAlignment="Center">
                <TextBlock Name="LoginResponse"
                           Text="{Binding LoginStatus}" />
            </StackPanel>
        </StackPanel>
        <StackPanel VerticalAlignment="Center"
                    HorizontalAlignment="Center"
                    Grid.Row="1">
            <StackPanel Orientation="Horizontal"
                        HorizontalAlignment="Center">
                <TextBox Name="UsernameBox"
                         Text="{Binding Username, UpdateSourceTrigger=PropertyChanged}"
                         AcceptsReturn="False"
                         PlaceholderText="Username..."
                         HorizontalAlignment="Center"
                         VerticalAlignment="Center"
                         FontSize="14"
                         Width="200"
                         Height="25" />
            </StackPanel>
            <StackPanel Orientation="Horizontal"
                        HorizontalAlignment="Center">
                <PasswordBox Name="PasswordBox"
                             PasswordRevealMode="Peek"
                             PlaceholderText="Password..."
                             Width="200"
                             Height="25"
                             VerticalAlignment="Center"
                             HorizontalAlignment="Center"
                             FontSize="14"
                             PasswordChanged="PasswordBox_PasswordChanged" />
            </StackPanel>
            <StackPanel Orientation="Horizontal"
                        HorizontalAlignment="Right">
                <Button Name="LoginButton"
                        HorizontalAlignment="Right"
                        VerticalAlignment="Bottom"
                        Margin="0,10,0,0"
                        FontSize="12"
                        Content="Login"
                        Command="{Binding LoginCommand}" />
                <Button Name="CancelButton"
                        HorizontalAlignment="Right"
                        VerticalAlignment="Bottom"
                        Margin="10,10,0,0"
                        FontSize="12"
                        Content="Cancel" />

            </StackPanel>
        </StackPanel>
    </Grid>
</Page>

Dan viewModel (Catatan: BindableBase adalah bagian dari Prism: https://github.com/PrismLibrary/Prism/blob/master/Source/Prism/Mvvm/BindableBase.cs):

using System;
using System.Diagnostics;
using System.Windows.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Prism.Commands;
using Prism.Mvvm;

namespace EJD_Control_Center.ViewModels
{
    class ViewLoginViewModel : BindableBase
    {
        private string _username;

        public string Username
        {
            get { return _username; }
            set { SetProperty(ref _username, value); }
        }

        private string _password;

        public string Password
        {
            get { return _password; }
            set { SetProperty(ref _password, value); }
        }

        private string _loginStatus;

        public string LoginStatus
        {
            get { return _loginStatus; }
            set { SetProperty(ref _loginStatus, value); }
        }

        public DelegateCommand LoginCommand { get; set; }

        public ViewLoginViewModel()
        {
            LoginCommand = new DelegateCommand(Execute, CanExecute).ObservesProperty(() => Username).ObservesProperty(() => Password);
        }

        private bool CanExecute()
        {
            Debug.WriteLine($"Username: {Username}, password: {Password}");
            Debug.WriteLine("Username is not null or whitespace? " + !string.IsNullOrWhiteSpace(Username) + ", and password: " + !string.IsNullOrWhiteSpace(Password));
            return !string.IsNullOrWhiteSpace(Username) && !string.IsNullOrWhiteSpace(Password);
        }

        private void Execute()
        {
            LoginStatus = $"Login Successful! Username: {Username} Password: {Password}"; 
            //actually return the status of the login request, but we don't have this yet. then switch screens.
        }
    }
}

Dan untuk penyelesaiannya, kode di belakang:

using Windows.Foundation;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Control_Center.ViewModels;

namespace Control_Center.Views
{
    /// <summary>
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            InitializeComponent();

            ApplicationView.PreferredLaunchViewSize = new Size(500, 320);
            ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.PreferredLaunchViewSize;

            DataContext = new ViewLoginViewModel();
        }

        private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
        {
            var vm = (ViewLoginViewModel) DataContext;
            vm.Password = PasswordBox.Password;
        }
    }
}

Sekarang sebelum ada yang memulai, saya tahu ini bukan cara yang aman untuk menangani penyampaian kata sandi. Bukan itu inti pertanyaan ini.

Masalahnya adalah ketika saya mengikat kotak teks nama pengguna ke variabel Nama Pengguna di ViewModel sepertinya tidak memperbarui String itu. Jika saya menyetel nama pengguna di ViewModel menjadi sesuatu, katakanlah "test", itu akan menampilkannya di kotak teks tetapi tidak ada pembaruan pada kotak teks itu yang mengubah variabel Nama Pengguna di ViewModel.

Output yang saya dapatkan ketika memodifikasi kedua kotak tersebut adalah:

Username: , password: d
Username is not null or whitespace? False, and password: True

Bahkan ketika kotak teks nama pengguna memiliki sesuatu di dalamnya. Satu-satunya alasan baris debug tersebut muncul adalah cara saya menangani kata sandi dengan memanggil canExecute, tetapi cara saya menangani nama pengguna tidak meskipun sejauh yang saya pahami seharusnya demikian.

Mengapa "{Binding Username, UpdateSourceTrigger=PropertyChanged}" tidak mengirimkan teks kotak teks nama pengguna ke variabel nama pengguna di ViewModel?


person Eric B    schedule 05.10.2016    source sumber
comment
bagaimana dengan BindableBase - bagaimana orang dapat menjalankan kode Anda tanpanya dan mengetahui jenis keajaiban apa yang ada di balik SetProperty()?   -  person RTDev    schedule 05.10.2016
comment
Karena BindableBase adalah bagian dari Prism, yang saya sebutkan secara khusus saya gunakan dan masukkan ke dalam tag.   -  person Eric B    schedule 05.10.2016
comment
oke, tidak punya prisma, jadi hanya berdasarkan apa yang saya lihat, saya sarankan menyetel Mode=TwoWay ke penjilidan Anda   -  person RTDev    schedule 05.10.2016
comment
Saya telah memperbarui pertanyaan dengan pemberitahuan tentang BindableBase, dan tautan ke halaman githubnya. Maaf tentang itu.   -  person Eric B    schedule 05.10.2016
comment
@RTDev wow, berhasil. Anda tuan luar biasa. Jika Anda ingin menjadikannya sebagai jawaban, saya dapat menandainya sebagai telah dijawab.   -  person Eric B    schedule 05.10.2016
comment
senang mendengarnya, saya sedang dalam perjalanan untuk menjalankan kode Anda dan mengujinya;)   -  person RTDev    schedule 05.10.2016


Jawaban (1)


Itu karena pengaturan Mode default untuk Binding adalah OneWay - jadi pembaruan dari kode ke tata letak berfungsi dengan baik, tetapi dari tata letak ke kode tidak. Atur Mode=TwoWay agar berfungsi dua arah.

"{Binding Username, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
person RTDev    schedule 05.10.2016
comment
Juga pengaturan Mode default untuk x:Bind adalah OneTime, tidak seperti Binding. - person Decade Moon; 06.10.2016
comment
Benar, tetapi Anda tidak akan menggunakan x:Bind dalam kasus ini - karena tidak mendukung UpdateSourceTrigger. - person RTDev; 06.10.2016