how to get TabHeader on trigger - wpf

i have a tabcontrol. i'm trying to pass the tabcontrol as a parameter to figure out the selected tab item so i can get the tab header name. Binding this doesn't seem to work. ideas?
<TabControl Background="#FFF9F9F9" Height="650">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<n:ExecuteCommandAction Command="{Binding UpdateTabCommand}" Parameter="{Binding this}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
in my viewmodel constructor i have:
_updateTabCommand = new ActionCommand< TabControl>(UpdateTab);
private method:
public void UpdateTab(TabControl tabControl)
{
var tabItem = (TabItem)tabControl.SelectedItem;

1) Use ElementName binding.
Example:
<TabControl Background="#FFF9F9F9"
Height="650"
Name="TabControl1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding UpdateTabCommand}"
CommandParameter="{Binding ElementName=TabControl1}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TabControl>
2) Use FindAncestor binding:
Example:
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding UpdateTabCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TabControl}}" />
</i:EventTrigger>
</i:Interaction.Triggers>

First of all there is no such thing as {Binding this} in WPF. If you want to refer to the element on which you are setting Binding use RelativeSource binding with mode set to Self.
Second observation. Passing UI elements to ViewModel smells badly (impacts testability, increases code coupling and most likely the class will end up violating more good design principles). The fix is really simple: just bind TabControl.SelectedItem to the field on ViewModel.

Related

How to change data context of Interaction Trigger

I have a text box A with an interaction trigger. The Data Context of the text box is a property in the view model. However, the ClearCommand in defined in the view Model. How can I change the Data Context of the Interaction Triggers or its Command to the view model it self.
Thank you
<TextBox Name="TextBoxA"Text="{Binding myObject.TextPrp,UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding ClearCommand}"
CommandParameter="{Binding ElementName=TextBoxB,Path=Text}"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
You have to bind to relative source like this
<TextBox Name="TextBoxA"Text="{Binding myObject.TextPrp,UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type YourUserControl}, Path=DataContext. ClearCommand}"
CommandParameter="{Binding ElementName=TextBoxB,Path=Text}"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>

How to use an EventToCommand with an editable Combobox to bind TextBoxBase.TextChanged with a command?

I have an editable ComboBox.
<ComboBox IsEditable="True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding CritereChangedCommand, Mode=OneWay}"/>
</i:EventTrigger>
<i:EventTrigger EventName="TextBoxBase.TextChanged">
<cmd:EventToCommand Command="{Binding CritereChangedCommand, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
I use GalaSoft.MvvmLight.Command.EventToCommand to bind the SelectionChanged event.
I also would like to bind the TextChanged event, but it is a little bit tricky:
This event is only accessible by the ComboBox TextBoxBase property, and I can't find the proper way to bind this event.
You can see one of my unsuccessful attempt: SelectionChanged binding works fine, but TextChanged binding does not.
I also tried this syntax:
<ComboBox IsEditable="True">
<TextBoxBase>
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<cmd:EventToCommand Command="{Binding CritereChangedCommand, Mode=OneWay}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBoxBase>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding CritereChangedCommand, Mode=OneWay}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
But this won't even compile. I get an error "Type that can be instantiated expected" on the TextBoxBase tag.
Any idea ?
I know, very late response... but I hope it will help someone.
The problem here is that the EventTrigger class in Microsoft.Expression.Interactivity.dll uses reflection to find the event by the value of the EventName property and this won't work for attached events like TextBoxBase.TextChanged.
One option, which I use, is to implement your own custom EventTrigger class as described here and use this one instead of EventTrigger (the link there to CommandAction implementation is broken, but I managed to get it using internet archive).
Another option, which I don't like because it's not classic MVVM Command use, is to bind the Text property of the ComboBox to a property in the ViewModel and invoke the command from the ViewModel code on the setter, like this:
<ComboBox IsEditable="True"
Text="{Binding Text, Mode=TwoWay}" />
And in the ViewModel:
private string text;
public string Text
{
get { return text; }
set
{
text = value;
OnPropertyChanged("Text");
if(CritereChangedCommand.CanExecute())
CritereChangedCommand.Execute();//call your command here
}
}
I have found a way to work around the problem:
I create an invisible TextBox, bound to the ComboBox, and I bind the command on the TextChanged event of the TextBox.
It's not pretty, but it works...
<ComboBox Name="CbRubrique" IsEditable="True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding CritereChangedCommand, Mode=OneWay}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
<TextBox Text="{Binding ElementName=CbRubrique, Path=Text, UpdateSourceTrigger=PropertyChanged}" Visibility="Collapsed">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<cmd:EventToCommand Command="{Binding CritereChangedCommand, Mode=OneWay}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>

Bind an ICommand to a TabItem WPF (MVVM)

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.

(Caliburn Micro) Mapping a ActionMessage Methodname to a Child Object of the ViewModel

I would like to bind the methodname propererty of the caliburn.micro actionmessage to a method on a child object of the ViewModel.
How I would imagine it should work:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="MenuItemX.Clicked" />
</i:EventTrigger>
</i:Interaction.Triggers>
The problem here is that the methodname does not live directly on the viewmodel, but on a childobject of the viewmodel.
So in this case I would like to bind to:
ViewModel.MenuItemX.Clicked()
Current workaround is having a pass-through method on my viewmodel which smells.
You can set the actual target of the action (MenuItemX) using cal:Action.TargetWithoutContext attached property:
<Button cal:Action.TargetWithoutContext="{Binding MenuItemX}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="Clicked" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
or the shorter syntax:
<Button cal:Action.TargetWithoutContext="{Binding MenuItemX}"
cal:Message.Attach="Clicked" />

How do you use mvvm-light to trigger gotfocus

Using silverlight, we are looking to move the focus to a textbox.
How do you use mvvm-light to trigger gotfocus?
The view contains:
<TextBox Margin="4,4,0,0" Text="{Binding Path=SearchOID, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<cmd:EventToCommand Command="{Binding GotFocusCommand, Mode=TwoWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
How should the ViewModel look?
How do you trigger this from the ViewModel?
Ended up using a TriggerAction, very similar to:
http://www.codeproject.com/Articles/42988/Silverlight-Behaviors-and-Triggers-Making-a-Trigge.aspx

Resources