Combobox inside ContextMenuStrip problem - winforms

Given the following insane setup (a ComboBox inside a UserControl inside a ToolStripControlHost inside a ContextMenuStrip):
there's something odd going on with clicking on different items in the ComboBox popup. If the item is inside the menu bounds (i.e. Amsterdam, Brussel or Luxemburg) the item is selected. If the item is outside the menu bounds (i.e. Berlijn and further) the menu is closed immediately.
Ignoring any spiffy remarks regarding the sheer crazy, anyone know what's going on and how to stop the menu from closing if a distant combobox item is selected?

The problem is due to a check deep in the ToolStripManager determining the mouse event is not on a child.
Basically you need to reject the ContextMenuStrip.OnClosing event if the ComboBox is displaying. There's inevitably a cleaner solution but I didn't see one.
public bool IsDropDownShowing { get; private set; }
private void InitializeContextMenu()
{
var userControl = new ComboMenuUserControl();
var toolStripHost = new ToolStripControlHost(userControl);
contextMenuStrip1.Items.Add(toolStripHost);
userControl.comboBox1.DropDown += (o, args) => IsDropDownShowing = true;
userControl.comboBox1.DropDownClosed += (o, args) => IsDropDownShowing = false;
contextMenuStrip1.Closing += (o, args) =>
{
if (IsDropDownShowing == true)
args.Cancel = true;
};
}

Related

How to auto-close the Caliburn Dialog window?

I have a ViewModel defined like
public class PlayerViewModel : Screen, IDiscoverableViewModel
I am showing a dialog pop up as
var result = await _dialogManager.ShowDialogAsync(item, new List<DialogResult>() { DialogResult.Cancel });
Here item is the another ViewModel which shows UI from the related View. This pop up is showing some information and needs to be auto closed after few seconds in case user doesn't select Cancel button.
Following is the Timer tick event that is getting fired after 10 seconds.
void timer_Tick(object sender, EventArgs e)
{
this.DialogHost().TryClose(DialogResult.Cancel);
}
But it's not working and throwing exception as this.DialoHost() is getting null always. I tried this solution but it is closing the whole ViewModel instead I want to close only the dialog window.
Could you confirm if your 'pop-up viewmodel' is deriving from Screen ? If so, TryClose should work. Could you please verify it ? Sample code for closing.
public class CreatePersonViewModel:Screen
{
System.Timers.Timer _timer = new Timer(5000);
public CreatePersonViewModel()
{
_timer.Elapsed += (sender, args) =>
{
_timer.Enabled = false;
TryClose(true);
};
_timer.Start();
}
}

Check if a control is loaded in Silverlight

