WPF: How to close <Popup> when outside scrollviewer mousewheel action - c#

I want a solution about close popup control when outside scrollviewer mousewheel changed.
<ScrollViewer>
....
<Grid>
<TextBox x:Name="PART_Text"/>
<Popup IsOpen="{Binding IsDropDown}" StayOpen="False"
PlacementTarget{BInding ElementName=PART_Text">
<Border>...</Border>
</Popup>
</Grid>
</ScrollViewer>
I want to make the pop-up window close Automatically, when a wheel moves, not a mouse click

Tip for the future: you'll have better luck getting questions answered if you make it easy for people. Your code doesn't compile on account of 1) your StaysOpen property is misspelled, 2) your PlacementTarget setting doesn't have an assignment operator and opening quotation, 3) your Binding keyword is mis-capitalized and 4) the setter doesn't have a closing bracket.
To answer your question, all you need to do is add a command handler for the PreviewMouseWheel event. Exactly where you intercept the event depends on what behavior you want; if you want it to occur when any control in your application has focus then add it to the MainWindow, otherwise add it to your ScrollViewer:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<ScrollViewer>
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseWheel">
<i:InvokeCommandAction Command="{Binding PreviewMouseWheelCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Then add a command handler for it back in your view model:
private ICommand _PreviewMouseWheelCommand;
public ICommand PreviewMouseWheelCommand => this._PreviewMouseWheelCommand ?? (this._PreviewMouseWheelCommand = new RelayCommand(OnPreviewMouseWheel));
private void OnPreviewMouseWheel()
{
this.IsDropDown = false;
}
So long as your IsDropDown property supports INPC the popup will disappear whenever a PreviewMouseWheel event occurs.

Related

Handling window closing in WPF - MVVM Light

I'm trying to handle the window closing using a solution similar to this, but the handler in my ViewModel is firing on application start, and not when closing.
XAML:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<command:EventToCommand Command="{Binding WindowClosing}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
ViewModel:
public RelayCommand<System.ComponentModel.CancelEventArgs> WindowClosing
{
get
{
return new RelayCommand<System.ComponentModel.CancelEventArgs>((args) => {});
}
}
The binding is obviously functioning, but it's just firing at exactly the wrong time. I thought the EventName="Closing" is what was supposed to bind to the actual closing event, but it doesn't matter what's contained there. It always fires on loading. What exactly is supposed to link this to the actual window closing event?

Mvvm light EventToCommand - Setting the Handled property?

I have a tabbed GUI with each tab containing a Frame. I use the EventToCommand with the SelectionChangedEvent whenever a new TabItem is selected. I do this to update the state of the app. This all works fine - a little bit too fine, the event gets fired too often. Here is my problem:
How can I prevent the event from bubbling up the visual tree by setting the Handled property on the event when using the mvvm light eventToCommand feature?
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<mvvm:EventToCommand
Command="{Binding YourCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
Your command needs to provide parameter from method like RelayCommand . This parameter is event args providing Handled property. More here.

Popup with "StaysOpen=false" steals LeftMouseButtonDown event

