I've created a class that inherits from Combobox, the class is editable. When a user have searched for a given predicate, I want them to be able to iterate the list with pressing the down key. This is somewhat implemented as it works sporadically. Is it any way to force focus to the popup element? I would like to do this by code, and not in XAML.
I've tried
var popup = Template.FindName("PART_Popup", this) as Popup;
popup.Focus();
Which return false as expected as this didn't work.
I've also tried
var s1 = Keyboard.FocusedElement;
if (s1 is FrameworkElement)
{
((FrameworkElement)s1).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
But this focus on the next element in the TAB stack.
This focus approach didn't work either,
Keyboard.Focus(popup);
Edit:
I solved (achieved the desired effect) by setting the animation to .None, and then close followed by open the popup again.
Related
In my WPF program I have a standard ComboBox (Editable). My desired behavior is that upon FormLoad, I would like the user to be able to type into the ComboBox and select from the list. (At FormLoad I've already populated it with some strings.) So I set comboBox.Focus(), and since I have IsTextSearchEnabled="True", which is the default behavior, this works fine.
My problem is, when comboBox.Focus() is set, it does indeed focuses the control, but I do not get the blinking cursor inside it. This is what it looks like:
This in theory gets the job done, but I believe it's not very user-friendly. I would like the ComboBox to be focused, and also have the blinking cursor.
I get the blinking cursor when I click on the editable ComboBox, so I looked up what event gets fired when I do that. Turns out it's PreviewMouseLeftButtonDown, so I tried programmatically firing this event (although this is something I usually try to avoid) to see if that will be an option. This is my code:
comboBox.RaiseEvent
(
new MouseButtonEventArgs
(
Mouse.PrimaryDevice,
Environment.TickCount,
MouseButton.Left
)
{
RoutedEvent = PreviewMouseLeftButtonDownEvent
}
);
I used a Console.WriteLine() to print a simple message to verify in Output Window if the event gets fired, and indeed it does. However, I still do NOT get the blinking cursor in my ComboBox.
Then I looked through SO, and found this question and the OP's edited fix seems to work. However this seems like quite a bit round-about way to get a seemingly simple task done, so I'm wondering if there's a rather straightforward or simpler way that I can achieve the desired result.
Calling the Focus() method once the window has loaded seems to work just fine for me:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
this.Loaded += (s,e) => comboBox.Focus();
}
}
If it doesn't work for you, then please provide a reproducible sample of your issue: https://stackoverflow.com/help/mcve
I am working on an application, wherein we have a main window which is having so many child windows in different dock option. So, one dock is having a property panel window which allows a user to modify property of selected entity and after changing the value user has to click on apply button which is available in the bottom of the control. So, I was willing to have some sort of functionality that if user has modified some value and instead of clicking on apply, if user click somewhere else apart from property panel view's sub control's, then user should be given a message that "Please first click apply to save your changes". for this what I did, I wrote the following piece of code on the mouse down event of MainWindow.
private void MainWindow_MouseDown(object sender, MouseButtonEventArgs e)
{
var hitObject = this.InputHitTest(e.GetPosition(this)) as DependencyObject;
if (hitObject.FindVisualAncestor<PropertyPanelUserControl>() == null)
{
MessageBox.Show("Please save your changes");
}
}
So, the logic is this, on mouse down of main window, get the hit object and check that if it is a child control of property panel control, then it will have PropertyPanelUserControl as its parent and other control which are not part or child control of PropertyPanelUserControl, then user will be prompted to click on the apply.
The above piece of code was working superb...but I figured out a strange issue, I had a combo box in the property panel which had entries from 1 to 10. So, when user tries to change value to other value, then user will not be given that message as, so far user is clicking on the property panel control and when i check the hit object in the mouse down event after selecting an item in the combo box, then hit object was the chromeButton or combo box. but When I selected the last item 10, then hit object comes as border which has the property panel control.
<Border><View:PropertyPanelControl/></Border> and above check fails, as border doesn't have ancestor as property panel control, rather border is the ancestor of the control. So, user gets a message even while changing only combo box value,
Moreover, I have made sure that I was clicking on the combo box item not outside, So, now question is this why wpf is behaving in this weird way and how to address this issue.
Your first question is strange:
why wpf is behaving in this weird way
You described what happens and it all seems totally normal to me. The user clicks on a ComboBoxItem and your HitTest tells you that you've clicked on a ComboBoxItem... I don't see any problem there.
how to address this issue
Now I imagine that if you had taken that ComboBoxItem and worked your way up the visual tree, then you would have found your PropertyPanelUserControl control. Try something like this instead:
HitTestResult result = VisualTreeHelper.HitTest(this, e.GetPosition(this));
UIElement uIElement = result.VisualHit.GetParentOfType<PropertyPanelUserControl>();
if (uIElement != null)
{
// the user clicked inside the PropertyPanelUserControl control
}
The GetParentOfType method is an Extension method that I created that walks up the visual tree looking for the first element of a particular type... you can easily refactor it into a normal method if you prefer:
public static T GetParentOfType<T>(this DependencyObject element) where T : DependencyObject
{
Type type = typeof(T);
if (element == null) return null;
DependencyObject parent = VisualTreeHelper.GetParent(element);
if (parent == null && ((FrameworkElement)element).Parent is DependencyObject) parent = ((FrameworkElement)element).Parent;
if (parent == null) return null;
else if (parent.GetType() == type || parent.GetType().IsSubclassOf(type)) return parent as T;
return GetParentOfType<T>(parent);
}
I want to set the focus to a control within an application without giving focus to the whole application.
For example: Click a button which takes a while to load a screen, when the screen is loaded set the focus on one of the controls. In the meantime I have gone to a different application to do something and the focus returns to the previous application.
This happens when I use Keyboard focus or Logical focus.
Is there any way to stop this happening?
Here is the code:
private void SetFocusInternal()
{
// Focus the first control we find with the 'PositionCursor' indicator expression
FrameworkElement controlToFocus = GetFirstRequiresFocusControl();
// Give focus back to the control which last had it (if any)
if (controlToFocus == null)
controlToFocus = GetLastFocusedControl();
// Just focus the first thing we can find
if (controlToFocus == null)
controlToFocus = GetFirstFocusableControl();
// Using any of the following goes wrong!!
controlToFocus.Focus();
Keyboard.Focus(controlToFocus);
FocusManager.SetFocusedElement(FocusManager.GetFocusScope(this), controlToFocus);
}
No matter what the scenario may be I'm able to recreate this annoying problem 100% of the time.
Create a .Net project, C# or VB.Net. Add a ToolStrip control to the form. Create a few simple DropDownButton(s) that contain at least 2 menu items. Add any other controls you wish, a list box (populate it so it can receive focus correctly) and a ComboBox control. Either assign shortcut keys or enable TabStop on the ToolStrip so that it can receive focus by Keyboard.
Run the project (Debug/Release, which ever you fancy). Use your Keyboard to give the ToolStrip Focus (by tab or shortcut key). Arrow down into a sub item. Now select the escape key to collapse the Toolstrip sub menu. Tab to the ListBox or ComboBox that contains a few Items.
All looks great right? Now use your arrow keys to navigate in these controls... Surprise! your back on the ToolStrip and the control you thought had focus doesn't!
I've tried multiple things to force focus on the ListBox. One example is I'd add the event handler for OnEnter (ListBox.Enter+=...) and add some code like:
ListBox.Focus();
ListBox.Select();
Nothing was a success... It seems like once the menu expands on a toolstrip you will be forever stuck on this control using your Keyboard...
This is important for me to resolve due to the fact that i work with blind users whom use keyboard navigation only... Is this a bug? I cannot reproduce this in MFC...
Any suggestions?
Update
I was able to find a control that doesn't reproduce this strangeness...
System.Windows.Forms.MainMenu is the only "Tool Bar object" that doesn't behave like the others...
I'd still like some feedback on the above though (Help for others and myself)...
Update 2
The underlying issue is within [ToolStripObject].TabFocus property... if set to false all seems to work ok... giving focus back to the control that "looks" like it's focused. But having that capability to allow a blind user to navigate throughout all UI controls via tab is a handy thing to implement... it's too bad this property doesn't work like it should....
I got it to work by overriding the ToolStripMenuItem:
public class ToolStripMenuItemEx : ToolStripMenuItem {
protected override bool ProcessCmdKey(ref Message m, Keys keyData) {
if (keyData == Keys.Escape) {
ToolStripDropDownButton tb = this.OwnerItem as ToolStripDropDownButton;
if (tb != null) {
tb.HideDropDown();
return false;
}
}
return base.ProcessCmdKey(ref m, keyData);
}
}
I have a frustrating problem. Here's a simplified version of what I'm doing:
A UserControl in c# contains a toolbar and an embedded WebBrowser object. The toolbar contains an "Edit" button, which when clicked sets the webbrowser control in design mode. Another button, "Cancel", turns off design mode.
Pseudocode (very simplified):
public void SetDesignMode(bool dm) {
IHTMLDocument2 doc = webBrowser.Document as IHTMLDocument2;
if (dm) doc.designMode = "On";
else doc.designMode = "Off";
_designMode = dm;
ReloadDocument(); // setting designmode clears the document element, so it must be reloaded
}
public void OnLoadCompleted() {
IHTMLDocument2 doc = webBrowser.Document as IHTMLDocument2;
if (!_documentLoaded) {
if (_designMode) doc.designMode = "On";
else doc.designMode = "Off";
ReloadDocument();
_documentLoaded = true;
}
}
public void ReloadDocument() {
_documentLoaded = false;
// code that navigates to the document
}
The problem:
If I click on the displayed web page, and then on the "Edit" button, the WebBrowser control will not become editable. The mouse pointer when hoovering over pictures/links show the web browser navigation mouse pointers, not the editing ones. If I click in the text, the caret won't display.
Debugging reveals that the designMode property on the document is actually set to "On" in this situation, but the control is behaving as if it is set to "Off".
If I don't click in the web page before clicking the "Edit" button, everything works as expected.
Elaboration:
If I click the "Cancel" button when the control is in design mode, I get the corresponding (mis)behaviour, if the document have been clicked in.
Simply clicking on "Edit", then "Cancel", then "Edit" etc. without ever clicking in the document works fine (the mouseover test shows the proper mouse pointers, and I get link navigation or editing depending on the design mode if I click a link in the displayed document).
I've tried various techniques to make sure that another control gets focus before I change the designMode property, but it doesn't make any difference. I've searched MSDN and half of the known internet and haven't found any mention of this kind of problem. Flipping the designMode property like this seems to be quite unusal.
One more tidbit of information: I'm setting up document events by advising the document with a sink implemented by the usercontrol. I doubt that this should have any bearing on the problem, but I've included it here for the sake of being complete. Update: Disabling this doesn't change anything regarding the problem.
Does anybody recognize this problem?
Update:
I've worked around the problem by re-creating the web browser control in SetDesignMode(). It's an ugly solution, but it works and does actually look ok. I'm very interested in any feedback on this problem, though. I believe it is a bug in MSHTML.
I'm not quite sure if we had exactly the same problem, but I suppose my solution should work for you as well.
The basic issue seems to be that x64 reset the designMode attribute, as noted in this article. In my case, I set it to "On" after instantiating the webbrowser, but in the DocumentCompleted event, it was "Inherit" again. Setting it back to "On" in DocumentCompleted makes it editable, but clears the document. Setting the DocumentText again restarts the whole doom loop.
So one solution I found was to refrain from setting the DocumentText, instead I created an empty document, then set the body's (which at this point is no longer null) InnerHtml property:
doc.designMode = "On"; // enable editing
// designMode change resets the document, create it anew
webBrowser1.Document.Write("<html><body></body></html>")
webBrowser1.Document.Body.InnerHtml = "myDocumentText"
Obviously, this works only if you have the text ready, and not if you're navigating to an URL. However, there is another solution which worked for me, which seems easier and safer. I found it in this answer by LaughingJohn. I guess the first line depends on your application, you had the IHTMLDocument directly in webBrowser1.Document.
doc = webBrowser1.Document.DomDocument as IHTMLDocument2;
if (doc != null && doc.body != null)
((HtmlBody)doc.body).contentEditable = "true";
It sounds to me like the WebBrowser gets the focus when you click on it and somehow holds on to it. Try this: click on the WebBrowser, then press the Tab key on the keyboard (which should move the focus off the WebBrowser) and then see if you can click on your buttons.
If you can, then try attaching a handler to the Button.MouseEnter event and call ((Button)sender).Foucs() in it to focus the button programmatically.