WPF, non-overlapping windows [duplicate] - c#

I have a multi-window application in WPF, where my main application window is invisible (Visibility=Collapsed) containing visible child windows. Application creates child windows on-demand. I need an algorithm to determine the coordinates and dimensions of the newly created child window. Obviously, the new child window should not cover (fully) another child window. Does WPF offer any support whatsoever to implement this kind of logic ? Or, do I have to do everything on my own. I imagine this would be a lot of work. The behavior I am looking for is very similar to Sticky Notes behavior in Windows 7.
Part of my code will help you to really understand what I mean:
public void ViewModelsCollectionChanged(object sender,
NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (ViewModel viewModel in e.NewItems)
{
View view = new View(viewModel);
view.Owner = SleekNoteUI.App.Current.MainWindow;
...
}
}
}

Related

C# Topmost=true - restrict to application

Our Application is displaying an "update-hint", if a new version is available.
This Update-hint should be "topmost" within the application, but, if the application is minimized, or send to the background, the update-hint should vanish as well..
Just using
this.TopMost = true;
will overlay "ANY" application that is currently running...
Is there a way to just "overlay" windows generated by the current application?
Desired:
application shows update-hint on top of every window, while application is in the foreground. Switching to another application will also send the update-hint to the background.
Desired: Update-Hint overlays ANY window of the current application:
Not desired: Update-Hint overlays FOREIGN applications as well:
Despite the name of the property, TopMost is actually your enemy here. To make a "floating form" stay above a main form, without obscuring other applications when they get focus, try it this way:
FormX f = new FormX();
f.Show(this);
"this" in this example, is the main form instance. It means the form you created is now owned by the main form, and will make it float above it. You get the added benefit of, when minimizing the main form, the floating forms will disappear, too.
I figured out a way to solve it.
Setting the owner of the UpdateHint is required, but in order to keep it on top of every application window, one has to change the owner, if a new Window is either shown, or activated.
Within our application, every Form is inheriting InterceptorForm, so all I had to do was to modify the InterceptorForm accordingly:
Change the Owner to this, except if there is no dialog, or this is the dialog itself:
public class InterceptorForm : Form
{
protected override void OnLoad(EventArgs e)
{
...
if (this.GetType() != typeof(UpdateHint) && MainWindow.updateHint != null)
{
Log.t("Setting owner on update hint during onload: " + this.GetType());
MainWindow.updateHint.Owner = this;
}
base.OnLoad(e);
}
and
protected override void OnActivated(EventArgs e)
{
if (this.GetType() != typeof(UpdateHint) && MainWindow.updateHint != null)
{
Log.t("Setting owner on update hint: " + this.GetType());
MainWindow.updateHint.Owner = this;
}
base.OnActivated(e);
}
}
The UpdateHint now stays on top of every window belonging to our application, but can be overlayed by any other application.

Alt keys and tab don't work in Windows Forms opened from a WPF application

I have lots of old Windows Forms applications that will eventually be ported to WPF (it is a large application so it can't be done in one sprint), and I have started the process by creating a main menu in WPF. The Windows Forms applications are separate windows opened from this menu.
The Windows Forms applications are opening and working without any problems except the issues I am having with the shortcut and Tab keys. The tab key is not moving focus to the next control, and the Alt key to trigger the &Search button no longer works.
What am I doing wrong?
A partial solution I discovered is to call this from your WPF constructor:
System.Windows.Forms.Integration.WindowsFormsHost.EnableWindowsFormsInterop();
(You need to reference the dll WindowsFormsIntegration.dll)
I say partial because not all key strokes function as expected. Eg, seems to work okay for simple forms.
See this:
http://msdn.microsoft.com/en-us/library/system.windows.forms.integration.windowsformshost.enablewindowsformsinterop(v=vs.100).aspx
I finally managed to fix the issue by hosting the winform inside a WindowsFormsHost control inside a WPF form.
public partial class MyWindow : Window
{
public MyWindow()
{
InitializeComponent();
Form winform = new Form();
// to embed a winform using windowsFormsHost, you need to explicitly
// tell the form it is not the top level control or you will get
// a runtime error.
winform.TopLevel = false;
// hide border because it will already have the WPF window border
winform.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.windowsFormsHost.Child = winform;
}
}
Please note that you may also need to hook up the winform close event if you have a button to close the form.
This is by design. Shortcut keys are handled at the message loop level, detected before the Windows message gets dispatched to the window with the focus. That's the reason those keys can work regardless of the focus.
Problem is, you don't have the Winforms message loop pumping the messages. Application.Run() is implemented by WPF in your program, not Winforms. So any of the code in Winforms that processes keyboard messages to implement shortcut keystrokes won't run.
There's no good solution for this, it is pretty fundamentally the "can't get a little pregnant" problem. This code in Winforms is locked up heavily since it would allow CAS bypass. The only workaround is to display a Form derived class that contain Winforms controls with its ShowDialog() method. That method pumps a modal message loop, the Winforms one, good enough to revive the shortcut keystroke handling code. Restructure your approach by converting the main windows first, dialogs last.
Another solution I found to handle focus on the Tab key is to override OnKeyDown like this:
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Tab)
{
HandleFocus(this, ActiveControl);
}
else
{
base.OnKeyDown(e);
}
}
internal static void HandleFocus(Control parent, Control current)
{
Keyboard keyboard = new Keyboard();
// Move to the first control that can receive focus, taking into account
// the possibility that the user pressed <Shift>+<Tab>, in which case we
// need to start at the end and work backwards.
System.Windows.Forms.Control ctl = parent.GetNextControl(current, !keyboard.ShiftKeyDown);
while (null != ctl)
{
if (ctl.Enabled && ctl.CanSelect)
{
ctl.Focus();
break;
}
else
{
ctl = parent.GetNextControl(ctl, !keyboard.ShiftKeyDown);
}
}
}
The advantage of this solution is that it doesn't require neither a WindowsFormsHost nor a message pump which can be a hassle to implement. But I don't know if it is possible to handle shortcuts keys like this because I didn't need it.
Check if IsTabStop="True" and TabIndex is assigned. For Alt + Key shortcut, try using the underscore (_) character instead of the ampersand (&).