I'm trying to write an extension method that will allow me to set focus on a Control. I've written the method below which works fine, however if the control is already loaded then obviously hooking up the Loaded event isn't going to be any use - I also need a way to check if the control has been loaded so I can simply run the Focus() code without hooking up the event.
Is there any way to simulate an IsLoaded property on a control?
public static void SetFocus(this Control control)
{
// return if the control is not visible
if (control.Visibility == Visibility.Collapsed) { return; }
control.Loaded += (sender, routedeventArgs) =>
{
// focus the Silverlight plugin first
System.Windows.Browser.HtmlPage.Plugin.Focus();
control.IsTabStop = true; // required to allow Focus
control.Focus();
if (control is TextBox)
{
((TextBox)control).SelectAll();
}
};
}
EDIT: As per ColinE's answer below, I implemented it like this:
public static void SetFocus(this Control control)
{
// return if the control is not visible
if (control.Visibility == Visibility.Collapsed) { return; }
if (control.Descendants().Count() > 0)
{
// already loaded, just set focus and return
SetFocusDelegate(control);
return;
}
// not loaded, wait for load before setting focus
control.Loaded += (sender, routedeventArgs) =>
{
SetFocusDelegate(control);
};
}
public static void SetFocusDelegate(Control control)
{
// focus the Silverlight plugin first
System.Windows.Browser.HtmlPage.Plugin.Focus();
control.IsTabStop = true; // required to allow Focus
control.Focus();
if (control is TextBox)
{
((TextBox)control).SelectAll();
}
}
If a control has not been loaded, then the various elements within its template will not have been constructed. Using Linq-to-VisualTree you can confirm this:
Debug.WriteLine(control.Descendants().Count());
control.Loaded += (s, e) =>
{
Debug.WriteLine(foo.Descendants().Count());
};
The first debug output should show '0', the second will be a number >0 which indicates the number of child elements of the control once the template has been applied.
or it is enough to check the parent:
var parent = System.Windows.Media.VisualTreeHelper.GetParent(control);
if the parent is null, then the control is not loaded (because it doesn't have a parent in a visual tree)

How to make CheckBox focus border appear when calling CheckBox.Focus()?

When the user tabs into a CheckBox to give it focus, a dotted border appears around the CheckBox to indicate that it has focus.
When the CheckBox gets focused by code calling myCheckBox.Focus(), no such indicator appears (even though pressing the space bar toggles the state).
How can I make the CheckBox focus border appear when I have programmatically focused the CheckBox?
The border is intentionally only shown if you are navigating by the keyboard (Tab key). The MSDN page on this topic has further details:
Focus visual styles act only when the
focus action was initiated by the
keyboard. Any mouse action or
programmatic focus change disables the
mode for focus visual styles.
If you want to show a border, you could use a Trigger on the IsFocused- Property to do some visual changes (although you can't set the border with this) or if you actually want a border, you would have to create your own ControlTemplate.
There is also a thread here on SO on a somewhat related topic where the suggestion is to simulate a key press, but I would suggest not to use this solution for your problem.
By editing the KeyboardNavigationEx file from ControlzEx I managed to solve the issue (full credit goes, as always, to punker76).
Just call the KeyboardHelper.Focus method passing the UIElement that shoud be focused (e.g. KeyboardHelper.Focus(myCheckBox))
Here's the KeyboardHelper class:
public sealed class KeyboardHelper
{
private static KeyboardHelper _Instance;
private readonly PropertyInfo _AlwaysShowFocusVisual;
private readonly MethodInfo _ShowFocusVisual;
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static KeyboardHelper()
{
}
private KeyboardHelper()
{
var type = typeof(KeyboardNavigation);
_AlwaysShowFocusVisual = type.GetProperty("AlwaysShowFocusVisual", BindingFlags.NonPublic | BindingFlags.Static);
_ShowFocusVisual = type.GetMethod("ShowFocusVisual", BindingFlags.NonPublic | BindingFlags.Static);
}
internal static KeyboardHelper Instance => _Instance ?? (_Instance = new KeyboardHelper());
internal void ShowFocusVisualInternal()
{
_ShowFocusVisual.Invoke(null, null);
}
internal bool AlwaysShowFocusVisualInternal
{
get { return (bool)_AlwaysShowFocusVisual.GetValue(null, null); }
set { _AlwaysShowFocusVisual.SetValue(null, value, null); }
}
public static void Focus(UIElement element)
{
element?.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
var keybHack = KeyboardHelper.Instance;
var oldValue = keybHack.AlwaysShowFocusVisualInternal;
keybHack.AlwaysShowFocusVisualInternal = true;
try
{
Keyboard.Focus(element);
keybHack.ShowFocusVisualInternal();
}
finally
{
keybHack.AlwaysShowFocusVisualInternal = oldValue;
}
}));
}
}
'initially set chkCheckBox.Appearance = 1
'on Got Focus set appearance = 0 - Flat
Private Sub chkCheckBox_GotFocus()
chkCheckBox.Appearance = 0
End Sub
'on Lost Focus set appearance = 1 - 3D
Private Sub chkCheckBox_LostFocus()
chkCheckBox.Appearance = 1
End Sub

How to handle dialogs with multiple buttons in WPF?

