IsChecked是在第二次而不是第一次单击时设置的(ToggleButton(XAML))

时间:2019-09-19 12:18:26

标签: c# wpf xaml

希望我的问题能够帮助我

我有两个按钮和一个矩形,我想做的是:  1.将矩形移至按钮的方向(RightButton将其向右移动,LeftButton则向左移动)(工作正常)  2.停用最后一次按下以移动矩形的按钮(矩形为右,禁用了RightButton),此外该按钮为红色。  3.激活之前已禁用的按钮(“矩形”处于右侧位置,“左按钮”处于活动状态),此外该按钮为绿色。

按钮的模板

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style TargetType="{x:Type ToggleButton}" x:Key="DefaultToggleButtonTemplate" x:Name="DefaultToggleButtonTemplate">
        <Style.Setters>
            <Setter Property="FontSize" Value="16"/>
            <Setter Property="FontFamily" Value="Arial"/>
            <Setter Property="Width" Value="100"/>
            <Setter Property="Height" Value="30"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalContentAlignment" Value="Top"/>


            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ToggleButton}" >
                        <Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" ClipToBounds="True">
                            <Rectangle x:Name="buttonFrame" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
                                   Stroke="{TemplateBinding Background}" RadiusX="5" RadiusY="5" StrokeThickness="1" Fill="Transparent"/>
                            <Rectangle x:Name="buttonBody" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                                   Stroke="Transparent" RadiusX="5" RadiusY="5" StrokeThickness="1" Fill="{TemplateBinding Background}"/>
                            <TextBlock x:Name="buttonText" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}"/>
                        </Grid>

                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style.Setters>
    </Style>
</ResourceDictionary>
    <Window x:Class="StyleResourceDictionariesDemo.MainWindow"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>

    </Window.Resources>
    <StackPanel>
        <Button Style="{StaticResource DefaultButtonTemplate}" Content="ok"/>
        <Button Style="{StaticResource DarkDefaultButtonTemplate}" Content="cancel"/>
        <Button Style="{StaticResource FreakShowButtonTemplate}" Content="FREak"/>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="2*"/>
                <RowDefinition Height="*"/>

            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <Grid.Triggers>
                <EventTrigger SourceName="RightButton" RoutedEvent="Button.Click">
                    <EventTrigger.Actions>
                        <BeginStoryboard>
                            <Storyboard>
                                <MatrixAnimationUsingPath Storyboard.TargetName="RectMatrixTransform" Storyboard.TargetProperty="Matrix" DoesRotateWithTangent="False" Duration="0:0:0.100">
                                    <MatrixAnimationUsingPath.PathGeometry>
                                        <PathGeometry>
                                            <PathFigure StartPoint="0,0">
                                                <LineSegment Point="100,0"/>
                                            </PathFigure>
                                        </PathGeometry>
                                    </MatrixAnimationUsingPath.PathGeometry>
                                </MatrixAnimationUsingPath>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>

                <EventTrigger SourceName="LeftButton" RoutedEvent="Button.Click">
                    <BeginStoryboard>
                        <Storyboard>
                            <MatrixAnimationUsingPath Storyboard.TargetName="RectMatrixTransform" Storyboard.TargetProperty="Matrix" DoesRotateWithTangent="False" Duration="0:0:0.100">
                                <MatrixAnimationUsingPath.PathGeometry>
                                    <PathGeometry>
                                        <PathFigure StartPoint="100,0">
                                            <LineSegment Point="0,0"/>
                                        </PathFigure>
                                    </PathGeometry>
                                </MatrixAnimationUsingPath.PathGeometry>
                            </MatrixAnimationUsingPath>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </Grid.Triggers>
            <ToggleButton Content="Right" Name="RightButton" Grid.Column="0" Grid.Row="0" >
                <ToggleButton.Style>
                    <Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource DefaultToggleButtonTemplate}">
                        <Style.Triggers>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsChecked" Value="True"/>
                                </MultiTrigger.Conditions>
                                <MultiTrigger.Setters>
                                    <Setter Property="IsEnabled" Value="False"/>
                                    <Setter Property="Background" Value="Red"/>
                                </MultiTrigger.Setters>
                            </MultiTrigger>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding ElementName=LeftButton, Path=IsChecked, NotifyOnSourceUpdated=True}" Value="True"/>
                                </MultiDataTrigger.Conditions>
                                <MultiDataTrigger.Setters>
                                    <Setter Property="IsChecked" Value="False"/>
                                    <Setter Property="IsEnabled" Value="True"/>
                                    <Setter Property="Background" Value="Green"/>
                                </MultiDataTrigger.Setters>
                            </MultiDataTrigger>

                        </Style.Triggers>
                    </Style>
                </ToggleButton.Style>
            </ToggleButton>
            <ToggleButton Content="Left" Name="LeftButton" Grid.Column="1" Grid.Row="0" Height="30" Width="100">
                <ToggleButton.Style>
                    <Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource DefaultToggleButtonTemplate}">
                        <Style.Triggers>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsChecked" Value="True"/>
                                </MultiTrigger.Conditions>
                                <MultiTrigger.Setters>
                                    <Setter Property="IsEnabled" Value="False"/>
                                    <Setter Property="Background" Value="Red"/>
                                </MultiTrigger.Setters>
                            </MultiTrigger>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding ElementName=RightButton, Path=IsChecked, NotifyOnSourceUpdated=True}" Value="True"/>
                                </MultiDataTrigger.Conditions>
                                <MultiDataTrigger.Setters>
                                    <Setter Property="IsChecked" Value="False"/>
                                    <Setter Property="IsEnabled" Value="True"/>
                                    <Setter Property="Background" Value="Green"/>
                                </MultiDataTrigger.Setters>
                            </MultiDataTrigger>
                        </Style.Triggers>
                    </Style>
                </ToggleButton.Style>
            </ToggleButton>



            <Rectangle x:Name="RectangleToMove" Grid.Column="0" Grid.Row="1" Height="100" Width="20"  Fill="Black" Stroke="Black">
                <Rectangle.RenderTransform>
                    <MatrixTransform x:Name="RectMatrixTransform"/>
                </Rectangle.RenderTransform>
            </Rectangle>



        </Grid>
    </StackPanel>
