กำลังพยายามผูกกล่องข้อความกับ viewmodel แต่ไม่มีการอัพเดตเกิดขึ้น

ฉันได้จัดทำโครงการทดสอบเล็กๆ และสร้างหน้าเข้าสู่ระบบที่ใช้ชื่อผู้ใช้และรหัสผ่าน ฉันกำลังใช้แนวทาง MVVM และกรอบงานปริซึม ฉันติดตามวิดีโอนี้ไปด้วย (https://www.youtube.com/watch?v=ZfBy2nfykqY)

นี่คือ xaml:

<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>

และ viewModel (หมายเหตุ: BindableBase เป็นส่วนหนึ่งของ 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.
        }
    }
}

และเพื่อให้เสร็จสมบูรณ์โค้ดเบื้องหลัง:

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;
        }
    }
}

ก่อนที่ใครจะเริ่มต้น ฉันรู้ว่านี่ไม่ใช่วิธีที่ปลอดภัยในการจัดการรหัสผ่าน นั่นไม่ใช่ประเด็นของคำถามนี้

ปัญหาคือเมื่อฉันผูกกล่องข้อความชื่อผู้ใช้กับตัวแปรชื่อผู้ใช้ใน ViewModel ดูเหมือนว่าจะไม่อัปเดตสตริงนั้น ถ้าฉันตั้งชื่อผู้ใช้ใน ViewModel เป็นบางอย่าง ให้พูดว่า "ทดสอบ" มันจะแสดงในกล่องข้อความ แต่ไม่มีการอัปเดตในกล่องข้อความนั้น การเปลี่ยนแปลงชื่อผู้ใช้ใน ViewModel

ผลลัพธ์ที่ฉันได้รับเมื่อแก้ไขทั้งสองกล่องนี้คือ:

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

แม้ว่ากล่องข้อความชื่อผู้ใช้จะมีบางอย่างอยู่ในนั้นก็ตาม เหตุผลเดียวที่บรรทัดแก้ไขข้อบกพร่องเหล่านี้ปรากฏขึ้นเลยก็คือวิธีที่ฉันจัดการรหัสผ่านที่เรียก canExecute แต่วิธีที่ฉันจัดการชื่อผู้ใช้ไม่ได้ไกลเท่าที่ฉันเข้าใจเท่าที่ควร

เหตุใด "{Binding Username, UpdateSourceTrigger=PropertyChanged}" จึงไม่ส่งข้อความกล่องข้อความชื่อผู้ใช้ไปยังตัวแปรชื่อผู้ใช้ใน ViewModel


person Eric B    schedule 05.10.2016    source แหล่งที่มา
comment
แล้ว BindableBase ล่ะ - ใคร ๆ ก็สามารถรันโค้ดของคุณโดยไม่มีมันได้อย่างไร และมีความรู้อะไรบ้างว่า SetProperty() มีเวทย์มนตร์ประเภทใดที่อยู่เบื้องหลัง?   -  person RTDev    schedule 05.10.2016
comment
เนื่องจาก BindableBase เป็นส่วนหนึ่งของ Prism ซึ่งฉันระบุไว้โดยเฉพาะว่าฉันใช้และใส่แท็ก   -  person Eric B    schedule 05.10.2016
comment
ตกลง ไม่มีปริซึม ดังนั้นจากสิ่งที่ฉันเห็นเท่านั้น ฉันขอแนะนำให้ตั้งค่า Mode=TwoWay ให้กับการเชื่อมโยงของคุณ   -  person RTDev    schedule 05.10.2016
comment
ฉันได้อัปเดตคำถามพร้อมประกาศเกี่ยวกับ BindableBase และลิงก์ไปยังหน้า GitHub ขอโทษด้วยกับเรื่องนั้น.   -  person Eric B    schedule 05.10.2016
comment
@RTDev ว้าวนั่นทำได้ คุณนายสุดยอดมาก หากคุณต้องการใส่สิ่งนั้นเป็นคำตอบ ฉันสามารถทำเครื่องหมายว่าตอบแล้วได้   -  person Eric B    schedule 05.10.2016
comment
ดีใจที่ได้ยินเช่นนั้น ฉันกำลังจะเรียกใช้โค้ดของคุณและทดสอบ ;)   -  person RTDev    schedule 05.10.2016


คำตอบ (1)


เป็นเพราะการตั้งค่าโหมดเริ่มต้นสำหรับ Binding คือ OneWay ดังนั้นการอัปเดตจากโค้ดหนึ่งไปอีกเค้าโครงหนึ่งจึงทำงานได้ดี แต่จากเค้าโครงหนึ่งไปยังอีกโค้ดจะไม่ทำงาน ตั้งค่า Mode=TwoWay เพื่อให้มันทำงานทั้งสองทาง

"{Binding Username, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
person RTDev    schedule 05.10.2016
comment
การตั้งค่าโหมดเริ่มต้นสำหรับ x:Bind คือ OneTime ซึ่งแตกต่างจาก Binding - person Decade Moon; 06.10.2016
comment
จริง แต่คุณจะไม่ใช้ x:Bind ในกรณีนี้ - เนื่องจากไม่รองรับ UpdateSourceTrigger - person RTDev; 06.10.2016