WinForms dialogs with TopMost = true - winforms

I have a dialog implemented in WinForms that is shown as a notify dialog on the bottom right of the screen. The problem is that whenever is shown it takes the focus and this happens only when TopMost = true. How can I solve this?

You need to inherit from Form and override a couple of properties:
[Flags]
enum WS_EX
{
TOPMOST = 0x00000008,
}
class TopMostForm : Form
{
protected override CreateParams CreateParams
{
get
{
var baseParams = base.CreateParams;
baseParams.ExStyle |= (int)WS_EX.TOPMOST;
return baseParams;
}
}
protected override bool ShowWithoutActivation
{
get { return true; }
}
}
Then just simply Show() on this form and it will be displayed as topmost and inactive.

Show the dialog with Show instead of ShowDialog.
ShowDialog will be topmost, user has to click it before doing something else (modal)
Show will show it as normal.

How about this strategy:
Show it at startup, then immediately:
Hide it with ShowWindow( SW_HIDE )
Never ever close the form, just let it be invisible
Show it with ShowWindow( SW_SHOWNOACTIVATE )

Related

MVVM show confirmation messagebox before command attached to a button is executed

I have a button attached to a command in the view model. This button deletes the rows current selected in the listview so I would like to show a messagebox of confirmation before proceeding. Is user click ok button (in messagebox) then command is executed, otherwise, if user click cancel button command attached is not called. Is it possible? If so how?
<Button Name="btnDelete" Command="{Binding DeleteRowsCommand}"/>
Another possibility is to call the command on click and in the view model through a property that is attached to a custom message box placed in the view, to make this custom messagebox visible when value of the property is true. But then how can I send back to the view model which button 'Ok' or 'Cancel' has been pressed?
The view model doesn't often need to know that there is a question for the user before the command is executed. If that's the case, you can create very simple custom button class to just show the message box and if the user click yes, execute the command (or do whatever).
public class YesNoButton : Button
{
public string Question { get; set; }
protected override void OnClick()
{
if (string.IsNullOrWhiteSpace(Question))
{
base.OnClick();
return;
}
var messageBoxResult = MessageBox.Show(Question, "Confirmation", MessageBoxButton.YesNo);
if (messageBoxResult == MessageBoxResult.Yes)
base.OnClick();
}
}
In XAML you can use the button like this:
<components:YesNoButton Content="Delete rows" Command="{Binding DeleteRowsCommand}" Question="Do you really want to delete rows?" />
EDIT: Another way to solve this is to define some MessageBoxService in the ViewModel layer and implement it in View layer. Interface could look like this:
public interface IMessageBoxService
{
void ShowError(string messageBoxText);
void ShowWarning(string messageBoxText);
void ShowInformation(string messageBoxText);
}
This way you can show message boxes directly from the VM without directly referencing WPF libraries.
Just use a MessageBox ;)
In the method which is routed to the DeleteRowsCommand use this
var result = MessageBox.Show("message", "caption", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
//your logic
}
Have a look at MessageBox Class for more information.
One of the possible (and in my opinion the cleanest) ways of doing this is to implement a service like DialogService, inject it into your ViewModel and call it when command is executed. By doing so you decouple your view and application logic so the ViewModel is completely unaware of how actually dialogs are shown and delegates all the work to the service. Here's an example.
First you create a dialog service which handles all the work of showing dialogs and returning their outcome:
public interface IDialogService
{
bool ConfirmDialog(string message);
}
public bool ConfirmDialog(string message)
{
MessageBoxResult result = MessageBox.Show(message, "Confirm", MessageBoxButton.YesNo, MessageBoxImage.Question);
return result == MessageBoxResult.Yes ? true : false;
}
Then you make your ViewModel dependent on that service and inject it in the ViewModel:
public class MyViewModel : ViewModelBase
{
private readonly IDialogService _dialogService;
public MyViewModel(IDialogService dialogService)
{
_dialogService = dialogService;
}
}
Finally in your command you call the service in your command to check whether the user is absolutely sure he wants to delete records or not:
public Command DeleteRecordsCommand
{
get
{
if (_deleteRecordsCommand == null)
{
_deleteRecordsCommand = new Command(
() =>
{
if (_dialogService.ConfirmDialog("Delete records?"))
{
// delete records
}
}
);
}
return _deleteRecordsCommand;
}
}

