繁体   English   中英

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

[英]IsChecked is set on second and not on first click (ToggleButton (XAML))

希望您能帮助我解决我的问题:

我有两个按钮和一个矩形,我想做的是:1.将矩形移动到按钮的方向(RightButton 将其向右移动,LeftButton 向左移动)(效果很好) 2.停用按钮最后一次按下以移动矩形(矩形是正确的,RightButton 已停用),此外该按钮是红色的。 3. 激活之前未激活的按钮(矩形在右侧 position,LeftButton 处于活动状态),此外按钮颜色为绿色。

按钮的模板

<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. 程序启动,按钮为灰色,矩形在左侧 position,两个按钮都处于活动状态 2. 按下一个按钮导致矩形向方向移动,将按下的按钮着色为红色(另一个绿色)并停用本身(激活另一个)。 3. 现在按下另一个按钮,重置按钮 colors(两者),将矩形移动到另一个 position(两个按钮仍处于活动状态)(这就是问题) 4.按下相同的按钮会导致 2 的预期行为。

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

编辑:我能够隔离问题的根源:在第 3 步中按下按钮,不要将 IsChecked-Property 设置为 true,在第 4 步中,IsChecked 按预期设置为 true。 问题是,为什么在第 3 步中没有将其设置为 true,或者我可以做些什么来实现它?

代码中的基本问题是触发器的评估顺序以及它们如何相互交互。

当程序启动时,两个按钮都没有被选中,因此没有任何触发器适用。 每个按钮都显示在其默认的 state 中。

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

例如,单击“右”按钮会将其自身的IsChecked值切换为true并导致“左”按钮的触发器将其IsChecked值设置为false 到目前为止,一切都很好。 这可以满足您的要求:您在单击的位置获得一个红色按钮,在您希望用户下一步单击的位置获得一个绿色按钮。 这是因为“右”按钮的IsChecked触发器已激活(禁用它并将其着色为红色),以及“左”按钮的RightButton.IsChecked触发器(启用它并将其着色为绿色)。

但是现在单击“左”按钮时,go 错误。 “左”按钮转换为选中的 state ( IsChecked变为true )。 这会触发引用“左”按钮的“右”按钮的数据触发器,导致“右”按钮再次变为未选中状态。 但是回想一下,“左”按钮最初只被涂成绿色,因为触发器引用了“右”按钮(在上一步中)。 现在不再选中“右”按钮,该触发器不再适用。 “左”按钮恢复到默认的无色状态!

更糟糕的是,当触发器停用时,它会将触发器集的所有属性返回到触发器激活时它们以前的状态。 所以“左”按钮现在也将其IsChecked属性设置回false (这是在触发器激活之前设置的值)。 现在“右”按钮的LeftButton.IsChecked触发器不再适用,并且该按钮返回其默认 state。

呸? 您是否遵循了所有这些,这有点令人费解,但是如果您按照我上面描述的相同步骤进行跟踪(像 go 一样写下各种属性状态,以帮助跟踪事情)。 我想它会变得清晰。

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

XAML 根本不适合程序任务。 仅以声明方式使用时效果最佳。 但就支持程序任务而言,真正做到这一点的是 WPF 中的 animation 功能。 因此,一种解决方案是将您想要的行为移动到您已经拥有的动画中:

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

这使用关键帧 animation 将IsChecked属性强制为每个控件的适当值以响应单击事件。

您可能会注意到我使用ObjectAnimationUsingKeyFrames而不是BooleanAnimationUsingKeyFrames 如果您可以确保切换按钮的IsChecked始终具有非空值,则可以使用类型更强的 animation。 But, to reproduce your exact behavior, while simplifying the button visual state aspects, I found it necessary to default the button's initial IsChecked state to null , which is incompatible with the BooleanAnimationUsingKeyFrames element (because it only supports non-nullable bool values).

我发现自己陷入这种困境的原因是,使用这种基于动画的方法,基于IsChecked属性值制作简单的样式触发器来更新按钮的视觉 state 会更有意义。 在您已经拥有的样式资源中,我添加了以下内容:

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

这样,您只需以声明方式准确指定每个 state 中的按钮外观,而不是尝试响应其他按钮的 state 中的更改。 简单得多。

(Ignore the extra p: XML namespace...I just use that to workaround Stack Overflow's buggy handling of XAML formatting, where it get confused when it sees "style" in the XML and "forgets" it's dealing with XML.)

请注意,如果您不介意将切换按钮的状态初始化为truefalse ,以便应用这些触发器中的一个或另一个,那么您可以使用BooleanAnimationUsingKeyFrames元素,因为IsChecked属性始终是非空值。 但是,为了保持您的行为,其中按钮在按下任一按钮之前具有默认的无色 state ,我将每个按钮初始化为具有IsCheckednull值:

<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}"/>

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

现在,所有这些都说:我在 C# 中比 XAML 更舒服,而且我发现 XAML 并没有很好地表达程序上的东西,以至于我大部分时间都不想使用它。 Were I to try to implement a behavior like what you've got here, I would have done all of the procedural aspects in a view model, and left XAML to deal only with visual state: the animation to move the rectangle, of course,还有按钮对象的特定格式,即IsChecked state 的触发器。

然后,我不会添加故事板来根据按钮的单击更新IsChecked状态,而是将所有逻辑放入视图 model 中,由绑定到按钮的Command属性的ICommand object 触发。 然后,该注释将通过绑定到按钮的自己的属性来处理IsChecked属性的切换。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM