Application Context Menu in WPF with WindowStyle=None - c#

I've got a very strange problem with a custom WPF window. The window is designed completely in WPF with WindowStyle=None and ResizeMode=NoResize. So I have to reimplement all the basic window handling stuff like moving, resizing and - here is the problem - the application context menu.
Currently I'm calling the context menu in the mouse-event of the app icon and application titlebar as follows:
POINT point=new POINT();
GetCursorPos(out point);
IntPtr handle=GetSystemMenu(new WindowInteropHelper(this).Handle, false);
IntPtr hd=new WindowInteropHelper(this).Handle;
int retvalue=TrackPopupMenu(handle, TPM_RETURNCMD, (int)point.X, (int)point.Y, 0, hd, new IntPtr());
PostMessage(hd, WM_SYSCOMMAND, retvalue, 0);
This is a code snipped you can find all over the net, looks like everyone is doing it this way.
However depending on the events I use to show the menu the following happens:
MouseUp:
Right click on titlebar/app icon: Application context menu appears
Doing another right click without moving the mouse: Somehow a right click event is generated inside the window (while the mouse still points on the titlebar).
Funny part: The Click is translated into the window by window.Left and window.Top. If I place the window correctly, I can open the context menu of a ListView inside of the window while the mouse still points to the tilebar.
MouseDown:
Right mouse down on titlebar/app icon: Application context menu appears
Right mouse up without moving the mouse: Same behaviour, a right click is generated inside of the window at Mouse position + window.Left/window.Top.
I've made a screenshot and measured the pixel distance to check if it's really window.Top/Left.
That's what I've done while trying to solve the problem:
Tested dozens of changes to the Code snipped to show the context menu without effect
I completely removed resize/move code from the window class, no change
Played around with EventArgs.Handled, no change
Enqueued the code snipped in the dispatcher instead of calling it directly in the event, no change

Have you already tried to implement the context menu in the xaml of the custom window? If you are working with Templates you can already integrate it on there or use it in the CustomWindow.xaml
<'ControlName'.ContextMenu>
<ContextMenu>
<MenuItem Header="FooRoot">
<MenuItem Header="Foo" Command="{Binding FooCommand}"/>
</MenuItem>
<MenuItem Header="Foo2" Click="OnClick"/>
</ContextMenu>
</'ControlName'.ContextMenu>
You can bind the command (maybe if you are working the the MVVM pattern) or make an event for it.

Related

WPF Show Menu in Keyboard Navigation Mode

Backstory aka My Configuration
Like many, I have an application which has a Menu docked to the top. I wanted this menu to be hidden until I pressed the alt key, where I'd then be able to navigate this menu either using my keyboard or by clicking my way through the MenuItems.
In my ViewModel, I made a boolean property called ShowMenu, an ICommand called ShowMenuCommand which operates as a flip flop for ShowMenu, and proceeded to wire things up with data binding. You can see that here:
ShowMenuCommand = new RelayCommand(
_ => ShowMenu = !ShowMenu);
The Menu is set up as such:
<Menu Name="MainMenu" DockPanel.Dock="Top"
Visibility="{Binding ShowMenu, Converter={StaticResource BoolToVis}}"
LostFocus="MainMenu_OnLostFocus">
I also configured keybindings for left and right alt that fire the ShowMenuCommand.
<Window.InputBindings>
<KeyBinding Key="F1" Command="{Binding AboutCommand}" />
<KeyBinding Key="LeftAlt" Modifiers="Alt" Command="{Binding ShowMenuCommand}" />
<KeyBinding Key="RightAlt" Modifiers="Alt" Command="{Binding ShowMenuCommand}" />
</Window.InputBindings>
This works exactly as I configured it to work: The menu is normally hidden, but when I press alt it appears and allows me to navigate the menu items until I click away or I select an item and it loses focus, the LostFocus handler setting ShowMenu to false.
The Problem
Having done this, I seem to have lost the ability to enter Keyboard Navigation Mode. For those who don't know what I mean, normally when you press alt in a gui app, certain characters become underlined, and you can press those characters on your keyboard to navigate the UI. I don't know the formal name for this, so bonus points to anyone who can provide the actual name.
Sources for getting this far
How to make WPF menu bar visible when ALT-key is pressed
LeftAlt Keybinding in WPF
What Now?
I've been searching high and low, crawling through keyboard classes and UIElement in hopes of finding something to fix this, and I've come up with a couple possible solutions. That said, the reason I have resorted to StackOverflow is because I don't know how to pose my google search in such a way that I find what I'm looking for.
My proposed solutions are as follows:
Modify ShowMenuCommand to toggle keyboard navigation mode in addition to modifying visibility.
Remove my keybindings and wire the menu's visibility to whether keyboard navigation mode is enabled.
I thought I would've had this with UIElement#IsInputMethodEnabled, however this does not appear to be the case. That said, I don't know if it matters what element you select, and I don't remember if I tried targeting the Menu or the Window.
If anyone else has any third idea or might know something I'm missing, please do let me know. Hiding the menu bar until you unhide it with alt should be something terribly trivial to set up, so I'd not be surprised if I'm missing something.
Additional Context
If you would like to see any code surrounding the provided snippets, you can view the project source code on GitHub.
So after some additional research and a some help from #Vlad, the final solution is the following:
Menu visibility is bound to ShowMenu property in View Controller.
Window has KeyUp event handler which watches for the alt key.
private void MainWindow_OnKeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.System && (e.SystemKey == Key.LeftAlt || e.SystemKey == Key.RightAlt))
{
MainWindowViewModel mwvm = (MainWindowViewModel)DataContext;
mwvm.ShowMenu = !mwvm.ShowMenu;
}
}
This works, except the menu will remain visible after an item is selected. I tried creating an item template to take care of it all at once, but it didn't want to fire.
Handle Click event on relevant menu items, skipping those that are just categories.
The only thing this doesn't take care of is when the user clicks away from the menu. I tried attaching a LostFocus event to the Menu, however this event fires when the menu opens, perhaps because the focus is being taken away from the Menu itself and onto the ContextMenu provider or whatever actually handles drawing the open menu.
I'll need to do some more research and some code cleanup, but the above solution works relatively okay.
Edit: I'll leave this answer unselected for a couple days just in case anyone else has any other ideas.
Edit 2: I found a solution for my use. Since I only have one top-level menu item on this menu, I hooked the MenuItem's SubmenuClosed event. When this happens (either because of a user selecting an option or them clicking away), it hides the menu by setting ShowMenu to false.
It's probably not the most elegant solution, but it's functional. I'll look into cleaning it up later.

