尝试将文本框与视图模型绑定但未发生更新
Attempting to bind textbox with viewmodel but no update occuring
我设置了一个小型测试项目并创建了一个需要用户名和密码的登录页面。我正在使用 MVVM 方法和 Prism 框架。我一直在关注这个视频 (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 中的用户名设置为某些内容,比如 "test",它会在文本框中显示它,但没有更新该文本框会更改 ViewModel 中的用户名变量。
我修改这两个框时得到的输出是:
Username: , password: d
Username is not null or whitespace? False, and password: True
即使用户名文本框中有内容。这些调试行出现的唯一原因是我处理密码的方式是调用 canExecute,但我处理用户名的方式并没有,尽管据我所知它应该。
为什么 "{Binding Username, UpdateSourceTrigger=PropertyChanged}"
不将用户名文本框文本发送到 ViewModel 中的用户名变量?
这是因为绑定的默认模式设置是单向的 - 所以从代码更新到布局可以正常工作,但从布局到代码则不行。
设置 Mode=TwoWay 使其双向工作。
"{Binding Username, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
我设置了一个小型测试项目并创建了一个需要用户名和密码的登录页面。我正在使用 MVVM 方法和 Prism 框架。我一直在关注这个视频 (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 中的用户名设置为某些内容,比如 "test",它会在文本框中显示它,但没有更新该文本框会更改 ViewModel 中的用户名变量。
我修改这两个框时得到的输出是:
Username: , password: d
Username is not null or whitespace? False, and password: True
即使用户名文本框中有内容。这些调试行出现的唯一原因是我处理密码的方式是调用 canExecute,但我处理用户名的方式并没有,尽管据我所知它应该。
为什么 "{Binding Username, UpdateSourceTrigger=PropertyChanged}"
不将用户名文本框文本发送到 ViewModel 中的用户名变量?
这是因为绑定的默认模式设置是单向的 - 所以从代码更新到布局可以正常工作,但从布局到代码则不行。 设置 Mode=TwoWay 使其双向工作。
"{Binding Username, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"