How do I simulate Visual Studio application window activation? - wpf

I'm trying to do something similar to what visual studio does with it's windows. I have a similar tabbed interface and I have added the ability to undock a tab into a separate window. The functionality I'm trying to get is where a user activates one of the windows of the application, all of the application windows should be brought to the top. My first thought was to override the OnActivated Window method where I would activate all of the other application windows. However, this is insufficient because that ends up bringing all of the other windows on top of the window I just activated (since OnActivated is called after the window is activated). I can't activate it last in this event because that would fire the activated event again (infinite loop). It would be nice if there was an OnActivating event that was call before the window was activated.
Has anyone else done this?
This doesn't work because it brings the other windows on top of the one just activated.
public partial class EditorWindow : Window
{
public event EventHandler ActivateOtherWindows;
protected override void OnActivated(EventArgs e)
{
if(ActivateOtherWindows != null)
{
ActivateOtherWindows(this, EventArgs.Empty);
}
base.OnActivated(e);
}
}

Related

Windows Narrator reads the names of all the controls in the window (even hidden ones)

I need to make my application visually impaired friendly... and I am facing this problem: Windows Narrator reads all the controls names in the window despite that some of them are hidden.
I have another app that I used WinForms to write it, and there it works fine.
After looking in the UI Spy I saw that WinForms app is not exposing hidden controls and WPF is exposing all the controls in the window.
Can it be that it's a bug in WPF?
I was having the same problem.
Based on Alexis's answer, I wrote the code bellow. It works for me.
public class MyAutoComplete : RadAutoCompleteBox
{
public MyAutoComplete ()
{
//init stuff here
}
protected override AutomationPeer OnCreateAutomationPeer()
{
return new MyAutomationPeer(this);
}
}
internal class MyAutomationPeer : RadAutoCompleteBoxAutomationPeer
{
public MyAutomationPeer(FrameworkElement owner)
: base(owner)
{
}
protected override List<AutomationPeer> GetChildrenCore()
{
return new List<AutomationPeer>();
}
}
If your controls are already in the visual tree, this behavior is the normal one, because UI Automation tree based on the Visual tree. So if you want to prevent of reading unnecessary elements using screen readers, you have to load them on demand.
You can also override the OnCreateAutomationPeer method in controls that contain visible and hidden elements to return your own AutomationPeer. Then you can override the GetChildrenCore method and return modified children collection. To update automation children tree, you need to call the AutomationPeer.ResetChildrenCache() method and the AutomationPeer.RaiseAutomationEvent(AutomationEvents.StructureChanged) one.

How to make a "popup" (hint, drop-down, popup) window in Winforms?