WPF ComboBox dropdown is preventing clicking other controls

I have a simple form like this:
I open the combobox and at the time dropdown is open, I click the button. On button click I show a simple message but the message is not shown at that time.
It shows when I click it again.
The same problem for textbox. When the dropdown is open, the textbox click is not working.
Why does combobox prevent clicking other controls when it is open?
You can create an event for ComboBox DropDownClosed and with the hittestfunction, find the other control that the user has clicked.
private void ComboBox_DropDownClosed(object sender, EventArgs e)
{
Point m = Mouse.GetPosition(this);
VisualTreeHelper.HitTest(this, this.FilterCallback, this.ResultCallback, new PointHitTestParameters(m));
}
Then in the FilterCallback function after finding that control, raise the mouse down event on that control.
private HitTestFilterBehavior FilterCallback(DependencyObject o)
{
var c = o as Control;
if ((c != null) && !(o is MainWindow))
{
if (c.Focusable)
{
if (c is ComboBox)
{
(c as ComboBox).IsDropDownOpen = true;
}
else
{
var mouseDevice = Mouse.PrimaryDevice;
var mouseButtonEventArgs = new MouseButtonEventArgs(mouseDevice, 0, MouseButton.Left)
{
RoutedEvent = Mouse.MouseDownEvent,
Source = c
};
c.RaiseEvent(mouseButtonEventArgs);
}
return HitTestFilterBehavior.Stop;
}
}
return HitTestFilterBehavior.Continue;
}
private HitTestResultBehavior ResultCallback(HitTestResult r)
{
return HitTestResultBehavior.Continue;
}
The combobox is implemented the way that it captures the mouse when the dropdown is open. This is done to easyly figure out when the user clicks outside of the combobox (in fact it's a one-liner). When the user clicks outside of the combobox it releases the mouse, closes the dropdown and marks the click as handled. The last action of course stops further processing and the click is not passed to the control you thought you clicked on.
My personal opinion is this behavior has pros and cons. Microsoft decided the way it is.

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

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;
}
}

Custom Winforms Controls (a button specifically)

I'm trying to create a custom button where the foreColor is always crimson and the backColor is always cyan. Ugly color scheme, but I'm just trying to get it so I can create large amounts of controls with a consistent color scheme without setting each control individually. Here's how I coded the button:
public partial class CustomButton : Button
{
private static Color _defaultForeColor = Color.Crimson;
private static Color _defaultBackColor = Color.Cyan;
public CustomButton()
{
InitializeComponent();
base.ForeColor = _defaultForeColor;
base.BackColor = _defaultBackColor;
}
public AutoScaleMode AutoScaleMode { get; set; }
[DefaultValue(typeof(Color), "Crimson")]
override public Color ForeColor
{
get { return base.ForeColor; }
set
{
base.ForeColor = _defaultForeColor;
}
}
[DefaultValue(typeof(Color), "Cyan")]
public override Color BackColor
{
get { return base.BackColor; }
set
{
base.BackColor = _defaultBackColor;
}
}
}
When I drop the custom button onto my form, the background is the regular button color and the text is crimson. If I run the app it's the same also. Now if I try to modify the forecolor or backcolor in the properties window they go right back to their defaults that I set (crimson, cyan) and then they also show up that way when I run my app. How do I get the controls to show up correctly in the designer and at run time?
The problem exists because UseVisualStyleBackColor is automatically set to true and you can't override it. If you change it to false, you'll notice that your button will work correctly.
One option is to override OnControlAdded of the button like this:
protected override void OnControlAdded(ControlEventArgs e)
{
base.OnControlAdded(e);
UseVisualStyleBackColor = false;
}
First time in the designer, the color won't show, but when you run the application it will work correctly.
I suggest you bind fore and back colors to, for example, application settings (Or settings class specific to your controls). Standard button and bind in designer, or use your own descendant and bind in code. In this case you will have consistent color scheme, and, more important, you can change it without recompilation.
public class CustomButton : Button
{
public CustomButton
{
InitializeComponent();
if (!DesignMode)
{
DataBindings.Add(new Binding("ForeColor", Settings.Default, "ButtonForeColor", true, DataSourceUpdateMode.Never));
DataBindings.Add(new Binding("BackColor", Settings.Default, "ButtonBackColor", true, DataSourceUpdateMode.Never));
}
}
// ...

Resources