In my application, when a user attempts to click a slider which is on the main window, while a popup control is open, the popup control steals the mouse down event.
This results in the slider not responding to the mouse down event correctly.
(it seems to get focus and move to an incorrect location)
I found that the that the "OnPreviewMouseLeftButtonDown" in the slider does not fire when popup's "StaysOpen" property is false (and the popup is open),
and does fire when its true (or when the popup is closed).
I was wondering if someone has found a solution for this issue.
I encountered these type of issues in other controls in my application in various contexts, So I would prefer a more general solution rather than just solving this for the slider.
Sample code:
<Window x:Class="SampleApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Root"
Title="MainWindow" Height="350" Width="525">
<Grid Height="130" Width="300">
<Button Width="40" Height="40" Click="ButtonBase_OnClick" HorizontalAlignment="Left" VerticalAlignment="Top"></Button>
<Popup StaysOpen="False" IsOpen="{Binding ElementName=Root, Path=IsOpen}" Width="100" Height="100"
HorizontalAlignment="Center" VerticalAlignment="Center" Placement="Center">
<Grid Background="Black">
<TextBlock Text="hello"></TextBlock>
</Grid>
</Popup>
<Slider Width="200" IsMoveToPointEnabled="True" VerticalAlignment="Bottom"></Slider>
</Grid>
Thanks ahead,
Yotam
This happens because PreviewMouseDown (and it's derivates) (from the base class UIElement) has a default RoutingStrategy.Direct.
Direct - The routed event does not route through an element tree, but does support other routed event capabilities such as class handling, EventTrigger or EventSetter.
This is the source code of the event taken from ReferenceSource.
public static readonly RoutedEvent PreviewMouseLeftButtonDownEvent =
EventManager.RegisterRoutedEvent(
"PreviewMouseLeftButtonDown",
RoutingStrategy.Direct,
typeof(MouseButtonEventHandler),
_typeofThis);
And here is what happens in the Popup:
private void OnPreviewMouseButton(MouseButtonEventArgs e)
{
// We should only react to mouse buttons if we are in an auto close mode (where we have capture)
if (_cacheValid[(int)CacheBits.CaptureEngaged] && !StaysOpen)
{
Debug.Assert( Mouse.Captured == _popupRoot.Value, "_cacheValid[(int)CacheBits.CaptureEngaged] == true but Mouse.Captured != _popupRoot");
// If we got a mouse press/release and the mouse isn't on the popup (popup root), dismiss.
// When captured to subtree, source will be the captured element for events outside the popup.
if (_popupRoot.Value != null && e.OriginalSource == _popupRoot.Value)
{
// When we have capture we will get all mouse button up/down messages.
// We should close if the press was outside. The MouseButtonEventArgs don't tell whether we get this
// message because we have capture or if it was legit, so we have to do a hit test.
if (_popupRoot.Value.InputHitTest(e.GetPosition(_popupRoot.Value)) == null)
{
// The hit test didn't find any element; that means the click happened outside the popup.
SetCurrentValueInternal(IsOpenProperty, BooleanBoxes.FalseBox);
}
}
}
}
So it was designed to work this way, and you should likely not use OnPreviewMouseDown for whatever you are trying to accomplish here.
In my application, when a user attempts to click a slider which is on the main window, while a popup control is open, the popup control steals the mouse down event
While your description is not completely correct, that is the normal behaviour of any Popup control. The reason that this occurs is because the Popup control has focus and so it is listening out for the Click event even if it occurs outside the bounds of the Popup. Think about this logically now... if it didn't do this, how would it know when to close? You will find the same behaviour from the Popup control used in a ComboBox.
There is a workaround to achieve the behavior you require, Set 'IsHitTestVisible = True' for the Slider control you are use.
PS:
Set IsHitTestVisible = True, only when the Popup is Open - False otherwise.

How can I trigger an event when the left mouse button gets released in WPF?

I need to trigger an event when the left mouse button gets released. I've tried this:
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseClick" >
<i:InvokeCommandAction Command="{Binding OnBarGroupChangeCommand}" CommandParameter="{Binding ElementName=ReportsBarGroup, Path=Key}" />
</i:EventTrigger>
</i:Interaction.Triggers>
and this
<igWPF:OutlookBarGroup.InputBindings>
<MouseBinding MouseAction="LeftClick"
Command="{Binding OnBarGroupChangeCommand}" CommandParameter="{Binding ElementName=ReportsBarGroup, Path=Key}"/>
</igWPF:OutlookBarGroup.InputBindings>
These both work. The problem with both cases is that the event fires when the button gets pressed. I need it to fire only when the button gets released. The MouseBinding does not seem to support this. Is there a way to do this with Interaction? What is the best way to handle this? Thanks.
Try EventTrigger event name "MouseLeftButtonUp".
I'm not too familiar with C# but, as far as I'm aware, MouseBinding doesn't allow the support of mouse-up actions, only mouse-down. Take a look at the answer over here
Why don't your try this:
private void btn_MouseUp(object sender, MouseEventArgs e)
{
/////WHAT YOU WANT THE BUTTON TO DO/////
}
If you need to know more about mouse events enter HERE

How would you know which element of an ItemsControl sends an event in MVVM?

Let's say I currently have an ItemsControl whose DataTemplate is a bunch of buttons. I'm wiring up these buttons' click events, but how am I to know which button was clicked? Should I not use a ItemsControl?
I'm trying to have no code-behind, but being pragmatic may be necessary.
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Margin="10">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding ItemsControlButtonClicked, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you want to know what Item was clicked, then pass {Binding } as the CommandParameter and it will pass the selected object to your Command
If you want to know what Button was clicked, I would do that in the code-behind since ViewModels do not need to know anything about the UI, and that includes buttons.
Also since your control is a Button, you should use the Command property instead of a Click trigger.
<Button Command="{Binding ItemsControlButtonClicked}" />
You can send parameters along with the command and based on these parameters you can find out which button was clicked
In my project I also use the MVVM Light I has an dropdown with collection of items, and a button which user press and action depend on selected item from drop down
you should create a Relay command with parameter look at the example from my code
public RelayCommand<Project> StartTimer { get; private set; }//declare command
StartTimer = new RelayCommand<Project>(OnStartTimer);
private void OnStartTimer(Project project)
{
if (project != null)
{
currentProject = project;
if (!timer.IsTimerStopped)
{
timer.StopTimer();
}
else
{
Caption = "Stop";
timer.StartTimer();
}
}
on the view I bind the drop down with collection of class Project
and for button command parameter I bind the selected item form drop down
look at the code
<ComboBox Name="projectcomboBox" ItemsSource="{Binding Path=Projects}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="FullName"
SelectedValuePath="Name" SelectedIndex="0" >
</ComboBox>
<Button Name="timerButton" Content="{Binding Path=Caption}" Command="{Binding Path=StartTimer}"
CommandParameter="{Binding ElementName=projectcomboBox, Path=SelectedItem}" ></Button>
pay attention to Command and CommandParameter binding
also you can use this approache not only for drop down
Well, you can use the Sender.DataContext which is the actual data.
Create command properties in your view model class (using Josh Smith's RelayCommand pattern is the simplest way to do this) and bind each button's Command to the appropriate one. Not only is this straightforward to do and simple to maintain, it also gives you an easy way of implementing the enable/disable behavior when you need to.

Categories

Resources