</Window>

当前行为是:  1.程序启动,按钮为灰色,矩形位于左侧,两个按钮均处于活动状态  2.按下一个按钮将导致矩形移动,将按下的按钮涂成红色(另一个涂成绿色),然后将其自身停用(激活另一个)。  3.现在按下另一个按钮,重置按钮的颜色(两个),将矩形移到另一个位置(两个按钮仍然处于活动状态)(这就是问题所在)  4.按下同一按钮将导致预期的行为2。

那么如何阻止3.的重置行为?

编辑:我能够找出问题的根源:在步骤3中,按按钮,不要将IsChecked-Property设置为true,在步骤4中,IsChecked设置为true。问题是,为什么在第3步中未将其设置为true,或者我怎么做才能实现呢?

1 个答案:

答案 0 :(得分:1)

代码中的基本问题是评估触发器的顺序以及触发器之间的交互方式。

程序启动时,未选中任何按钮,因此没有触发器适用。每个按钮均以默认状态显示。

当用户单击切换按钮时,首先发生的是切换按钮的状态。由于按钮以默认值false开始,因此其新值变为true。到那时,您的触发器开始生效。

例如,单击“右”按钮会将其自己的IsChecked值切换为true,并使“左”按钮的触发器将其IsChecked值设置为{{1} }。到目前为止,一切都很好。这可以实现您想要的:单击时有一个红色按钮,希望用户单击下一步时有一个绿色按钮。这是因为激活了“向右”按钮的false触发器(将其禁用并将其着色为红色),以及为“向左”按钮的IsChecked触发器已被激活(使其启用并为绿色着色) )。

但是现在单击“左”按钮时,出现了问题。 “左”按钮转换为已检查状态(RightButton.IsChecked变为IsChecked)。这将触发引用“左”按钮的“右”按钮的数据触发器,从而导致“右”按钮再次变为未选中状态。但是请回想一下,因为触发器引用了“右”按钮(在上一步中),所以“左”按钮最初只是被涂成绿色。现在不再选择“右键”按钮,该触发器将不再适用。 “左”按钮恢复为默认的无色状态!

更糟糕的是,当触发器停用时,它将使触发器集的所有属性返回到触发器被激活时的先前状态。因此,“左”按钮现在也将其true属性设置回了IsChecked(这是在激活触发器之前设置的属性)。现在,“右键”按钮的false触发器不再适用,并且该按钮返回其默认状态。

Ph!你遵循了所有这些吗?这有点令人费解,但是如果您跟踪与我上面描述的步骤相同的步骤(在执行过程中记下各种属性状态,以帮助跟踪情况),我认为它将变得很清楚。

现在,该怎么办?好吧,我觉得从根本上来说,问题是您滥用触发机制来尝试在XAML中执行程序操作。循环依赖会导致一些非常奇怪且难以调试的问题。

XAML并不是真的非常适合于程序任务。如果仅以声明方式使用它,则效果最佳。但是在一定程度上支持过程性任务的情况下,WPF中的动画功能才真正做到了这一点。因此,一种解决方案是将所需的行为转移到您已有的动画中:

LeftButton.IsChecked

