切换按钮双向绑定不起作用(通用Windows平台)

时间:2015-11-04 22:17:44

标签: c# wpf windows xaml win-universal-app

我正在尝试将ToggleButton上的“IsChecked”属性绑定到“ModelView.IsEnabled”。
“ModelView.IsEnabled”总是“假”
但不知何故,ToggleButton仍然可以显示为“已检查” 绑定有什么问题吗?
enter image description here

XAML

...
<Page.Resources>
    <ModelView:ModelView x:Key="ModelView"/>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <ToggleButton IsChecked="{Binding Source={StaticResource ModelView}, Path=IsEnabled, Mode=TwoWay}">
        <TextBlock >UWP Toggle Button</TextBlock>
    </ToggleButton>
</Grid>
...

ModelView.cs

using...

namespace App2
{
    class ModelView : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public event EventHandler CanExecuteChanged;

        private bool _isEnabled;

        public bool IsEnabled
        {
            get {
                return _isEnabled;
            }
            set
            {
                _isEnabled = false;
                OnPropertyChanged("IsEnabled");
            }
        }

        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
    }
}

4 个答案:

答案 0 :(得分:2)

试试这个,它对我有用:  1. Xaml代码更改:

    <Grid>
    <Grid.DataContext>
        <soHelpProject:MainViewModel/>
    </Grid.DataContext>
    <ToggleButton IsChecked="{Binding IsToggled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
        <TextBlock >UWP Toggle Button</TextBlock>
    </ToggleButton>
</Grid>

的问候,

答案 1 :(得分:1)

在您的班级buildPrattParser table termP = parser precs where precs = reverse table prefixP = choice prefixPs <|> termP where prefixPs = do precsR@(ops:_) <- tails precs Prefix opP <- ops return $ opP <*> parser precsR infixP precs lhs = choice infixPs <|> pure lhs where infixPs = do precsR@(ops:precsL) <- tails precs op <- ops p <- case op of Infix opP assoc -> do let p precs = opP <*> pure lhs <*> parser precs return $ case assoc of AssocNone -> error "Non associative operators are not supported" AssocLeft -> p precsL AssocRight -> p precsR Postfix opP -> return $ opP <*> pure lhs Prefix _ -> mzero return $ p >>= infixP precs parser precs = prefixP >>= infixP precs 中,从此处更改ModelView

IsEnabled

到此:

 public bool IsEnabled
    {
        get {
            return _isEnabled;
        }
        set
        {
            _isEnabled = false;
            OnPropertyChanged("IsEnabled");
        }
    }

enter image description here

编辑:如果我按照您的建议使用 public bool IsEnabled { get { return _isEnabled; } set { _isEnabled = value; OnPropertyChanged("IsEnabled"); } } ,它仍然有效,按钮和状态现在显示相反的值:

enter image description here

编辑2 :现在,如果您想正确测试绑定,那么您可以添加一个额外的常规按钮并执行此操作:

_isEnabled = !value;

因此,每次点击 private void button1_Click(object sender, RoutedEventArgs e) { myModelView.IsEnabled = !myModelView.IsEnabled; } 时,您都可以在ToggleButtontrue之间切换false。请注意Test Button不受任何约束,仅用于测试目的。请参阅底部的相应XAML。

enter image description here

问题在于你的方式,“强迫”Test Button总是IsEnabled,你实际上在破坏自己的代码...:O)

最后,您的代码在何时/何地分配您的false时并不清楚。请看下面的操作方法。

XAML:

DataContext

代码隐藏:

<Page.DataContext>
    <local:MyModelView/>
</Page.DataContext>

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <ToggleButton x:Name="toggleButton1" Content="ToggleButton" IsChecked="{Binding IsEnabled, Mode=TwoWay}" HorizontalAlignment="Center"/>
    <TextBlock x:Name="textBlock1" Text="{Binding IsEnabled}" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="126,0,201,286" />
    <Button x:Name="button1" Click="button1_Click" Margin="127,400,0,220" Content="Test Button" Height="35" />
</Grid>

答案 2 :(得分:0)

IsEnabled属性指示用户是否可以与控件进行交互IsPressed只读属性。所以 IsChecked 可能就是您所需要的。

答案 3 :(得分:0)

我遇到了同样的问题,不管是ToggleButton,还是TextBox,我想要格式化用户输入的文字。

在您的情况下,您希望更改视图模型中的IsChecked属性并立即将其反映在用户界面中(因此始终取消选中)。你想要的原因绝对不重要。

问题在于,使用UWP时,您单击ToggleButton时会调用属性的getter。 ToggleButton的正常操作是从未选中更改为已选中(反之亦然),这就是您的情况。但是,您希望NotifyPropetyChanged表示UI中的控件。那就是出错的地方。执行setter时,getter永远不会被调用(包括NotifyPropertyChanged),因此UI不能反映你在setter中所做的事情。 这与TwoWay Binding过去做的非常不同(在WPF中仍然如此)。所以你的代码没有任何问题,但似乎绑定机制发生了变化,尽管微软声称它没有。如果你使用x:Bind,它可以正常工作,所以帽子可以解决你的问题。

为了澄清事情,我已经采取了你的例子并稍微修改了它,以显示问题。 我已经在页面上放置了一个ToggleButton,其中包含与视图模型的TwoWay绑定,就像您一样。单击ToggleButton将其状态从已检查切换为未选中,反之亦然,即使我的viewmodel中的setter始终将属性设置为false(因此未选中)。 但是我还添加了一个普通按钮,我已经绑定了一个命令,该命令也修改了ToggleButton绑定的属性。单击此按钮可调用ToggleButton绑定的属性上的setter。当然,setter的调用方式是相同的,但之后会调用ToggleButton的绑定,因此在这种情况下NotifyPropertyChanged会导致UI更新。

如果你使用调试器,你可以看到我的意思。 所以你的问题可以通过使用x:Bind来解决,或者通过找出另一种更新UI的方法来解决,如果Binding仍然像以前一样工作,你不应该这样做。也许微软已经实现了某种优化,现在破坏了经典的Binding。

没有特别的东西,只有MainPage和viewmodel。

我的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();
        }
    }
}