我遇到了同样的问题,不是ToggleButton,而是TextBox,我想格式化用户输入的文本。
在您的情况下,您希望更改视图模型中的 IsChecked 属性并立即将其反映在用户界面中(因此始终不要选中)。你想要那个的原因绝对不重要。
问题在于,使用 UWP 时,当您单击 ToggleButton 时,您的属性的 getter 会像您期望的那样被调用。 ToggleButton 的正常操作是从未选中更改为选中(反之亦然),这就是您的情况。但随后您期望 NotifyPropetyChanged 向 UI 中的控件发出信号。这就是它出错的地方。执行 setter 时永远不会调用 getter(包括 NotifyPropertyChanged),因此 UI 不会反映您在 setter 中所做的事情。
这与 TwoWay Binding 过去所做的(在 WPF 中仍然如此)非常不同。所以你的代码没有问题,但似乎绑定机制已经改变,尽管微软声称它没有。如果您使用x:Bind,它可以正常工作,所以帽子可能会解决您的问题。
为了更清楚地说明问题,我以您的示例为例并稍作修改,以显示问题。
我已经在页面上放了一个ToggleButton,并使用 TwoWay 绑定到一个视图模型,就像你做的那样。单击ToggleButton 会将其状态从选中切换到未选中,反之亦然,即使我的视图模型中的设置器始终将属性设置为 false(因此未选中)。
但我还添加了一个普通按钮,我已经绑定到一个命令,该命令还修改了ToggleButton 绑定到的属性。单击此按钮会调用ToggleButton 绑定到的属性上的设置器。当然,setter 的调用方式相同,但之后会调用与 ToggleButton 的绑定,因此在这种情况下,NotifyPropertyChanged 会导致 UI 更新。
如果你使用调试器,你可以明白我的意思。
因此,您的问题可以通过使用 x:Bind 或通过找出另一种更新 UI 的方法来解决,如果 Binding 仍然像以前那样工作,您就不必这样做。也许微软已经实施了某种优化,现在破坏了经典的 Binding。
没有特别的东西,只有一个 MainPage 和一个视图模型。
我的 MainPage.xaml 代码
<Page x:Class="App10.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:App10"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<local:ViewModel x:Key="viewModel" />
</Page.Resources>
<Grid x:Name="mainGrid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Margin="10,20,10,0">
<Button
x:Name="Button"
Content="UWP Normal button"
Command="{Binding Source={StaticResource viewModel}, Path=SwitchIschecked}"
HorizontalAlignment="Stretch" />
<ToggleButton
x:Name="toggleButton"
Margin="0,10,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
IsChecked="{Binding Source={StaticResource viewModel}, Path=IsChecked,
Mode=TwoWay}">
<TextBlock>UWP Toggle Button</TextBlock>
</ToggleButton>
</StackPanel>
</Grid>
</Page>
MainPage.xaml.cs 的代码
using Windows.UI.Xaml.Controls;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace App10
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
}
}
以及 ViewModel.cs 的代码
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace App10
{
public class ViewModel : INotifyPropertyChanged
{
private bool _isChecked;
// property for TwoWay binding with ToggleButton
public bool IsChecked
{
get
{
return _isChecked;
}
set
{
// extra var just to check 'value'
var _value = value;
// now always set it to false
_isChecked = false;
// Try to pass value of _isChecked to user interface
// because there is no check whether the value really
// has changed
// But this only works if the setter is not being called
// directly from the control the property is bound to
OnPropertyChanged();
}
}
private ICommand _switchChecked;
// ICommand for normal button, binding to Command
// calls method to set Property for ToggleButton
public ICommand SwitchIschecked
{
get
{
if ( _switchChecked == null )
_switchChecked = new ChangeChecked( new Action( ChangeVar ));
return _switchChecked;
}
set
{
_switchChecked = value;
}
}
// This will set the property for the ToggleButton
private void ChangeVar()
{
IsChecked = !IsChecked;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged( [CallerMemberName] string propertyName = null )
{
var handler = PropertyChanged;
handler?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
}
}
/// <summary>
/// Quick class to implement ICommand
/// </summary>
class ChangeChecked : ICommand
{
Action _execute;
public ChangeChecked( Action execute )
{
_execute = execute;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute( object parameter )
{
return true;
}
public void Execute( object parameter )
{
_execute();
}
}
}