此操作使用关键帧动画将<EventTrigger SourceName="RightButton" RoutedEvent="Button.Click"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="LeftButton" Storyboard.TargetProperty="IsChecked" Duration="0" FillBehavior="HoldEnd"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <s:Boolean>False</s:Boolean> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RightButton" Storyboard.TargetProperty="IsChecked" Duration="0" FillBehavior="HoldEnd"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <s:Boolean>True</s:Boolean> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> <MatrixAnimationUsingPath Storyboard.TargetName="RectMatrixTransform" Storyboard.TargetProperty="Matrix" DoesRotateWithTangent="False" Duration="0:0:0.100"> <MatrixAnimationUsingPath.PathGeometry> <PathGeometry> <PathFigure StartPoint="0,0"> <LineSegment Point="100,0"/> </PathFigure> </PathGeometry> </MatrixAnimationUsingPath.PathGeometry> </MatrixAnimationUsingPath> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> <EventTrigger SourceName="LeftButton" RoutedEvent="Button.Click"> <BeginStoryboard> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RightButton" Storyboard.TargetProperty="IsChecked" Duration="0" FillBehavior="HoldEnd"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <s:Boolean>False</s:Boolean> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="LeftButton" Storyboard.TargetProperty="IsChecked" Duration="0" FillBehavior="HoldEnd"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <s:Boolean>True</s:Boolean> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> <MatrixAnimationUsingPath Storyboard.TargetName="RectMatrixTransform" Storyboard.TargetProperty="Matrix" DoesRotateWithTangent="False" Duration="0:0:0.100"> <MatrixAnimationUsingPath.PathGeometry> <PathGeometry> <PathFigure StartPoint="100,0"> <LineSegment Point="0,0"/> </PathFigure> </PathGeometry> </MatrixAnimationUsingPath.PathGeometry> </MatrixAnimationUsingPath> </Storyboard> </BeginStoryboard> </EventTrigger> 属性强制为每个控件的适当值,以响应单击事件。

您可能会注意到我使用IsChecked而不是ObjectAnimationUsingKeyFrames。如果可以确保切换按钮的BooleanAnimationUsingKeyFrames始终具有非空值,则可以使用类型更强的动画。但是,为了重现您的确切行为,同时简化了按钮的视觉状态,我发现有必要将按钮的初始IsChecked状态默认设置为IsChecked,这与null元素不兼容(因为它仅支持不可为空的BooleanAnimationUsingKeyFrames值)。

我之所以陷入这种困境,是因为使用这种基于动画的方法,仅基于bool属性值进行简单的样式触发器以更新按钮的视觉状态就更有意义了。在您已经拥有的样式资源中,我添加了以下内容:

IsChecked

这样,您只需声明性地指定按钮在每种状态下的外观,而不是尝试响应 other 按钮状态的变化。简单得多。

(忽略额外的<p:Style.Triggers> <Trigger Property="IsChecked" Value="True"> <Setter Property="IsEnabled" Value="False"/> <Setter Property="Background" Value="Red"/> </Trigger> <Trigger Property="IsChecked" Value="False"> <Setter Property="IsEnabled" Value="True"/> <Setter Property="Background" Value="Green"/> </Trigger> </p:Style.Triggers> XML名称空间...我只是用它来解决Stack Overflow对XAML格式的错误处理,当它在XML中看到“样式”并正在处理时,会感到困惑使用XML。)

请注意,如果您不介意将切换按钮的状态初始化为p:true,以便应用其中一个或另一个触发器,则可以使用{{ 1}}元素,因为false属性将始终为非null值。但是,为了保留您的行为,即在按下按钮之前,按钮具有默认的无色状态,我将每个按钮初始化为BooleanAnimationUsingKeyFrames的{​​{1}}值:

IsChecked

请注意,由于您现在可以为两个按钮使用相同的样式,因此无需为每个按钮声明自定义样式。每个按钮都可以引用您已经创建的样式资源,并在其中添加了按钮视觉状态的设置器。

现在,所有这些都说明了:我在C#中比XAML舒服得多,而且我发现XAML不能很好地表达程序性内容,因此我不想在大多数时间用于此目的。如果我尝试实现类似您在此处的行为,那么我将在视图模型中完成所有过程方面的工作,然后让XAML处理 only 的视觉状态:当然,移动矩形,还要移动按钮对象的特定格式,即null状态的触发器。

然后,我不会添加故事板来基于按钮的单击来更新IsChecked状态,而是将所有逻辑放入视图模型中,由绑定到该对象的<ToggleButton Content="Right" Name="RightButton" Grid.Column="0" Grid.Row="0" IsChecked="{x:Null}" Style="{StaticResource DefaultToggleButtonTemplate}"/> <ToggleButton Content="Left" Name="LeftButton" Grid.Column="1" Grid.Row="0" Height="30" Width="100" IsChecked="{x:Null}" Style="{StaticResource DefaultToggleButtonTemplate}"/> 对象触发按钮的IsChecked属性。然后,该注释将通过绑定到按钮的自身属性来处理IsChecked属性的切换。

但是,如果您想将所有内容都放在XAML中,那么我上面描述的方法将适用于您的示例。