I'm just starting to use the composite application libraries for WPF. In my shell I have a region in a tabcontrol that is used to display different types of views. I also have a toolbar with buttons hooked up to commands, for example save. The commands are bound in my views, and the views have the canExecute and execute methods.
The idea is that when i click a tab, my tool bar buttons should be enabled or disabled according to the methods in the view. Problem is when I switch tabs the view is not getting the focus and the canExecute for that view doesn't get called. The toolbar buttons remain connected to the commands in the previously selected view, and reminds that way until i actually click on the new view
I'm stumped right now on how to force the view to get the focus. I've tried looking at the tab's content when the tabs SelectionChanged and setting the focus there but its not making a difference. Any ideas?
Try listening for the View.Loaded event, then call View.focus() in the handler. Wpf will not accept focus requests before an element is initialized and loaded. Since the SelectionChanged event is raised before the view is loaded, the focus request will just be ignored. The loaded event is called each time the element is shown after being hidden.
See this blog post for more information on focus:
http://www.julmar.com/blog/mark/PermaLink,guid,6e4769e5-a0b3-47b2-a142-6dfefd0c028e.aspx
Related
I'm working on a Winforms project that uses CefSharp as a Gui. For several reasons I would like to implement a custom context menu using the Winforms ContextMenu class; rendering the menu in Html or customizing the ChromiumWebBrowser's context menu (using CefSharp.IContextMenuHandler) are not an option.
The context menu it triggered by Javascript code that calls a method on a .net object I passed to RegisterAsyncJsObject; the default context menu is prevented using Javascript. I'm invoking the method call on the Gui thread, because the call over the "javascript bridge" to the registered object comes from a different thread.
My problem: when manually showing the Winforms context menu over the CefSharp.WinForms.ChromiumWebBrowser the context menu does not get the keyboard focus (e.g. selecting items with the arrow key doesn't work nor can I close the contextmenu using Esc); instead the keyboard focus remains with the ChromiumWebBrowser control. And, if I click on the ChromiumWebBrowser's control area the context menu doesn't close either. I can only close the context menu by selecting an item with the mouse or clicking on another control within the form (in which the ChromiumWebBrowser is contained) or somewhere completely else (e.g. desktop or another application).
If I trigger the context menu from elsewhere in my code - ultimately using the same method that calls myContextMenu.Show() - the context menu gets the keyboard focus as desired. But one problem still remains: it doesn't close when I click within the ChromiumWebBrowser control.
I haven't used IFocusHander, IContextMenuHandler, IKeyboardHandler - should I?
I'm using CEF 3.2454.1344.g2782fb8, Chromium 45.0.2454.101 and .net 4.5.1.
Unfortunately extracting demo code isn't reasonably possible.
Anyone any ideas?
EDIT1:
After reading the comments, I decided to describe the code flow more precisely:
When right clicking Javascript sends a message to the registered .net object, containing the mouse coordinates. The default context menu is prevented by setting preventDefault on the MouseEvent arguments of the ContextMenu event.
The registered .net object receives the messages and calls windowForm.Invoke(Sub() ... ), because the message is not received on the Main/Gui thread, but must be processed there for the context menu to appear correctly.
The contextmenu is created and assigned to the ContextMenuStrip property of the UserControl that contains the actual ChromiumWebBrowser control.
It is displayed using ContextMenuStrip.Show(location) method.
Issues:
The context menu has no keyboard-focus.
All mouse events appear to be "swallowed" by the ChromiumWebBrowser: clicking there does not close the context menu.
Opening the context menu identically except for using a different "trigger" works fine, except for the 2nd issue.
In the end the solution is simple; everything works as implemented and desired, if the following steps are added:
Before showing the context menu disable the UserControl with the ChromiumWebBrowser and set the focus to the owning form; something like this:
Private Sub showContextMenu(position As Point)
Me.ctrlCefBrowser.Enabled = False
Me.Focus()
myContextMenu.Show(position)
End Sub
That takes the focus away from the ChromiumWebBrowser, giving the context menu a chance to respond to the keyboard inputs. And also, by disabling the control, the mouse events are not "swallowed" anymore so clicking on the browser area causes the context menu to go away again.
Then, finally, add an event handler to the context menu to re-enable the browser control again:
Private Sub myContextMenu_Closed(sender As Object, e As ToolStripDropDownClosedEventArgs) Handles myContextMenu.Closed
Me.ctrlCefBrowser.Enabled = True
Me.ctrlCefBrowser.Focus()
End Sub
That did the trick for me, now I have a fully customizable Gdi context menu for my webbrowser control :o)
Note:
A similar problem arises when using other menus as well, e.g. in a main menu or tool bar: clicking on the ChromiumWebBrowser control will not close the menu (because the mouse event is also "swallowed"). The same solution can be applied: when opening a drop down menu deactivate (Enabled = False) the web browser control. And when it closes, reactivate it. For my menus I used a derived class (Inherits ToolStripMenuItem) that adds listeners to the according events. That takes care of the problem in a global and simple way.
EDIT:
The proposed solution above left the problem that the click on the disabled browser control closed the menu as intended, but got lost, i.e. the browser couldn't process it. My current workaround now is:
Do not disable the browser control.
Using the openening events of menu items and context menus, keep track of which menu is currently open.
When the browser receives the focus (obtainable by intercepting WndProc messages) close the opened menu.
Implementing the actual solution caused some headaches in the details, but maybe that helps someone along anyhow...
I have a Region in the top left of the WPF application which I want to be my global button bar where the user chooses which screen they want to view, and the appropriate content will then be displayed in the main region. THere will also be a sub navigation region in the top right with sub-menu options within that screen, eg if the user clicked "Maintenance" from the main menu, the sub menu would show options for New, Update, Delete, Edit etc.
What I want to have is a way to create a custom menu bar by simply specifying a list of Text button names, and ICommand/Parameter pairs for the action to invoke on button click. The natural way to do this would be have a MenuButtonViewModel class with dependency properties Title, Command and CommandParameter. One more would also be needed, IsSelected, so there is some visual way for the user to see which screen you are currently on, so it needs to behave like a toggle button, but where only one button in the group can be selected at a time. I can then have a UserControl where the content of the bar binds to an ObservableCollection and uses data templates to render the button bar.
I have tried a few ways of doing this so far but cannot figure out a way that gives me the visual behaviour I want. These are my requirements
1) Button to change background to a different Brush OnMouseOver
2) When button is selected, a different Brush is displayed as the background until a new button in the group is selected, like a togglebutton IsSelected behaviour
3) Only one button in the group can be selected at a time
The ways I have tried to do this so far are
1) Extending RadioButton with my own class, adding dependency properties for command and commandparameter. Setting all controls to have the same group Id. Data template to override display of radio button to make it look like a visual button with triggers for mouseover and isselected.
This works fine, except for one thing. Once you select a radio button in a group, there is no way to deselect all options in the radio button group. So if you navigate to "maintenance" and then click the sub menu for "Countries" for example, then you are displayed the country maintenance screen. If you then go to a different area of the app and select "Deal Entry" from the main menu, you are taken to the deal entry screen. If you then click "Maintenance", it displays the generic "maintenance" content and brings back the sub menu control for maintenance, where "Country" is selected in the radio button group, but this is undesirable. When you navigate back to Maintenance, it should deselect all sub menu options, display the generic maintenance landing page content and let you select a sub menu option before displaying that screens content. The first time you load Maintenance, nothing is selected, but once you have chosen an option, then there is no way to have nothing selected again when you reload the maintenance screen.
2) I then tried extending a ListBox, styling it with a horizontal stackpanel for the content, each listboxitem is a menubuttonViewModel. This allows me to only select a single option at a time and to clear the selection when you navigate away from the page. It also lets me change the background when you mouse over each listboxitem.
The bit I can't get working with the listbox is to have the background different on the IsSelected trigger. There seems to be some trigger on the default ListBoxItem template that overrides anything you specify in CSS so no matter what trigger I put on the listboxitem or menubuttonviewmodel style, the background simply does not change. I think I need to redefine the data and content template for listboxitem, but only have it apply for this listbox, so it can't be a global template change to all listboxitems - as I want to be able to use listboxes elsewhere in the app without inheriting this behaviour.
Can anyone give their thoughts on these two approaches and how to perhaps solve the issues I'm having to make one of them work in the way I want, particularly if you can advise how I can override the content/data template for the listboxitems in my style so they do not use the default triggers, and I can get the IsSelected trigger working for me?
If I understood correctly, you would want to clear the selection on the RadioButtons from the Sub-Menu each time you navigate back from another Main Menu option.
In order to solve this, you could have every Radio Button "unchecked" by manually setting the RadioButton´s IsChecked property value to false. A possible solution could be the following:
If you want to clear any checked option from the Sub-Menu everytime you navigate to a different section of the Main Menu, you could first notify to the Sub-Menu´s ViewModel by publishing an event using the EventAggregator after selecting and clicking a different MainMenu button, from the SelectionChangedEventHandler method of the Main Menu´s ViewModel.
Therefore, the Sub-Menu EventHandler of the published event would update each RadioButton´s IsChecked property value to false, which it could be accomplished by using a "Two-Way" Binding between each IsChecked property defined on the View, and each boolean "RadioButton1IsChecked" property implemented on the ViewModel.
I hope this helped you.
I'm displaying a Popup in response to a button click (popup.IsOpen = true;). The popup contains a ComboBox, and when I click an item in the combobox, one of the things the SelectionChanged event does is to hide the popup.
The Popup appears over a DataGrid that I also have on my page, and I'm finding that the mouse-click on the combobox is also being picked up by a MouseUp event that I've got on the DataGrid. Any idea what's going on?
The MouseUp Event has a routing strategy of type Bubbling. Events that use this type of strategy get passed up the chain to parent controls. Since the Popup is a child of the DataGrid, the event will "bubble" up to the DataGrid. If you would rather the event not bubble, you can try using PreviewMouseUp, which has a Tunneling routing strategy, and will "tunnel" down the chain to child controls. Here is a decent overview of Routing Strategies.
I've hit the same issue. Oddly, it doesn't happen when the code is run in the debugger - it only happens in the release version. It really seems to be a bug in WPF. Trying to catch the click and set the event to handled doesn't work.
My workaround is to, when the popup opens, to tell the control underneath to ignore the click.
Like the title said,how may i turn the grid(or page) and the components in it into background gray and unable and show a new component in foreground.
It's a common effect in the web page,but i cannot do that with a xaml.
Please answer in detail better with sample code if you do.Thanks in advance.
Here's an example of using a Popup to display a UserControl when a Page is navigated to. The OnNavigatedTo function creates a popup, binds a Click event to the UserControl, and then shows the UserControl over the entire page. If the correct password is entered, ("password" in the example) the control disappears. You'll need to modify it for your own requirements, but it should definitely get you started.
https://skydrive.live.com/redir.aspx?cid=ef08824b672fb5d8&resid=EF08824B672FB5D8!343
You'll want to use the VisualStateManager and create some animation transitions to switch between the old and new components. I won't paste the code here for brevity, but if you check out:
http://visualstudiomagazine.com/articles/2011/07/22/wcmob_visual-states.aspx
You'll see a simple example of switching between two UI components on a single page within a Windows Phone 7 PhoneApplicationPage. In your scenario, you'd use this paradigm to create 2 animation storyboards, one for showing an overlay control and one for settings the 'disabled' property on the main grid.
I'm having a problem when the Silverlight toolkit's ContextMenu is clicked while it is over a UIElement that has registered a Tap event GestureListener. The context menu click propagates to the underlying element and fires its tap event.
For instance, say I have a ListBox and each ListBoxItem within it has registered both a ContextMenu and a Tap GestureListener. Assume that clicking context menu item2 is supposed to take you to Page1.xaml, while tapping on any of ListBox items themselves is supposed to take you to Page2.xaml.
If I open the context menu on item1 in the ListBox, then context menu item2 is on top of ListBox item2. When I click on context menu item2 I get weird behavior where the app navigates to Page1.xaml and then immediately to Page2.xaml because the click event also triggered the Tap gesture for ListBox item2.
I've verified in the debugger that it is always the context menu that receives the click event first. How do I cancel the context menu item click's routed event propagation so it doesn't reach ListBox item2?
Thanks for your help!
You can get around the problem by doing the following:
In the context menu's Opened handler set LayoutRoot.IsHitTestVisible (LayoutRoot is the default name for the root UIElement) to false
In the context menu's Closed handler set LayoutRoot.IsHitTestVisible back to true
You could try adding a rectangle with a transparent background (important) over the effected area/page when showing the context menu.
I had a very similar issue, but I am using the ManipulationCompleted event as a "tap" detector as the object the ContentMenu applies to is a custom control.
LayoutRoot.IsHitTestVisible didn't work for me, perhaps because it does not apply to Manipulation events. However, it set me on the right path. I just implemented my own simple equivalent of it - I created a boolean variable bCancelManipulation in the page's scope.
In the ContentMenu's Opened event set it to True.
In the ContentMenu's Closed event set it to False.
In the ManipulationCompleted function, the first thing I do is check
if(bCancelManipulation==true) { return; }
It's kind of a hack, but it works great and is quite simple to code - it can easily be adapted to a Tap event too.