【问题标题】:Handling a Button's Click event in XAML?在 XAML 中处理按钮的 Click 事件?
【发布时间】:2014-06-03 09:41:51
【问题描述】:

这感觉像是一个非常基本的问题,但我相信有更好的方法来做到这一点。我的 UI 中有一个Button,它选择一个特定的选项卡并从ViewModel 触发一个Command

这是当前代码(可以正常工作):

XAML:

<Button Content="Button!" Click="OnButtonClick" Command="{Binding WhateverCommand}" />

后面的代码:

private void OnButtonClick(object sender, RoutedEventArgs e)
{
     theTab.IsSelected = true;
}

难道没有更简洁、仅限 XAML 的方式来执行该 UI 操作吗?我正在考虑类似的事情:

<Button Content="Button!" Click="OnButtonClick" Command="{Binding WhateverCommand}">
    <Button.Triggers>
        <EventTrigger RoutedEvent="Click">
            <Setter TargetName="theTab" Property="IsSelected" Value="True" />
        </EventTrigger>
    </Button.Trigger>
</Button>

但不幸的是,EventTrigger 似乎不支持Click 事件。为什么这样?在 WPF 工作了几年后,我有时仍然对触发器感到困惑,这几乎可以概括。尝试构建时,Setter 行出现错误:

A value of type 'Setter' cannot be added to a collection or dictionary of type 'TriggerActionCollection'.

谢谢!

编辑因为我被问到我的窗口的 XAML 结构,它看起来像这样:

<DockPanel LastChildFill="True">
    <Ribbon DockPanel.Dock="Top">
        <Button Content="Button!" Click="OnButtonClick" Command="{Binding WhateverCommand}" />
    </Ribbon>
    <TabControl>
        <TabItem x:Name="theTab" />
    </TabControl>
</DockPanel>

【问题讨论】:

  • 您可以发布您的完整 xaml,包括您正在谈论的选项卡吗?
  • 我相信this 和/或this 可能是您的答案...(也可以使用 Button.Click 而不是 Click)
  • 这与问题本身无关。你需要吗?
  • 是的。我想查看按钮在哪里以及选项卡在哪里的层次结构?
  • @Damascus 我同意,它看起来非常复杂,但这就是它可以做到的方式:) 你可以创建一个故事板资源,并在触发器中使用它,它看起来会更简单一些。

标签: c# wpf xaml


【解决方案1】:

错误总结了它。您不能在EventTrigger 中使用Setter。如果您想在 XAML 中执行此操作,您可以使用Storyboard,它会在按下按钮时选择给定的选项卡。像这样的:

<Button Content="Click">
   <Button.Triggers>
       <EventTrigger RoutedEvent="Button.Click">
           <BeginStoryboard>
               <Storyboard>
                   <BooleanAnimationUsingKeyFrames Storyboard.TargetName="theTab" Storyboard.TargetProperty="IsSelected">
                       <DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True"/>
                   </BooleanAnimationUsingKeyFrames>
               </Storyboard>
           </BeginStoryboard>
       </EventTrigger>
   </Button.Triggers>
</Button>

【讨论】:

  • 有人也将此作为评论发布,我担心的是:为了简单起见,为什么 StoryboardAnimation 比代码隐藏 @987654327 更简单/干净@?如果这是做到这一点的唯一方法,我想我可能会坚持使用代码隐藏方式。在接受您的回答之前,我会保持问题开放,看看是否还有其他问题:) 谢谢!
  • 你是对的,在这种情况下,后面的代码更简单。问题是正常的Trigger 有状态的概念,所以Setter 可以撤消,但EventTrigger 没有这样的概念,所以你所能做的就是在它发生时开始一些动作。
  • 哦,原来如此。我总是对所有这些Triggers 可能性感到困惑。为什么Style 中的那些会有所不同!我会尝试一个更短的解决方案。也许通过创建一个单独的Storyboard 派生类
【解决方案2】:

肯定有更好的方法。在Windows.Interactivity 程序集的帮助下,您可以将事件源绑定到单个类,仅包含相关操作。有了这个,你几乎可以做你想做的一切。

动作类必须派生自 TriggerAction。通过覆盖Invoke-方法,您可以指定操作。

尽管有这种情况,也可以将EventTrigger 绑定到命令(例如中继命令),从而实现干净的 MMVM。

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

<Button x:Name="button">
                        <i:Interaction.Triggers>
                            <i:EventTrigger SourceName="button" EventName="Click">
                                <app:MyAction/>
                            </i:EventTrigger>
                        </i:Interaction.Triggers>
                    </Button>
Public Class MyAction
    Inherits Interactivity.TriggerAction(Of UIElement)

    Protected Overrides Sub Invoke(parameter As Object)
        MsgBox("Clicked")
    End Sub

End Class

我更新了代码以满足您的特定要求。 TriggerAction 类现在还包含一个依赖属性,可以在您的选项卡控件中找到它:

<TabControl x:Name="tab"/>
                    <Button x:Name="button">
                        <i:Interaction.Triggers>
                            <i:EventTrigger SourceName="button" EventName="Click">
                                <app:MyAction Target="{Binding ElementName=tab}"/>
                            </i:EventTrigger>
                        </i:Interaction.Triggers>
                    </Button>
Public Class MyAction
    Inherits Interactivity.TriggerAction(Of UIElement)

    Protected Overrides Sub Invoke(parameter As Object)
        DirectCast(Target, TabControl).SelectedIndex = 0
    End Sub

    Shared Sub New()
        _targetProperty = DependencyProperty.Register(
          "Target",
          GetType(UIElement),
          GetType(MyAction),
          New UIPropertyMetadata(Nothing))
    End Sub

    Private Shared _targetProperty As DependencyProperty
    Public Shared ReadOnly Property TargetProperty As DependencyProperty
        Get
            Return _targetProperty
        End Get
    End Property

    Property Target As UIElement
        Get
            Return DirectCast(GetValue(TargetProperty), UIElement)
        End Get
        Set(value As UIElement)
            SetValue(TargetProperty, value)
        End Set
    End Property

End Class

【讨论】:

  • 太棒了 :-) 请注意我更新了代码以处理 TabControl。
【解决方案3】:

您可以将bool 属性IsMyTabSelected 引入您的VM,将其绑定到TabItem.IsSelected

<TabItem x:Name="theTab" IsSelected="{Binding IsMyTabSelected}" >

然后您只需在WhateverCommand 处理程序中为Button 设置此标志。 注意:IsMyTabSelected 属性必须实现 System.ComponentModel.INotifyPropertyChanged

【讨论】:

  • 那么它不是 XAML-Only!在我的 ViewModel 中保留 IsMyTabSelected 纯 UI 属性也没有任何意义,因为它可以在后面的 XAML 代码中切换。有趣的是,对于这样一个简单的问题,每一个解决方案似乎都同样不适应:/
  • 不仅仅是 XAML,也没有代码隐藏和简单。此外,在大型项目中让ViewModel 远离“纯 UI”是很困难的(不可能?)。
猜你喜欢
  • 2015-06-22
  • 1970-01-01
  • 1970-01-01
  • 2012-04-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-12
相关资源
最近更新 更多