Showing a ChildForm on top, even when ParentForm is hidden - winforms

I have an application that’s running silently in the System Tray. Occasionally, I need it to pop-up a small notification form to the end-user.
I’ve attempted to accomplish this w/ a WinForms application that has the bulk of its logic built into a hidden form that’s not displayed to the user. Then, when certain criteria is met, I display a secondary form to the user.
My problem is, this secondary form isn’t always on top, even when I set TopMost = true.
I believe this is because the main form isn’t being displayed, so its child forms can’t take advantage of TopMost = true. I've tried moving TopMost around to a few different places. Any other ideas?
MainForm logic:
ChildForm childForm = new ChildForm(this);
int x = (Screen.PrimaryScreen.Bounds.Width / 2) - (childForm.Width / 2);
childForm.StartPosition = FormStartPosition.Manual;
childForm.Location = new Point(x, 0);
childForm.ShowDialog();
//childForm.TopMost = true;
ChildForm logic:
public ChildForm(MainForm mainForm)
{
InitializeComponent();
//this.TopMost = true;
}

After stepping into some breakpoints, I realized that the childForm will be TopMost as long as I set that property after the childForm has been initialized properly and shown. I was able to force this to work by setting the TopMost command within the Shown event like this:
private void ChildForm_Shown(object sender, System.EventArgs e)
{
this.TopMost = true;
}

Related

WinForm Touch Events Cefsharp

I have an issue with Winforms and touch inputs via a Microsoft Surface tablet. I know that winforms does not support touch input, but maybe there is something there can be done about this. So in my Winforms application i have a Form with two controls. A simple List Box and CefSharp ChromiumWebBrowser Control.
When I open the application the HTML is loaded and everything works fine with normal mouse input. I can click the textboxes and I can type. But when i do the same thing with touch controls on a MS Surface it seems the the ChromiumWebBrowser Control dows not get focus. The HTML Textboxes inside the Browser have focus, but the ChromiumWebBrowser Control does not. So whem i type, no input is send to the textboxes.
I tried to set focus manually whit C# in winforms, but those click events do not get fired in touch mode except for the Enter event. But this is fired only once. So i dont know how to get around this.
The user needs to be able to click outside the Browser Controle and do something there and then get back to the Browser and type into those textboxes.
Maybe someone had a similar issue and knows a workaround. If other Info is needed, please tell me.
Here is a gif to illustrate, hope it helps: https://i.stack.imgur.com/8J0EZ.gif
Thanks
private void Form1_Load(object sender, EventArgs e)
{
var asForm = System.Windows.Automation.AutomationElement.FromHandle(this.Handle);
wbMap = initBrowser();
this.panel1.Enabled = true;
this.panel1.Controls.Add(wbMap);
wbMap.FrameLoadEnd += WbMap_FrameLoadEnd;
wbMap.Click += WbMap_Click;
wbMap.Enter += WbMap_Enter;
wbMap.MouseClick += WbMap_MouseClick;
wbMap.DoubleClick += WbMap_DoubleClick;
wbMap.Leave += WbMap_Leave;
wbMap.LostFocus += WbMap_LostFocus;
wbMap.GotFocus += WbMap_GotFocus;
wbMap.PreviewKeyDown += WbMap_PreviewKeyDown;
}
private ChromiumWebBrowser initBrowser()
{
ChromiumWebBrowser wbMap = new ChromiumWebBrowser();
BrowserSettings browserSettings = new BrowserSettings();
browserSettings.FileAccessFromFileUrls = CefState.Enabled;
browserSettings.UniversalAccessFromFileUrls = CefState.Enabled;
wbMap.BrowserSettings = browserSettings;
wbMap.LoadHtml("<!DOCTYPE html><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><head><meta charset=\"utf-8\" /><title></title></head><body><input type=\"text\"><input /></body></html>");
wbMap.Dock = DockStyle.Fill;
return wbMap;
}
private void WbMap_FrameLoadEnd(object sender, FrameLoadEndEventArgs e)
{
Console.WriteLine("QGIS_WbMap_FrameLoadEnd");
}

Silverlight printing busy\progress bar