A way to monitor when a Control's screen location changes?

With WinForms, is there a way to be alerted to a control changing location with respect to the screen?
Say you have a Form with a button on it, and you would like to know when the button is moved from its current pixel location on the screen. If the button is moved to a different location on its parent Form you could obviously use the LocationChanged event, but if the Form is moved by the user, how do you know the button has visually moved?
In this simplified case the quick answer is to monitor the Form's LocationChanged and SizeChanged events, but there can be an arbitrary number of levels of nesting so monitoring those events for each parent up the chain to the primary form is not feasible. Using a timer to check if the location changed also seems like cheating (in a bad way).
Short version:
Given only an arbitrary Control object, is there a way to know when that Control's location changes on the screen, without knowledge of the control's parent hierarchy?
An illustration, by request:
Note that this "pinning" concept is an existing capability but it currently requires knowledge of the parent form and how the child control behaves; this is not the problem I am trying to solve. I would like to encapsulate this control tracking logic in an abstract Form that "pin-able" Forms can inherit from. Is there some message pump magic I can tap into to know when a control moves on the screen without having to deal with all the complicated parent tracking?
I'm not sure why you would say tracking the parent chain "is not feasible". Not only is it feasible, it's the right answer and the easy answer.
Just a quick hack at a solution:
private Control _anchorControl;
private List<Control> _parentChain = new List<Control>();
private void BuildChain()
{
foreach(var item in _parentChain)
{
item.LocationChanged -= ControlLocationChanged;
item.ParentChanged -= ControlParentChanged;
}
var current = _anchorControl;
while( current != null )
{
_parentChain.Add(current);
current = current.Parent;
}
foreach(var item in _parentChain)
{
item.LocationChanged += ControlLocationChanged;
item.ParentChanged += ControlParentChanged;
}
}
void ControlParentChanged(object sender, EventArgs e)
{
BuildChain();
ControlLocationChanged(sender, e);
}
void ControlLocationChanged(object sender, EventArgs e)
{
// Update Location of Form
if( _anchorControl.Parent != null )
{
var screenLoc = _anchorControl.Parent.PointToScreen(_anchorControl.Location);
UpdateFormLocation(screenLoc);
}
}

Handling flicks on object

I have created a new UIElement that derives from Systen.Windows.Controls.Canvas.
I am trying to handle flicks made on this object.
Everything is done in C#, in code (no XAML) using the Silverlight Phone Toolkit (February version as I want to target 7.0)
In my object constructor I do:
//Create gesture handling
gl = GestureService.GetGestureListener(this);
gl.Flick += new EventHandler<FlickGestureEventArgs>(gl_Flick);
and gl_Flick is simply:
void gl_Flick(object sender, FlickGestureEventArgs e)
{
if (e.HorizontalVelocity >= 0)
{
// Right swipe (flick)
if (gotSwipe != null)
{
gotSwipe(this, e);
}
}
}
Now, in the constructor, I also create and add a few TextBlocks
For some reason, the flick is only generating an event if done over one of those TextBocks. If I do the flick on any of the empty area of the Canvas nothing occurs.
As I can't find any documentations related to the Silverlight toolkit, everything has been done via trials&errors.
How could I do, so the flick will be recognised when performed anywhere over this canvas and not limited to over the children it contains?
I'm more focused towards WPF, but the way you talk about it reminds me of the classic null background issue.
The solution: set a non null background for your canvas.
EDIT: something like
myCanvas.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 1));

Is it absolutely safe to display a WPF window from a WinForms form?

I would like to display a WPF window from a windows forms application (.NET 3.5).
This code seems to work without any problem in a sample project:
public partial class WinFormsForm1 : Form
{
public WinFormsForm1() {
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) {
WpfWindow1 w = new WpfWindow1();
w.Show();
}
}
The form is started from Main() as a normal Winforms form:
Application.Run(new WinFormsForm1());
This seems to me too easy to be true. Are there any shortcomings in this? Is this safe to do?
It has one serious shortcoming: the modeless WPF window would not get keyboard input.
The EnableModelessKeyboardInterop method call needs to be added before the WPF window is shown:
WpfWindow1 w = new WpfWindow1();
System.Windows.Forms.Integration.ElementHost.EnableModelessKeyboardInterop(w);
w.Show();
ElementHost resides in WindowsFormsIntegration.dll.
Further reading: http://msdn.microsoft.com/en-us/library/aa348549.aspx
Bottom line: it is.
We have pretty heavy application combining both WPF and winforms: windows, user controls, modal dialogs, you name it... and it's working in the medical market.
We've got into some dark corners, one is the infamous EnableModelessKeyboardInterop, another is having the WPF window be the child of the Winforms window, which you can read Here
It is really that simple. I can't think of any downside doing it this way.

Categories