I am using the MVVM pattern with the help of MVVM light. When I click a button I change the visibility property that the textboxs' visibility is bound to. When I lose focus, the trigger should execute and collapse the textbox. The problem is that the textbox never loses focus.
<TextBox x:Name="TextBox"
Text="{Binding TextBoxText, Mode=TwoWay}"
Visibility="{Binding TextBoxVisibility}"
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
Height="30" Width="100"
TextWrapping="Wrap">
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostKeyboardFocus">
<command:EventToCommand Command="{Binding CollapseTextBoxCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
CollapseTextBoxCommandchanges the TextBoxVisibility property to Visibility.Collapsed.
Here is the button
<Button Name="AddButton"
Command="{Binding ShowTextBoxCommand}"
Grid.Column="0" Grid.Row="1"></button>
ShowTextBoxCommand changes the TextBoxVisibility property to Visibility.Visibile.
Here is a recorded screencapture of the problem I'm talking about.
What should happen, is that when I click off the textbox, it should collapse the visibility.
Any help is much appreciated. Thanks.
I suggest looking at this older stackoverflow question TextBox LostFocus does not fire . I tried out a few things on a dummy project, and I couldn't get it to work with LostKeyboardFocus or PreviewLostKeyboardFocus as the other question suggests.
What I did try and I can confirm it worked, was to create an eventtrigger on the WPF window itself, to detect when clicking somewhere else other than the textbox:
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseUp">
<command:EventToCommand Command="{Binding ShowTextBoxCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
Of course you can use MouseDown if you prefer.
I think it does nothing to the MVVM pattern, so I tried to replicate your issue simply using a Button, a CheckBox and a TextBox, where the CheckBox acts as a focusable control to help to remove the focus from the TextBox. Please try the following code snippet.
xaml:
<UniformGrid Columns="1">
<TextBox Name="textBox"
Text="Text is coming"
LostKeyboardFocus="TextBox_LostKeyboardFocus" />
<Button Content="getFocusButton"
Click="AddButton_Click" />
<CheckBox Content="dummyCheck" />
</UniformGrid>
xaml.cs:
private void TextBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
(sender as TextBox).Visibility = Visibility.Collapsed;
}
private void AddButton_Click(object sender, RoutedEventArgs e)
{
textBox.Visibility = Visibility.Visible;
}
Based on your screen capture, the reason I think why your LostKeyBoardFocus event is not fired is because when your TextBox is focused, the black background you clicked is not focusable, so that the focus on TextBox is not lost at all.
Therefore, in my code snippet, I add a CheckBox. When TextBox is focused, its Visibilty will be collapsed once the CheckBox is clicked and focused.
Related
I created a login-site in my program in WPF where you enter your credentials and then press Enter or the button below. The button has a command (for View Model Binding) and a Click-Event (I got an AutoResetEvent in Code Behind that shakes the Textbox if the login wasn't successful within a second).
Now I tried the same with the Textbox: Command and Event. So this is my Textbox at the moment:
<TextBox Margin="0,0,0,10" Text="{Binding Username}" KeyDown="MainWindow_KeyDown">
<TextBox.InputBindings>
<KeyBinding
Key="Enter"
Command="{Binding KeyPressCommand}" />
</TextBox.InputBindings>
</TextBox>
And this is my button:
<Button Command="{Binding ConnectCommand}" Content="Connect" Background="#E9712F" Margin="0,10,0,0" FontFamily="Arial" FontWeight="Bold" Foreground="White" Cursor="Hand" Click="Button_Click"/>
The button works, it triggers the event and sends the command. But the Textbox completely ignores the Event and just sends the Command to the View Model.
Is there a way to trigger both, event and command?
Instead of KeyDown event Use PreviewKeyDown
UPDATED
The KeyDown event is not raised for navigational keys that would normally be handled by WPF, but the PreviewKeyDown event also support those keys.
Maybe helpful: What are WPF Preview Events?
System.Windows.Interactivity allows commands to be invoked when a specific event is triggered, without having to write code behind. However, I couldn't find out how to invoke a command when the middle mouse button (scroll wheel) is clicked on an element.
<StackPanel>
<i:Interaction.Triggers>
<i:EventTrigger EventName="...">
<i:InvokeCommandAction Command="{Binding CloseCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
...
</StackPanel>
Since Ash pointed out that clicking the wheel button was a thing, it prompted me to look into how that would work.
You can just use a mousebinding.
<StackPanel.InputBindings>
<MouseBinding Gesture="WheelClick" Command="{Binding WheelClickCommand}" />
</StackPanel.InputBindings>
As Andy's answer showed, you can use mouse binding. However the WheelClick gesture indicates a scroll action, use MiddleClick instead.
<StackPanel>
<StackPanel.InputBindings>
<MouseBinding Gesture="MiddleClick" Command="{Binding CloseCommand}" />
</StackPanel.InputBindings>
...
</StackPanel>
You could create a custom EventTrigger that handles this for you:
public class MouseWheelButtonEventTrigger : System.Windows.Interactivity.EventTrigger
{
public MouseWheelButtonEventTrigger()
{
EventName = "MouseDown";
}
protected override void OnEvent(EventArgs eventArgs)
{
MouseButtonEventArgs mbea = eventArgs as MouseButtonEventArgs;
if (mbea != null && mbea.ChangedButton == MouseButton.Middle)
base.OnEvent(eventArgs);
}
}
Sample usage:
<StackPanel Background="Yellow" Width="100" Height="100">
<i:Interaction.Triggers>
<local:MouseWheelButtonEventTrigger>
<i:InvokeCommandAction Command="{Binding CloseCommand}" />
</local:MouseWheelButtonEventTrigger>
</i:Interaction.Triggers>
</StackPanel>
The built in one doesn't as there is no specific event raised for the mouse wheel button.
This label is present in a User Control and I am binding this Label's Content property to Window's datacontext. I want to execute a ICommand whenever a Content of this Label changes. Property it is binded to is in Parent's ViewModel. While I have a ICommand in user control's viewmodel.
<Label Style="{StaticResource LabelStyle}"
FontSize="28" HorizontalAlignment="Center"
Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window},
Mode=FindAncestor}, Path=DataContext.CurrentTag}">
</Label>
I found a cleaner approach that fits with MVVM pattern.
Interactivity is coming from below namespace
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Setting NotifyOnTargetUpdated=True, enables TargetUpdated event to trigger where I bound my ICommand
<Label Style="{StaticResource LabelStyle}" FontSize="28" HorizontalAlignment="Center"
Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}
,Mode=FindAncestor}
, Path=DataContext.CurrentTag, NotifyOnTargetUpdated=True}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="TargetUpdated">
<i:InvokeCommandAction Command="{Binding SubmitCommand}"
CommandParameter="{Binding
RelativeSource={RelativeSource AncestorType={x:Type Window}
,Mode=FindAncestor}, Path=DataContext.CurrentTag}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Label>
The Label has no ContentChanged event to handle.
What you could do is to use a DependencyPropertyDescriptor and hook up an event handler using its AddValueChanged method programmatically:
public partial class MyUserControl : UserControl
{
public MyUserControl ()
{
InitializeComponent();
DataContext = new YourViewModelClass();
DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(Label.ContentProperty, typeof(Label));
if (dpd != null)
{
dpd.AddValueChanged(label, OnLabelContentChanged);
}
}
private void OnLabelContentChanged(object sender, EventArgs e)
{
var vm = this.DataContext as YourViewModelClass;
vm.YourCommand.Execute(null);
}
}
Handling changes to dependency properties in the view: https://blog.magnusmontin.net/2014/03/31/handling-changes-to-dependency-properties/
<Label x:Name="label"
FontSize="28" HorizontalAlignment="Center"
Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window},
Mode=FindAncestor}, Path=DataContext.CurrentTag}">
</Label>
As mentioned before, Label has no ContentChanged event.
However, when I needed this property to work I have used SizeChanged Event instead.
What I did was whenever I change the Label's Content, I have changed the width of it with a value that close to original width.
Since Label does not show the edges, It does not matter that how much your Label long, unless you do not have any other tool near it.
I know this is not the exact and formal solution for this problem, but It saved my day.
Label has no ContentChanged event as already said but TextBox does have a TextChanged event.
If you disable the IsHitTestVisible and turn the BorderBrush and Background invisible then it looks almost the same as a Label and is not writeable so it also functions the same.
I'm having some problems getting the correct behavior for my WPF user control.
I use a listbox to show elements for a databound ObservableCollection in a datatemplate defined like this
<DataTemplate DataType="{x:Type ToolNodeVM:NodeViewModel}">
<Thumb>
<Thumb.Template>
<ControlTemplate>
<Border Width="160" Margin="0,0,10,0" ClipToBounds="False">
<TextBlock HorizontalAlignment="Center" Text="{Binding NodeName}" Foreground="White"/>
</Border>
</ControlTemplate>
</Thumb.Template>
<i:Interaction.Triggers>
<i:EventTrigger EventName="DragDelta">
<cmd:EventToCommand PassEventArgsToCommand="True"
Command="{Binding DragDeltaCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Thumb>
</DataTemplate>
If I press on the text block, the thumb receives the mouse event and the DragDeltaCommand databound command is executed, and if there is no element under the mouse (within the Border the Listbox recieves the command and sets the item as the selected one and highlights it with the default blue color.
Eventually I would like to give the item a background, but this would mean the listbox item would never be selected because there would be something in the way... the background of the Border.
How would I proceed in order to let the item always become selected, giving the mouse press event to both the listbox and to the thumb with its drag delta command?
I'm trying to launch an ICommand when the user double-clicks on a listbox item. Also, I'm trying to do this using the MVVM pattern.
In this XAML, the key press "p" works perfectly. When I double click on the list box, the command never starts. I've set a break point to confirm "PlayVideoCommand" is not called with a double-click. Am I missing something or do I have to use Setter (which I'm not familiar with)?
<ListBox Name="SmallVideoPreviews" Grid.Column="1" MaxHeight="965"
ItemsSource="{Binding BrowseVideos}"
ItemTemplate="{StaticResource BrowseTemplate}">
<ListBox.InputBindings>
<KeyBinding Key="p"
Command="{Binding PlayVideoCommand}"
CommandParameter="{Binding ElementName=SmallVideoPreviews, Path=SelectedItem}"/>
<MouseBinding Gesture="LeftDoubleClick"
Command="{Binding PlayVideoCommand}"
CommandParameter="{Binding ElementName=SmallVideoPreviews, Path=SelectedItem}"/>
</ListBox.InputBindings>
</ListBox>
Both double-click and "p" should execute the same command. When using the mouse, I can see the listboxitem is selected. I have a hunch that the MouseBinding Command property is not a dependency property but I don't know how to confirm this.
What's happening in your sample is that the listbox itself is reacting to the double click, but only in the part of it's area that is not covered by a list box item.
You need the event handler to be tied to the listboxitem.
Some ways to do it are here:
Double Click a ListBox item to open a browser
And some discussion about why a little code-behind in MVVM is not necessarily a terrible thing:
Firing a double click event from a WPF ListView item using MVVM
More discussion:
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/9fb566a2-0bd6-48a7-8db3-312cd3e93340/
It seems that the ListBox doesn't handle double click on a ListBoxItem. This is a good answer:
Can't bind Command to ListBox
One could argue weather or not code-behind is terrible, but it ís possible use a command. Add the LeftDoubleClick gesture to the ItemTemplate like this:
<UserControl.Resources>
<DataTemplate x:Key="BrowseTemplate" >
<StackPanel >
<StackPanel.InputBindings>
<MouseBinding Gesture="LeftDoubleClick"
Command="{Binding DataContext.PlayVideoCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Mode=OneWay}"
CommandParameter="{Binding }" />
</StackPanel.InputBindings>
<TextBlock Text="{Binding }" Width="50" />
</StackPanel>
</DataTemplate>
</UserControl.Resources>