I'm using Silverlight 4 to print but I would like some sort of progress bar or busy indicator.
I've tried to using a progress bar but it is not really working. The 2 issues I have are:
the progress bar does not indicate progress, I have IsIndeterminate=True, but it does not animate when printing starts (Print dialog's Print button is clicked)
the progress bar visibility is not being set at the proper time, depending upon where I put the code to set visibility it displays either too soon (before print is clicked) or too late (after printing has worked for awhile)
I'm guessing, but I think the reason for the above is because when the print dialog is displayed Silverlight has handed off control to the OS for prining(??).
I tried using a dispatcher invoke but I get a security exception (dialog can only be displayed from user click).
Any ideas on how to deal with either of the above issues?
thanks.
Create a BusyIndicator in your XAML, it's a part of the Silverlight Toolkit. And then during the BeginPrint event set the BusyIndicator's IsBusy to True. Also during EndPrint set IsBusy back to false.
var docToPrint = new PrintDocument();
docToPrint.BeginPrint += (s, args) =>
{
MyBusyIndicator.IsBusy = true;
MyBusyIndicator.BusyContent = "Printing...";
};
docToPrint.PrintPage += (s, args) =>
{
args.PageVisual = this.MainCanvas;
};
docToPrint.EndPrint += (s, args) =>
{
MyBusyIndicator.IsBusy = false;
MyBusyIndicator.BusyContent = "";
};

WPF: How to apply a change to an Opacity/Background immediately? (WPF analog to WinForms Control.Update() method?)

I have a WPF app, upon clicking a button, the app goes into a calculation that can take 4-10 seconds. I'd like to update the opacity of the background and show a progress bar, during that operation.
To do that, I use this code:
this.Cursor = System.Windows.Input.Cursors.Wait;
// grey-out the main window
SolidColorBrush brush1 = new SolidColorBrush(Colors.Black);
brush1.Opacity = 0.65;
b1 = LogicalTreeHelper.FindLogicalNode(this, "border1") as Border;
b1.Opacity = 0.7;
b1.Background = brush1;
// long running computation happens here ....
// show a modal dialog to confirm results here
// restore background and opacity here.
When I run the code, the background and opacity doesn't change until the modal dialog appears. How can I get those visual changes to happen right now, before the calculation begins? In Windows Forms there was an Update() method on each control, that did this as necessary, as I recall. What's the WPF analog?
What if you would do long running computation in the background thread? Once they are done dispatch results back to UI thread...
Honestly, I suspect there is nothing else there, that can solve your problem. Maybe nested pumping will do the trick, but I really doubt it.
Just in case this reference is helpful: Build More Responsive Apps With The Dispatcher
Use the DoEvents() code as shown here:
http://blogs.microsoft.co.il/blogs/tamir/archive/2007/08/21/How-to-DoEvents-in-WPF_3F00_.aspx
My actual code:
private void GreyOverlay()
{
// make the overlay window visible - the effect is to grey out the display
if (_greyOverlay == null)
_greyOverlay = LogicalTreeHelper.FindLogicalNode(this, "overlay") as System.Windows.Shapes.Rectangle;
if (_greyOverlay != null)
{
_greyOverlay.Visibility = Visibility.Visible;
DoEvents();
}
}
private void DoEvents()
{
// Allow UI to Update...
DispatcherFrame f = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
new Action<object>((arg)=> {
DispatcherFrame fr = arg as DispatcherFrame;
fr.Continue= false;
}), f);
Dispatcher.PushFrame(f);
}

WinForms TabControl validation: Switch to a tab where validation failed