WPF application in one window

I am new in WPF and want to create WPF application like cookbook. I already done this and app work correctly. But I make it in this way:
First screen show buttons, which open new windows to do something. As a result i have 14 different windows. It is ok, but now i want to make it in other way.
I am trying to make one window, which will be showed at start, and change content. I divided window on two grids. First is static and is placed on bottom. It contains buttons, which represents functionality of the program. Second one will be dynamic. There i want to show content of every window. So i want to change content of this panel instead of creating new windows.
I tried to make *.cs files which will create controls in code-behind, functions and data. But my idea is not succesful and i do not know how to do this.
At all, I want to create app, which will work like this:
- if you click button "Add receip" then app will show controls to add name, ingredients and save it at the end.
- if you clik "Show receip" previous content will be replaced by list of ingredients
and etc.
I hope you will understand me.
You can create a Frame instead of second grid. Frame allows you to show pages, and not in seperate windows, in Frame itself. You can navigate the frame into the page like
mainFrame.Source = new Uri("Page1.xaml",UriKind.Relative);
This changes the frame to your page. You can change the source again, if you wanna change the page again.
Note: You can add tags to your buttons like "showReceip" and you can make just one buttonclick event for your buttons. Code will look like this.
mainFrame.Source = new Uri((sender as Button).Tag.ToString() + ".xaml",UriKind.Relative);
That takes the tag of your clicked button, add the string ".xaml" on it and take it on the source part. So, if your tag is "Page1", Source will look like "Page1.xaml" as my solution.
Appreciate the try, I hope you are looking for WPF user controls instead for separate windows. User controls are similar to windows you can create the UI and functionalities in the user control. I would like to recommend you to design the main window like the following:
<Grid>
<Canvas Name="canFunctionalButtons">
<!--Define the buttons inside this canvas
And allocate proper place for this in the UI
-->
</Canvas>
<Canvas Name="canControlContainer">
<!--This is to display the user control
Which can be changed dynamically according to the Button's click
-->
</Canvas>
</Grid>
Then you have to add click event for those buttons, which will add specific user control to the canControlContainer canvas. An example for adding an user control to this canvas is as follows, Let btnAddSomething be a button and btnAddSomething_Click be its click event then you can do something like:
private void btnAddSomething_Click(object sender, RoutedEventArgs e)
{
canControlContainer.Children.Clear(); // will remove previous contols from this canvas
// UC_AddSomething be the user control that you wanted to add here
canControlContainer.Children.Add(new UC_AddSomething());
}

short keys for menu not working in wpf in prism

