TabItem - show popup on hover - c#

I have a TabControl with multiple TabItems, with each TabItem containing multiple sections in the content area. When the user hovers over any Tab, I'd like to show a Popup that shows a list of the sections in that Tab and allow the user to click on the section and navigate to it.
For this question, the portion that I need help with is the closing/opening of the Popups as the user hovers over each Tab. The main sticking points are:
Allowing the user to move the cursor into the Popup area from the Tab. Once the Popup has been opened, it refuses to close on TabItem MouseLeave.
If the user moves from Tab to Tab, the Popup from the previous Tab should close and a new one should open.
The user should only need one click to navigate. This means they shouldn't have to "click away" the popup from one tab in order for another popup to show.
My thought is to expose a DependencyProperty that hit tests which Tab the mouse is over, and trigger the Popup off of that.
Edited for clarity: When moving the mouse over a tab, I show a popup. When I move the mouse away from the Tab, I want the Popup to go close. It has to be a Popup (and not a Tooltip), because the user has to be able to click on the content inside the Popup.
I've sub-classed and re-templated the TabControl, but I would like some help with how to close the Popup on the MouseLeave event. I've triggered on the MouseLeave event, but when the Popup opens, the focus shifts to the Popup so the TabItem MouseLeave event isn't fired properly.

You can use the Interaction.Triggers from the Blend SDK to trigger the popup to close whenever the mouse leaves the popup. Here is a short example:
<Popup Height="300" x:Name="MyPopup">
<Label Content="MyLabel"></Label>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeave">
<ei:ChangePropertyAction TargetObject="{Binding ElementName=MyPopup}" PropertyName="IsOpen" Value="False"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Popup>
You'll need the following namespaces:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"

Related

Remove focus from textbox when clicking on parent grid in MVVM

I have a simple window that has a few buttons and a textbox. I want that when I press "Enter" on my keyboard this will trigger a button command but the problem is that when I'm focused on the textbox, I can't press "Enter" because it will just add a linebreak to the textbox.
I tried binding a LeftClick gesture of the grid to a command that uses keyboard.ClearFocus() but I guess that isn't the right function because that didn't work.
Another option which might work but I didn't try yet and I'm not sure if it's even a good practice, is adding x:Name to the textbox, and sending the window as the parameter to the command.
I want that when I click the parent grid of the textbox the focused will be removed from it so I can press "Enter", is there a simple way to do it in MVVM?
You said: "I want that when I press Enter on my keyboard this will trigger a button command"
Have you tried adding InputBindings to the text box?
<TextBox.InputBindings>
<KeyBinding Key="Return" Command="{Binding YourCommand}" CommandParameter="{Binding Path=Text, RelativeSource={RelativeSource AncestorType={x:Type TextBox}}}"/>
</TextBox.InputBindings>

WPF combobox like popup

I am trying to make a custom popup that will be similar to the combobox one.
I used a Toggle button as the header and a Popup for the content.
<ToggleButton IsChecked="{Binding ElementName=Popup, Path=IsOpen, Mode=TwoWay}">
...
</ToggleButton>
<Popup x:Name="Popup" StaysOpen="False">
...
</Popup>
This works great except one case. When the popup is opened and I click on the ToggleButton again, the popup disappears but then reopens right after.
Looks like when I clicked on the toggle button, Popup detected that the mouse click is outside of the popup so it closes itself and sets ToggleButton.IsChecked = false. Then the click sets IsChecked = true so the popup opens again.
Edit: In such case I would like the popup to close just like the behavior of a Combobox.
Is there a way to solve this problem?
Locate the popup on top of the button so the user can't click on the button. You can do it easily with Placement and so on.

Control doesnt respond to mouse actions because it's on bottom of view hierarchy

