如何使用仅在XAML中单击按钮来设置控件的用户可编辑属性

时间:2015-12-03 21:22:26

标签: wpf xaml eventtrigger

具体来说,我试图创建一个"重置"将滑块的值设置为零的按钮。我不想要代码隐藏,因为模板可以自定义。

我尝试了以下内容。如果没有FillBehavior="Stop",则用户不再可以移动滑块。使用FillBehavior="Stop"时,该值立即返回到前一个值(给出它根本不执行任何操作的外观)

    <Slider Name="MySlider" Minimum="-5" Maximum="5" Value="{Binding FloatProperty}"></Slider>
    <Button Content="Reset">
        <Button.Triggers>
            <EventTrigger RoutedEvent="Button.Click">
                <BeginStoryboard>
                    <Storyboard FillBehavior="Stop">
                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="MySlider" Storyboard.TargetProperty="Value">
                            <DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
                        </DoubleAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
        </Button.Triggers>
    </Button>

我也尝试在Slider绑定到按钮的IsPressed属性上使用DataTrigger,但我也无法使其工作。它将返回先前的值。

请注意,Slider的值绑定到数据的属性。

谢谢!

1 个答案:

答案 0 :(得分:0)

对您的XAML进行少量编辑似乎可以解决问题。您需要按特定的源名称过滤Button.Click事件,如下所示。此外,从Slider.Value中删除绑定似乎有所帮助。

我不太确定如何最好地维护Binding并仍然能够使其正常工作。

<Slider Name="MySlider"
        Maximum="5"
        Minimum="-5"/>
<Button Name="myButton" Content="Reset">
    <Button.Triggers>
        <EventTrigger RoutedEvent="Button.Click" SourceName="myButton">
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation Duration="0"
                                     Storyboard.TargetName="MySlider"
                                     Storyboard.TargetProperty="Value"
                                     To="0" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Button.Triggers>
</Button>

如果目标是将Slider.Value数据绑定到某些内容,例如......

<Slider Value="{Binding FloatProperty}"/>

...然后我怀疑(尽管我并非100%肯定)你想要做的事情可能是不可能的。

首先,让我解释为什么你看到滑块被冻结&#34;。这不是一个错误 - 我只需要仔细阅读文档以了解发生了什么。

当FillBehavior设置为Stop时,动画将在TimeLine完成时恢复该值(到动画开始之前的任何值)。

另一方面,当FillBehavior为HoldEnd时(这是未明确指定FillBehavior时的默认行为),DoubleAnimation将继续保持结束值并防止其他更改来源(如滑块手动移动)。因此,每次移动滑块时,动画都会将其重置为0,因为HoldEnd。这就是为什么你看到滑块表现得好像它被冻结了#34;在0.

我在Button.IsPressed = True上使用DataTrigger进行了调查。这通常在Style或ControlTemplate中完成 - 它看起来像这样:

         <Slider Maximum="5"
                Minimum="-5"
                Value="{Binding FloatProperty}">
            <Slider.Template>
                <ControlTemplate TargetType="Slider">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                        <Slider x:Name="Slider"
                                Grid.Row="0"
                                Maximum="{TemplateBinding Maximum}"
                                Minimum="{TemplateBinding Minimum}"
                                Value="{Binding RelativeSource={RelativeSource TemplatedParent},
                                                Path=Value,
                                                Mode=TwoWay}" />
                        <Button x:Name="ResetButton"
                                Grid.Row="1"
                                Content="Reset" />
                    </Grid>
                    <ControlTemplate.Triggers>
                        <DataTrigger Binding="{Binding ElementName=ResetButton, Path=IsPressed}" Value="True">
                            <Setter TargetName="Slider" Property="Value" Value="0" />
                        </DataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Slider.Template>
        </Slider>

不幸的是,Dependency Property Value Precedence rules认为TemplatedParent模板属性的优先级高于Triggers和Style setter。因此,请点击“重置”按钮。按钮会暂时将滑块值更改为0,但会立即重置回原来的值,该值由相应的Binding支持。

用尽动画和数据触发器,我得出的结论是,这可能是不可能的。也就是说,鉴于WPF中存在的各种功能,我很可能错了......

<小时/> 编辑:12/5/2015

我认为我有一个满足您关键要求的解决方案 - 即允许设计师修改UI,包括&#34; reset&#34;滑块的价值。也就是说,这个解决方案需要代码隐藏 - 但是这样的代码隐藏不会阻止XAML完全表达您的意图。

我们的想法是写一个行为,并使用该行为将Button与Slider相关联,并指示此Button应触发“重置”&#39;行动。

行为的基本轮廓如下所示:

    /// <remarks>
    /// Requires System.Windows.Interactivity.dll to be 
    /// added to the References section of the Project
    /// </remarks>
    public class SliderResetBehavior: Behavior<Button>
    {
        protected override void OnAttached()
        {
            // AssociatedObject refers to a Button instance
            // to which this Behavior is attached
            AssociatedObject.Click += OnClick;
        }

        /// <summary>
        /// Upon receiving a Click event, reset the Slider 
        /// </summary>
        private void OnClick(object sender, RoutedEventArgs e)
        {
            if(Slider != null)
            {
                Slider.Value = ResetValue;
            }
        }

        #region Dependency Property - ResetValue

        /// <summary>
        /// Stores a double that will act as the definition of 'reset' 
        /// value for the Slider associated with this Behavior. 
        /// </summary>
        public double ResetValue
        {
            get { return (double)GetValue(ResetValueProperty); }
            set { SetValue(ResetValueProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ResetValue.
        // This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ResetValueProperty =
            DependencyProperty.Register(
                "ResetValue", 
                typeof(double), 
                typeof(SliderResetBehavior), 
                new PropertyMetadata(0.0));

        #endregion

        /// <summary>
        /// Maintains a System.Windows.Controls.Slider object upon 
        /// which this Behavior will act upon. 
        /// </summary>
        #region Dependency Property - Slider 
        public Slider Slider
        {
            get { return (Slider)GetValue(SliderProperty); }
            set { SetValue(SliderProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Slider.
        // This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SliderProperty =
            DependencyProperty.Register(
                "Slider", 
                typeof(Slider), 
                typeof(SliderResetBehavior), 
                new PropertyMetadata(default(Slider)));

        #endregion
    }

XAML现在可以很简单,就像这样:

<StackPanel>
    <Slider x:Name="Slider" Minimum="-5" Maximum="5" Value="{Binding SliderValue}"/>
    <!-- 
       Requires xmlns entries like this:
        xmlns:local="clr-namespace:<Namespace containing SliderResetBehavior>"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    -->
    <Button x:Name="button" Content="Reset">
        <i:Interaction.Behaviors>
            <local:SliderResetBehavior ResetValue="0" Slider="{Binding ElementName=Slider}"/>
        </i:Interaction.Behaviors>
    </Button>
</StackPanel>

上面显示的这个XAML保留了设计者可以完全自定义的关键属性,只使用松散的XAML,并允许Slider.Value的数据绑定。