I have a Window with seven buttons; I use it as a menu in a simple game I am working on, but I display it as a dialog. How can I know which button user has pressed, since DialogResult in WPF only offers true, false and null?
If you're making a custom Window in this way, you don't really need to worry about DialogResult.
You can keep track of this in a property within your Window, and just read the property after the dialog is closed.
MyDialog window = new MyDialog();
if (window.ShowDialog() == false)
{
// user closed the window...
}
var choice = window.CustomPropertyContainingChoice;
Define your own enum and offer a static method to display the window that return your enum.
The code below does the same thing it is part of a window that allows users to review their changes and accept or cancel. As I only need true and false I used a bool however it would be trivial to change to an enum.
public static bool DisplayChanges(List<INormalizedMessage> LstMessages)
{
var retlist = LstMessages.Where(( INormalizedMessage NM ) => { return NM.Status != NormalizedMessageStatus.NoChange; });
ReviewChanges RC = new ReviewChanges();
RC.Messages = retlist.ToList();
RC.ShowDialog();
return RC.Result;
}
private void cmdCancle_Click( object sender, RoutedEventArgs e )
{
Result = false;
Hide();
}
private void cmdOK_Click( object sender, RoutedEventArgs e )
{
Result = true;
Hide();
}

Button Click Event Getting Lost

I have a Menu and Submenu structure in Silverlight, and I want the submenu to disappear when the parent menu item loses focus - standard Menu behavior. I've noticed that the submenu's click events are lost when a submenu item is clicked, because the parent menu item loses focus and the submenu disappears.
It's easier to explain with code:
ParentMenuBtn.Click += delegate
{
SubMenu.Visibility = (SubMenu.Visibility == Visibility.Visible) ? SubMenu.Collapsed : SubMenu.Visible;
};
ParentMenuBtn.LostFocus += delegate
{
SubMenu.Visibility = Visibility.Collapsed;
};
SubMenuBtn.Click += delegate
{
throw new Exception("This will never be thrown.");
};
In my example, when SubMenuBtn is clicked, the first event that triggers is ParentMenuBtn.LostFocus(), which hides the container of SubMenuBtn. Once the container's visibility collapses, the Click event is never triggered.
I'd rather avoid having to hide the sub-menu each time, but I'm a little surprised that the Click event is never triggered as a result...
I can't put any checks inside the LostFocus() event to see if my SubMenuBtn has focus, because it does not gain focus until after the LostFocus() event is called. In other words, SubMenuBtn.IsFocused = false when LostFocus() is triggered.
Anyone have any thoughts about this?
I've found out the solution - albeit, it's not as simple, or elegant as I would have liked. The solution is to use a secondary thread that pauses only for a moment before executing.
ie.
public partial class Navigation : UserControl
{
public Navigation()
{
ParentMenuBtn.Click += delegate
{
SubMenu.Visibility = (SubMenu.Visibility == Visibility.Visible) ? Visibility.Collapsed : Visibility.Visible;
};
ParentMenuBtn.LostFocus += delegate(object sender, RoutedEventArgs e)
{
HideSubMenu(SubMenu);
};
SubMenuBtn.Click += delegate
{
//Sub Menu Button actions...
};
private void HideSubMenu(UIElement subMenu)
{
//Get the Main Page
App app = (App)Application.Current;
MainPage mainPage = (MainPage)app.RootVisual;
Thread thread = new Thread(Navigation.HideSubMenu);
thread.Start(new ThreadState(mainPage, subMenu));
}
private static void HideSubMenu(object threadStateObj)
{
ThreadState threadState = (ThreadState)threadStateObj;
//Execute after 5 milliseconds...
System.Threading.Thread.Sleep(5);
threadState.MainPage.Dispatcher.BeginInvoke(delegate() {
threadState.TargetElement.Visibility = Visibility.Collapsed;
});
}
I just use a simple object called ThreadState to handle all the state objects I want to preserve:
public class ThreadState
{
public MainPage MainPage = null;
public UIElement TargetElement = null;
public ThreadState(MainPage mainPage, UIElement targetElement)
{
this.MainPage = mainPage;
this.TargetElement = targetElement;
}
}

Resources