I have a ContentControl with following DataTemplate
xmlns:oxy="clr-namespace:OxyPlot.Wpf;assembly=OxyPlot.Wpf"
....
<DataTemplate DataType="{x:Type y:DataGraph}">
<Grid>
<!--Because PlotView is under Label it doesnt respond to mouse actions (click, zoom, etc)-->
<oxy:PlotView Model="{Binding PlotViewModel}"/>
<!--Want Label to be displayed on top-->
<Label Content="some text"/>
</Grid>
</DataTemplate>
I want some text to be displayed on top of oxy:PlotView, but if I do so, oxy:PlotView stops to respond to mouse actions (zoom, click etc).
How can I show label hovering another control, but make bottom control remain responding to mouse actions?
Try setting the IsHitTestVisible property of the Label to False. Mouse clicks will then pass straight through it.
If you're ever wondering where the clicks are going then fire up Snoop, attach to your window, open the Events tab, click on the thing in your program, then see what happened to your event back in Snoop.

RoutedEventArgs.Source give me wrong value? [duplicate]

I am new to WPF and am going through the examples of Professional WPF in .net 4.5. In the commands chapter, there is an example where multiple controls can send the same command. I am using a Button, CheckBox and MenuItem to trigger the New command.
The issue I am facing is that if MenuItem is pressed for the first time, the source shows correctly. However, after clicking the Button or CheckBox, then clicking MenuItem shows me the source of the last control Button or CheckBox, whichever was pressed. I couldn't find what was wrong with my code or why is this behavior shown by MenuItem in WPF.
Below is the code.
<Window x:Class="WpfApplication1.CommandSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CommandSample" Height="300" Width="300">
<Window.CommandBindings>
<CommandBinding Command="New" Executed="CommandBinding_Executed" />
</Window.CommandBindings>
<StackPanel>
<Button Command="New" MaxWidth="80" MaxHeight="30" Content="{x:Static ApplicationCommands.New}" />
<Menu MaxHeight="30" VerticalAlignment="Top">
<MenuItem Header="File">
<MenuItem Command="New"></MenuItem>
</MenuItem>
</Menu>
<CheckBox Command="New"></CheckBox>
</StackPanel>
</Window>
namespace WpfApplication1 {
public partial class CommandSample: Window {
public CommandSample() {
InitializeComponent();
}
private void CommandBinding_Executed(object sender,ExecutedRoutedEventArgs e)
{
MessageBox.Show("New Command launched by " + e.Source);
}
}
}
Yes this is correct (or at least that's how it's designed). Routed commands start routing based on the CommandTarget you specify. If one isn't specified typically the object raising the event uses itself as the starting point (so the MenuItem in this case). So the routing starts with the MenuItem in this case as you might expect. Nothing handles it there so the CommandManager goes up the parent chain. When it hits an element that is a FocusScope (like the Menu), it checks the FocusedElement of the "parent" FocusScope (e.g. the FocusScope of the parent of the Menu which in this case is the Window). If there is a FocusedElement (which there will be one once you have focused an element in the window's focus scope such as your button, checkbox, a textbox that you might put in that stackpanel, etc.) then the CommandManager starts routing the event from that element. When it does that it creates a new ExecutedRoutedEventArgs where the OriginalSource is that starting element (so the button, checkbox, textbox) and then continues routing up the tree.
So when you first ran the app, the FocusedElement of the Window (that's the root focus scope in your example) is null so there is no re-routing needed so the CommandManager just kept going up the parent chain past the Menu and that is why MenuItem appeared as the Source & OriginalSource. When you clicked on the Button you gave that keyboard focus and as part of it also became the logically focused element of its focus scope (i.e. the FocusedElement of its containing FocusScope). So when the MenuItem was subsequently clicked and the CommandManager ultimately reached the Menu, it then re-routed over to the Button (or whatever you focused in the window's focusscope) and started routing up from there. I say this is expected because with routed command you want the routing to go through the logically focused element so that for example, the Cut command of a menu item would trigger a cut of the TextBox in the Window that had focus.

Closing a WPF dialog box with X button in the viewmodel

I currently have a dialog box opening in WPF that will close after displaying warning messages if you hit the cancel button, but if the user hits the X button in the top corner, it closes without the messages. I can override the OnClosing event in the code-behind which fires when I hit the X button, but I'd rather do it through the ViewModel if possible.
<telerik:RadButton Content="Cancel" Command="{Binding CloseCommand}" IsCancel="True" />
So, does anyone know how I can attach an event to the X button click within the viewmodel?

Categories