In WPF, a button can be bound to a command.
<Button Command="{Binding DoSomething}">Click me!</Button>
Now I want to do the same to a TabItem:
<TabItem Header="A little tab" ???="{Binding DoSomething}">...</TabItem>
What should ??? be? Or is there another way to do it?
It depends on what do you want to achieve. TabItems have the IsSelected property
IsSelected="{Binding IsSelected}"
that can be bounded TwoWay, and can be used to signal stuff to the ViewModel.
You could also use the fact that you can override the header of the TabItem, and bound it to a command, using Interactivity.
<TabItem TabIndex="0"
Tag="{Binding CurrentPrinterStatus}">
<TabItem.Header>
<Grid Background="Transparent">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<i:InvokeCommandAction Command="{Binding DoSomething}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock Style="{StaticResource TextBlockSelectedStyle}"
Text="Printers"/>
</Grid>
</TabItem.Header>
Other solutions are to use the SelectionChanged event of the TabControl, and that could allow you to find the ViewModel of the TabItem currently selected.
Hope this ideas atleast help you get a solution to your problem.
Related
I have a user control that use another user control. The child user control has a combobox and the click event works because i can open the combobox, but the double click it doesn't work.
My code is this:
Main user control:
<StackPanel>
<views:ucMyChildUserControl/>
</StackPanel>
My child user control:
<StackPanelOrientation="Horizontal">
<StackPanel Orientation="Vertical">
<Label Content="Content" Style="{StaticResource LabelDefault}"/>
<ComboBox Name="cmbMyCombobox"/>
</StackPanel>
<!--More related controls-->
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding MyCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</StackPanel>
But I have realized that if the comand of mouse double click is set in the parent user control, it works:
<views:ucChildUserControl>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding MyCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</views:ucChildUserControl>
So I guess the problem is about the handling of the event, but I don't know how to catch it in the child user control.
Thanks.
The issue is that the MouseDoubleClick event and the PreviewMouseDoubleClick are defined on Control. Since UserControl is a derivative of Control, the event is available and can be handled. However, StackPanel is not a derivative of Control so the event is not available, therefore the trigger does not work.
There are workarounds to this in code-behind that you can eventually turn into a behavior for MVVM, but simple workarounds like using input bindings on the left mouse click action only work on some elements, as others like ComboBox will already handle it and then the input bindings are not triggered.
<StackPanel Orientation="Horizontal">
<StackPanel.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick"
Command="{Binding MyCommand}"/>
</StackPanel.InputBindings>
<StackPanel Orientation="Vertical">
<Label Content="Content"/>
<ComboBox Name="cmbMyCombobox"/>
</StackPanel>
<!--More related controls-->
</StackPanel>
The most simple solution without creating additional code is to wrap the StackPanel in a control that is a derivative of Control, e.g. a ContentControl, like this.
<ContentControl>
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical">
<Label Content="Content"/>
<ComboBox Name="cmbMyCombobox"/>
</StackPanel>
<!--More related controls-->
</StackPanel>
<b:Interaction.Triggers>
<b:EventTrigger EventName="MouseDoubleClick">
<b:InvokeCommandAction Command="{Binding MyCommand}" />
</b:EventTrigger>
</b:Interaction.Triggers>
</ContentControl>
I have a control tree like so (properties removed for brevity):
<ListView.ItemTemplate>
<DataTemplate>
<Border>
<Button>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseRightButtonUp">
<Command:EventToCommand Command="{Binding SelectCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Button.Template>
<ControlTemplate>
<DockPanel>
<Button>
<Button.ContextMenu>
and I am struggling to stop the SelectCommand firing when the inner button is right-clicked to bring up the Context Menu.
Is there a way to do this without having to implement a custom ContextMenu?
Any help or pointers much appreciated.
Thanks.
I have a TabControl with a few TabItems. I want one of my TabItems to act as a button. When I click on the TabItem, I want it to execute a Command in my associated ViewModel. I have the following code in my View:
<TabItem Header="Manage Users" Visibility="{Binding IsAdmin, Converter={StaticResource VisibilityOfBool}}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding Path=OpenLoginCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TabItem>
The OpenLoginCommand is an ICommand in the ViewModel. I have the interactivity namespace defined. What am I missing here?
Try PreviewMouseLeftButtonDown
<TabItem Header="Manage Users" Visibility="{Binding IsAdmin, Converter={StaticResource VisibilityOfBool}}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding Path=OpenLoginCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TabItem>
Try using MouseDown instead of MouseLeftButtonDown as referencing MSDN the latter event doesn't exist on a TabItem control.
If your requirements insist on left-button only, then check the state of the mouse within the command.
There is a Treeview Control.
<TreeView Name="ProductsHierarchy" FontFamily="Arial"
Background="White" Margin="2"
FontSize="12" SelectedItemChanged ="ProductsHierarchy_SelectedItemChanged">
Is there a way to bind a command for SelectedItemChanged event of the treeview, avoiding the code behind event handler?
Try MVVM Toolkit's EventToCommand.
"Built-in" (from Blend) approach is to use Interactivity
<TreeView Name="ProductsHierarchy" FontFamily="Arial"
Background="White" Margin="2"
FontSize="12" SelectedItemChanged ="ProductsHierarchy_SelectedItemChanged">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}" CommandParameter="argument"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
You must include namespace:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
The disadvantage here is that you have no access to EventArgs. Here's the solution (it's in Polish, but code samples are pretty much self-explanatory).
Seems like this would be a common problem...
I'm using MVVM Light
I have a listbox defined in my xaml
<ListBox ItemsSource="{Binding Events}" SelectedItem="{Binding SelectedEvent, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<cmd:EventToCommand Command="{Binding EventPageCommand, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="0,0,0,22">
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Preview}" TextWrapping="Wrap"/>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This works all fine and dandy, except the user is able to click below this list and the event is still fired.
I can modify it to use SelectionChanged such as:
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding EventPageCommand, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
But then if I use the back button, I cannot select the same item twice since the selection hasn't actually changed. I could probably go into my codebehind and set the selected index = -1 on page load, but this seems like quite a hack, and I use list boxes in quite a few places and I would hate to have to remember to do this hack everywhere.
Any ideas?
edit
I added background color to my listbox and my listbox items and while my listbox items only take up what space the text occupies, the listbox takes up the entire screen, extending below the last listbox item. Perhaps one way would be to shore up this extra space.
update
what seems to work best so far is to set the VerticalAlignmnet="Top" on the listbox (this shores up the extra space on the bottom, not sure why, but then scrolling ends at the bottom of the list and not the bottom of the screen). I then disabled the scrolling on the listbox itself ScrollViewer.VerticalScrollBarVisibility="Disabled" and wrapped the whole thing in a <ScrollViewer>
Currently you are looking for the Tap event on the whole of the list box, not a individual item.
This is how I typically I use interaction triggers:
<DataTemplate DataType="{x:Type ViewModel:DataItem}" x:Key="ItemTemplate">
<ContentControl>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<i:InvokeCommandAction Command="{Binding Tap}"/>
</i:EventTrigger>
<i:EventTrigger EventName="KeyUp">
<i:InvokeCommandAction
Command="{Binding RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBox}},
Path=DataContext.KeyUpCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBox Text="{Binding Name}"/>
</ContentControl>
</DataTemplate>
In this example the Tap event binds to a command on the DataContext (a data item) of an ItemsControl, whereas the KeyUp event binds to a command on the parent DataContext of the ListBox.
If all you want is to get an event when the user taps an item, why don't you use the trigger code you have on the Border element of the template?
You probably will have to use the Source property to bind the command to the ViewModel, but that can also be easely achieved (will just depend on how you are setting the Page DataContext right now)