My application is developed using C# (WPF and Prism). It is having 4 projects in it and different regions for each project:
menu region contains menu view
Drawing Region contains drawings
Tree region contains tree view
Status Region contains satus view
I done binding of shortcut keys for menu and it works fine but for that I need to select a menu header first, for example if I want to open new dashBoard window on CTRL+N first i need to select New Menu on the menu bar. If you have a look at other applications like Word, Notepad etc. if you press CTR+N it opens new file and you need not require to go at new menu.
Is it due to different regions in prism?
There is nothing wrong with the code. It is simple input binding code is something like this:
<UserControl.InputBindings>
<KeyBinding Key="A" Command="{Binding AddDashBoardCommand}">
</KeyBinding>
</UserControl.InputBindings>
Is it due to when I press short keys m in different region n m expecting other region to respond? What is the solution for this?
Based on my understanding, it may be related to the focus on a particular region. Anyway, a possible solution that would work is by using Windows's InputBindings, instead of defining the ShortKey on a specified Region.
This way, the Input Binding would take action anytime you would have focus on the Window Application, without needing to open the New menu. However, you would need to take into account that the Command Binding would yield on the Shell's ViewModel.
So, if you would need to delegate the action to a particular ViewModel, you could Publish() an Event through EventAggregator, which its type would depend on the action made by the user (NewWindowEvent, CopyEvent, SaveEvent, ...). And therefore, each corresponding ViewModel would then suscribe to the particular Event/s that it would only know how to handle it.
You should be able to use InputBindings with or without Modifiers attribute.
Regards.
Please try to use it as below:
<UserControl.InputBindings>
<KeyBinding Key="A" Modifiers="Control" Command="{Binding AddDashBoardCommand}">
</KeyBinding>
</UserControl.InputBindings>
So, using above (added Modifiers property): shortcut Ctrl+A should work for Add Dashboard command.
BTW, I would recommend changing your Add Dashboard shortcut to Ctrl+D, as Ctrl+A is already used for selecting all text.

c# simulate click in context menu

So my question is:
I got an Windows Forms application in C# and an webBrowser control in this application. When you for example right-click on a video in youtube, a context menu shows up. Now, is it possible to programmatically rightclick in the webBrowser control an then, again programmatically, click a specific entry in that context menu?
Yes it is, but you always have to start from the same pixel, or better said an actual pixel range, so you can be sure that the clicked result will be the required one. Also you cant click an item by specifying its text, you must do everything programatticaly, from the graphics point of view(just work on the X - Y Axis since its only 2 dimensional). That is the way most web bots are made for various purposes.
Do you really have to simulate a click of the context menu or is just having the desired action good enough? If so than you can just get the item from the ContextMenu.Items list and assuming it a button raise its Click event. If you do need to at least show the context menu while do this you can call the ContextMenu.Show event. This all assumes that the contextmenu for your WebBrowser control is public (not some third party inherited control that hides it or something).

WPF and Prism View Overlay

I need some help with overlaying views using the prism framework.Its a little more complexed than that so let me explain.I could be over-thinking this as well :D
i have shell (wpf window) and i have 2 views(A & B - both usercontrols) in a module.
when the shell loads it loads view A. On view A i have a button to "popup" view B
for some user input. so naturally i would think to some sort of modal window/control, maybe even a popup. however the problem i face with the popup is that when i move the shell the popup remains fixed and it doesnt block events in view A. I've tried disabling view A to stop events being fired and i've also tried to use a to get the view B move with the shell. Only the canvas works but i now need a way to block it tho'. Is there anyway i can overlay a view on top of another view with prism? or how does everyone else create modal popups with prism & wpf? any advise or pointers would be greatly appreciated.
If you want to use embedded dialogs without an extra window, you can use Prism's RegionManager to achieve the outlined behavior. The trick is to put the PopUp region parallel to your main region in the visual tree:
<Grid>
<ContentControl cal:RegionManager.RegionName="MainRegion" IsEnabled={Binding IsNoPopUpActive} />
<ContentControl cal:RegionManager.RegionName="PopUpRegion"/>
</Grid>
Now use the RegionManager to put view "A" into the "MainRegion". Create a controller class similar to IPopUpDialogController. It should be responsible for putting your view "B" (or any other PopUpView in your application) into the "PopUpRegion" on demand. Addtionally, it should control a flag that signal the underlying "MainRegion" to be enabled or disabled. This way a user won't be able to play with the controls in your view "A" until the pop up is closed.
This can even be done in a modal fashion by using ComponentDispatcher.PushModal() before pushing a frame onto the Dispatcher. However, I would recommend avoid modal dialogs.
Update: As requested in a comment, the IsNoPopUpActive could be implemented in the backing view model. There you could link it to RegionManager's View collection for the popup region:
public bool IsNoPopUpActive
{
get { return _regionManager.Regions["PopUpRegion"].Views.Count() == 0; }
}
Remember to trigger a PropertyChanged event as soon as you modify the views collection (add/remove a popup).
Just for your information: nowadays I avoid disabling the controls in the background and instead insert a transparent panel. This avoids clicking on background controls. However, this does not handle keyboard input (tab-ing to controls). To fix the keyboard input you need to make sure that the keyboard focus is trapped in the popup (MSDN on WPF Focus concepts).
Adding the following focus attributes to the popup region should do the trick:
KeyboardNavigation.DirectionalNavigation="None"
KeyboardNavigation.ControlTabNavigation="None"
KeyboardNavigation.TabNavigation="Cycle"
KeyboardNavigation.TabIndex="-1"
If you are using WPF + MVVM with Prism you can take a look at this Message View overlay controller. The nice part about this approach is you can write unit tests on you view model using a mock overlay controller and have the mock controller return the result that the user would choose in the overlay.
You can find it here: http://presentationlayer.wordpress.com/2011/05/24/wpf-overlay-message-view-controller/
Hope this helps

Categories