I currently have a Form with a TabControl containing some TabPages. Each TabPage has several controls with validation logic and appropriate ErrorProviders. On my OK_Button_Clicked event, I call Form.ValidateChildren() in order to determine whether to save and close the form . Now, suppose I have a control in tab 1 that fails validation, but the currently visible tab is tab 2. When the user presses OK, he would get no visual indication as to why the form is not closing. I would like to be able to automatically switch to a tab where validation failed, so the user would see the ErrorProvider's indication of error.
One approach would be subscribing to the Validated and validating events of all appropriate controls, and knowing which tab each of them is in, whenever one fails validation, a list of tabs that failed validation could be built. Since no ValidationFailed event is generated as far as I know, this could be cumbersome (e.g. defining a boolean for each control, setting it to false before validation and to true on its Validated event). And even if I had such an event, I would be forced to listen to many validation events, one for each control that might fail validation, and maintain the list of unvalidated tabs in code. I should note here that subscribing directly to the TabPage's validation events doesn't work, because they pass as validated even if controls contained inside them fail validation.
Another approach could leverage the fact that the controls in my TabPage happen to be custom controls. I could then make them implement an interface such as:
interface ILastValidationInfoProvider
{
public bool LastValidationSuccessful {get; set;}
}
For example:
public MyControl : UserControl, ILastValidationInfoProvider
{
MyControl_Validing(object sender, object sender, CancelEventArgs e)
{
if (this.PassesValidation())
this.ErrorProvider.SetError(sender, null);
LastValidationSuccessful = true;
else
e.Cancel = true;
this.ErrorProvider.SetError("Validation failed!", null);
LastValidationSuccessful = false;
}
}
And then, after the call to ValidateChildren I could use code such as:
public void OK_Button_Click
{
if (form.ValidateChildren())
this.Close()
else
foreach (TabPage tab in this.TabControl)
foreach (Control control in tab.Controls)
{
ValidationInfo = control as ILastValidationInfoProvider
if (ValidationInfo != null && !ValidationInfo.LastValidationSuccessful)
{
this.TabControl.SelectTab(tab);
return;
}
}
}
I like this approach better but it doesn't cater to cases where the controls being validated are not custom.
I would gladly use a better approach. Any ideas?
EDIT I am using Form.AutoValidate = EnableAllowFocusChange (as recommended by Chris Sells in his WinForms book), So the focus can indeed change from controls that failed validation (including moving to other tabs). I have also updated the sample code for the custom control to emphasize the fact that the ErrorProvider resides internally inside it.
OK so I finally figured it out.
I keep a dictionary whose keys are the TabPages and the values are HashSets of unvalidated controls within the corresponding tab. This is easily done by subscribing to all the validating and validated events of the controls in each tab. Finally, in OK_BUtton_Click, if ValidateChildren fails, I know one of the hashsets will be none empty and I simply jump to the first unvalidated tab (only if the currently selected tab doesn't have any error itself).
Dictionary<TabPage, HashSet<Control>> _tabControls
= new Dictionary<TabPage, HashSet<Control>>();
public OptionsForm()
{
InitializeComponent();
RegisterToValidationEvents();
}
private void RegisterToValidationEvents()
{
foreach (TabPage tab in this.OptionTabs.TabPages)
{
var tabControlList = new HashSet<Control>();
_tabControls[tab] = tabControlList;
foreach (Control control in tab.Controls)
{
var capturedControl = control; //this is necessary
control.Validating += (sender, e) =>
tabControlList.Add(capturedControl);
control.Validated += (sender, e) =>
tabControlList.Remove(capturedControl);
}
}
}
private void Ok_Button_Click(object sender, EventArgs e)
{
if (this.ValidateChildren())
{
_settings.Save();
this.Close();
}
else
{
var unvalidatedTabs = _tabControls.Where(kvp => kvp.Value.Count != 0)
.Select(kvp => kvp.Key);
TabPage firstUnvalidated = unvalidatedTabs.FirstOrDefault();
if (firstUnvalidated != null &&
!unvalidatedTabs.Contains(OptionTabs.SelectedTab))
OptionTabs.SelectedTab = firstUnvalidated;
}
}
I think it's pretty sweet !

WPF ComboBox DropDown part appears in the wrong place

I put several ComboBoxes on a XAML window. When I expand any of them, the DropDown part appears on the upper left corner of the screen.
I use Visual Studio 2008 C# Express. I don't remember this phenomenon when I used Visual Studio 2008 (Trial Version), though I use the same FrameWork (3.5).
It seems to be a bug.
Workaround:
Use Window.Show() instead with a custom logic to simulate the ShowDialog() behavior.
This appears to be a bug in WPF. In my case, I was trying to open a window in the Loaded event of another window. To get around this, I set a timer up to fire, then used a delegate to open the window (cannot open the window in a timer event because the calling thread that opens a window must be STA).
Edit - timer isn't necessary - didn't see the answer above just queue it on the dispatcher...
private delegate void DelegateOpenWindow();
private DelegateOpenWindow m_DelegateOpenWindow;
private Timer loginTimer = new Timer(200);
private void MainWindow1_Loaded(object sender, RoutedEventArgs e)
{
// create delegate used for asynchronous call
m_DelegateOpenWindow= new DelegateOpenWindow(this.OpenWindow);
// start a timer to fire off the open window.
loginTimer.Elapsed += loginTimer_Elapsed;
loginTimer.Enabled = true;
}
void loginTimer_Elapsed(object sender, ElapsedEventArgs e)
{
loginTimer.Enabled = false;
this.Dispatcher.BeginInvoke(m_DelegateOpenWindow);
}
void OpenWindow()
{
MyWindow w = new MyWindow();
w.Owner = this;
w.ShowDialog();
}
I started observing this (and other strange behavioral quirks) yesterday when I tried to "tweak" window sizes, shapes, colors, and invoke a log-on dialog from the Window.Loaded event handler. I had been doing this just fine in each of a dozen+ individual "MVVM" pattern apps. Yesterday, I decided to move this from each app's code behind into a consolidated code-behind base class, since the pre-processing had become common in all those apps. When I did, the drop-downs in two ComboBoxes in the log-in dialog suddenly appeared in the upper left corner of my screen. I seem to have "solved" it by using the following technique (your mileage may vary):
protected void WindowBaseLoadedHandler(object sender, RoutedEventArgs e)
{
...non-essential lines of code removed...
if (DataContext != null)
{
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
/*----------------------------------------------------------------------
* Do we have a View Model? If so, perform standard VM Initialization...
*---------------------------------------------------------------------*/
this.IsEnabled = false;
LoginDlg loginDlg = new LoginDlg();
loginDlg.ShowDialog();
if (!loginDlg.Success)
{
/*-----------------------------------
* Log on failed -- terminate app...
*----------------------------------*/
...termination logic removed...
}
this.IsEnabled = true;
}));
}
WindowBaseLoadedHandler is the Loaded event handler. LoginDlg is a WPF app with a dialog containing two ComboBoxes.
Recap: After I consolidated the code into the Loaded event handler of the base class the ComboBox's drop down lists appeared in the upper left corner of my screen. Once I wrapped the logic into the Dispatcher.BeginInvoke call, the appropriate ComboBox behavior returned with lists below the current item.
I suspect WPF needs the application to return from the Loaded event to complete the layout system's initialization. That doesn't fully explain why it worked before, but I'll have to queue up my desire to hunt that "why" down for some rainy day in the future and celebrate overcoming the latest obstacle for today.
In any event, I hope someone finds this of use.
I'm using the latest .Net 4.5 and WPF framework and I still have this problem. One thing I noticed is that it only happen when there's an attached debugger. When the debugger is not attached, everything works fine.
I had the same problem on Visual Studio 2019.
Using window.Show() can help but it can ruin your design.
The solution is to open the window asynchronously.
var yourDialog= new YourDialog();
yourDialog.Owner = this;
TaskCompletionSource<bool?> completion = new TaskCompletionSource<bool?>();
this.Dispatcher.BeginInvoke(new Action(() =>
completion.SetResult(yourDialog.ShowDialog())));
bool? result = await completion.Task;
You can also create a more elegant solution by making the extension method:
public static class AsyncWindowExtension
{
public static Task<bool?> ShowDialogAsync(this Window self)
{
if (self == null) throw new ArgumentNullException("self");
TaskCompletionSource<bool?> completion = new TaskCompletionSource<bool?>();
self.Dispatcher.BeginInvoke(new Action(() => completion.SetResult(self.ShowDialog())));
return completion.Task;
}
}
And you can use it like this:
await dlgReview.ShowDialogAsync();
It’s a bug in WPF (not the only one, I'm afraid). It happened when I opened another window in the Loaded Event, something like:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Window selectionWindow = new SelectionWindow();
bool? result = selectionWindow.ShowDialog();
if (result == true)
RecordChanged();
}
I already found a workabout.

Resources