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>
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>
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With