Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling a Button's Click event in XAML?

Tags:

c#

wpf

xaml

This feels like a terribly basic question but I am sure there is a better way to do this. I have a Button in my UI which selects a specific tab and fire a Command from the ViewModel

Here is the current code (which works fine):

XAML:

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

Code behind:

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

Isn't there any cleaner, XAML-only way to do that UI operation? I was thinking about something like:

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

But unfortunately it seems like the EventTrigger won't support a Click event. Why so? I am still sometimes confused with triggers after a few years working in WPF, and this pretty much sums it up. When trying to build that I have an error on the Setter line:

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

Thank you!

EDIT since I was ask the XAML structure of my Window, it looks like this:

<DockPanel LastChildFill="True">
    <Ribbon DockPanel.Dock="Top">
        <Button Content="Button!" Click="OnButtonClick" Command="{Binding WhateverCommand}" />
    </Ribbon>
    <TabControl>
        <TabItem x:Name="theTab" />
    </TabControl>
</DockPanel>
like image 420
Damascus Avatar asked Jun 03 '14 09:06

Damascus


2 Answers

Error sums it up. You cannot use Setter in EventTrigger. If you want to do it in XAML you can use Storyboard that will select given tab when button is pressed. Something like this:

<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>
like image 118
dkozl Avatar answered Oct 02 '22 12:10

dkozl


There definitely is a better way. With the help of the Windows.Interactivity assembly you are able to bind the event source to a singe class, containing only the associated action. With this you can almost do everything you ned.

The action class has to derive from TriggerAction. By overriding the Invoke-method you can specify the action.

Despite this scenario it also possible to bind the EventTrigger to a command (e.g. relay command), allowing a clean MMVM implementation.

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

I updated the code to meet your specific requirements. The TriggerAction class now also contains a dependency property, which can be cound to your tab control:

<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
like image 29
Dennis Kassel Avatar answered Oct 02 '22 12:10

Dennis Kassel