How can i make, what i will call, a "popup" window" in WinForms?
Since i used my own made-up word "popup", let me give examples of this so-called "popup" window:
a tooltip window (can extend outside the boundaries of its parent form, doesn't appear in the taskbar, is not modal, and doesn't steal focus):
a popup menu window (can extend outside the boundaries of its parent form, doesn't appear in the taskbar, is not modal, and doesn't steal focus):
a drop-down window (can extend outside the boundaries of its parent form, doesn't appear in the taskbar, is not modal, and doesn't steal focus):
A main menu window (can extend outside the boundaries of its parent form, doesn't appear in the taskbar, is not modal, and doesn't steal focus):
Update A popup window not make itself the "active" window when interacted with using a mouse or keyboard (the "owner" window remains the active window):
The attributes that i'm looking for in this mythical "popup" are that it:
can extend outside the boundaries of its parent form (i.e. is not a child window)
doesn't appear in the taskbar (i.e. Window's heuristics of which windows should appear doesn't kick in, nor does it have WS_EX_APPWINDOW extended window style)
is not modal (i.e. doesn't disable its "owner")
doesn't steal focus
is always on-top of of it's "owner"
does not become the "active" window when interacted with (the owner remains active)
Windows applications are already managing to create such windows. How can i do it in a WinForms application?
Related questions
How do i achieve all the above in native code?
How do i create a popup window in Delphi?
i have this native code to show a "popup" window - what P/Invokes are required to perform the same actions in .NET?
i have a set of P/Invoke's in .NET - can i reuse a regular WinForm, overriding certain methods, to achieve the same effect?
i have WinForm that i'm showing as a "popup" by overriding certain methods - is there a built-in Control that can act as a popup for me?
How to simulate a drop-down window in WinForms?
Attempt#1
i tried the Show(onwer) + ShowWithoutActivation method:
PopupForm dd = new PopupForm ();
dd.Show(this);
with PopupForm:
public class PopupForm: Form
{
public PopupForm()
{
InitilizeComponent();
}
private void InitilizeComponent()
{
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.WindowState = FormWindowState.Normal;
this.ShowInTaskbar = false;
}
protected override bool ShowWithoutActivation
{ get { return true; } }
}
Very nearly solved the problem, but then i discovered was reminded of another property of "popup" windows: they do not take focus from their "owner" form become active when interacted with by mouse or keyboard.
You want an owned window. In your main form:
private void showPopup_Click(object sender, EventArgs e)
{
PopupForm popupForm = new PopupForm();
// Make "this" the owner of form2
popupForm.Show(this);
}
PopupForm should look like this:
public partial class PopupForm : Form
{
private bool _activating = false;
public PopupForm()
{
InitializeComponent();
}
// Ensure the popup isn't activated when it is first shown
protected override bool ShowWithoutActivation
{
get
{
return true;
}
}
private const int WM_NCACTIVATE = 0x86;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
protected override void WndProc(ref Message m)
{
// The popup needs to be activated for the user to interact with it,
// but we want to keep the owner window's appearance the same.
if ((m.Msg == WM_NCACTIVATE) && !_activating && (m.WParam != IntPtr.Zero))
{
// The popup is being activated, ensure parent keeps activated appearance
_activating = true;
SendMessage(this.Owner.Handle, WM_NCACTIVATE, (IntPtr) 1, IntPtr.Zero);
_activating = false;
// Call base.WndProc here if you want the appearance of the popup to change
}
else
{
base.WndProc(ref m);
}
}
}
And make sure that PopupForm.ShowInTaskbar = false.
I was curious as to how combobox dropdowns and menus work, so I did some more research.
There are two basic approaches.
Create the popup as an overlapped window, owned by the main window
This method is required if the popup has embedded controls, or if you want the popup to behave as a modeless dialog.
If the user is going to interact with child controls in the popup window, it must receive activation. (So the various techniques for blocking activation, such as handling WM_MOUSEACTIVATE are red herrings.) And when it receives activation, Windows will deactivate the main window. The fix for this is to send a WM_NCACTIVATE message to the parent to update its visual appearance without changing its activation status. This is the approach used by the .Net ToolStrip, and my other answer illustrates it with code.
Create the popup as a child of the Desktop window
This method is used by combobox dropdowns and (I guess) menus, but you can't embed child controls so it's not widely applicable.
The popup is a child window so it doesn't interfere with activation. It is always on top. Mouse capture is used to detect clicks outside the popup and dismiss it.
However, this isn't straightforward to implement. Activation remains with the main application, so it keeps the focus and receives keystrokes. This seems to preclude embedded controls in the popup because they can't receive focus. The combobox handles this by forwarding keystroke messages to its dropdown list. (Note that menus, combobox dropdowns, etc. are all entirely owner-draw in the sense that they have no embedded windows.)
Create your "popup" window as a child window of desktop, then show it without activating it.
hWnd = CreateWindowEx(..., WS_CHILDWINDOW | WS_VISIBLE | WS_BORDER | WS_CLIPSIBLINGS, ..., GetDesktopWindow(), ...);
SetWindowPos(hWnd, HWND_TOPMOST, ..., SWP_NOACTIVATE);
After doing this, your original window remains activated even if you click on the "popuped" window. The "popup" window can have its own children controls. You can click the button on it. But if it is an edit control, you cannot edit it, I don't know why. Maybe because there is already a cursor on your original window, blinking.

Appropriate point to attach NodeChanged event, XAML

I've got a WPF usercontrol in a winforms application form.
Basically, I want a generic eventhandler attached to my WPF TreeView to handle "Document.NodeChanged". As this particular event fires when the tree is populated, I attempted to do a late attachment, via my treeview control's Loaded event.
The code goes something like:
private void UpdateGrid()
{
myGridView.UpdateXML(entityId, runDate, rtbToggleFullView.ToggleState == Telerik.WinControls.Enumerations.ToggleState.Off, userName);
//Safely attach the event to fire when the treeview has finished loading.
myGridView.tvRatings.Loaded -= AttachNodeChangedEvent;
myGridView.tvRatings.Loaded += AttachNodeChangedEvent;
}
Then the "AttachNodeChangedEvent" method looks like this:
public void AttachNodeChangedEvent(object i, EventArgs a)
{
((XmlDataProvider)myGridView.dataProvider).Document.NodeChanged -= OnNodeChanged;
((XmlDataProvider)myGridView.dataProvider).Document.NodeChanged += OnNodeChanged;
}
With a simple OnNodeChanged method:
public void OnNodeChanged(object i, EventArgs a)
{
Dirty = true;
}
The idea being:-
UpdateGrid runs UpdatesXML on treeview
Attaches a "NodeChangeHandler attacher" to treeview.Loaded
(when treeview is loaded) treeview fires "NodeChangeHandler attacher" which then attaches "OnNodeChanged" to the Treeview's populated XmlDocumentProvider.
This appeared to work perfectly in Windows 7. In Windows XP, however, the AttachNodeChangedEvent routine fires, and experiences a NullReferenceException (presumably because the Document hasn't loaded yet?) crashing the app.
Commenting out the ...((XmlDataProvider)myGridView... lines fixes the crash, but obviously disables the functionality.
Can anyone suggest a better way of achieving the same, or shed some light on why this works for Windows 7, but not Windows XP? "e.g. Attach NodeChangedEvent after the initial population of the Treeview"
I can confirm that both use the appropriate .Net Framework 4 package and seem to have all other dependencies appropriately included.
Thanks!

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.

WPF: Should I manually call Close on windows opened by ShowDialog method?

The thing is that WPF Window doesn't implement IDisposable interface which led me to believe, that I don't have to manually dispose of it when I open it by calling ShowDialog() but the first comment on this MSDN page states differently. Does anybody know what's the truth?
Only if you open the window using Show() instead of ShowDialog().
From the documentation:
If a window, opened by calling ShowDialog, and with a Button with its IsCancel property set to true, will automatically close when the button is either clicked, or ESC is pressed. If the window was opened using Show, however, Close must be explicitly called, such as from Click event handler for the Button.
and:
Closing a window causes the Closing event to be raised. If the Closing event isn't canceled, the following occurs:
The Window is removed from Application.Windows (if an Application object exists).
The Window is removed from the owner Window if the owner/owned relationship was established before the owned Window was shown and after the owner Window was opened.
The Closed event is raised.
Unmanaged resources created by the Window are disposed.
If ShowDialog was called to show the Window, ShowDialog returns.
How about the code below? implementing IDisposable on your form and removing all event handlers.
However, Is Microsoft already doing that on Close()?
Also does manual GC.Collect help?
Some references and related posts:
What is the correct way to dispose of a WPF window?
What is the correct way to dispose of a WPF window?
/// <summary>
/// Interaction logic for MyForm.xaml
/// </summary>
public partial class MyForm: IDisposable
{
public MyForm()
{}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
Dispose();
}
public void Dispose()
{
try{
_resourcescollection.Clear();
_resourcescoll = null;
//close other resources
}
catch(exception ex)
{}
EventHelper.RemoveAllEventHandlers(_resources);
EventHelper.RemoveAllEventHandlers(_timer);
EventHelper.RemoveAllEventHandlers(_etc);
EventHelper.RemoveAllEventHandlers(this);
}
~MyForm()
{
Dispose();
}
}

Resources