Skip to content

Commit

Permalink
Merge pull request #2034 from cwensley/curtis/wpf-focus-and-visible-w…
Browse files Browse the repository at this point in the history
…indows

Wpf: Fix issues with Window.CanFocus and Visible
  • Loading branch information
cwensley authored Oct 1, 2021
2 parents 68b5120 + 3b88a75 commit fc3d728
Show file tree
Hide file tree
Showing 7 changed files with 252 additions and 157 deletions.
42 changes: 40 additions & 2 deletions src/Eto.WinForms/Win32.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using System.Windows.Forms;
using System.Text;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;

namespace Eto
{
Expand Down Expand Up @@ -175,7 +177,7 @@ public enum WM
NCCREATE = 0x0081,
NCLBUTTONDOWN = 0x00A1
}

public enum HT
{
CAPTION = 0x2
Expand Down Expand Up @@ -435,10 +437,46 @@ public static bool ApplicationIsActivated()


[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern IntPtr GetForegroundWindow();
public static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetGUIThreadInfo(uint idThread, ref GUITHREADINFO lpgui);

[StructLayout(LayoutKind.Sequential)]
public struct GUITHREADINFO
{
public int cbSize;
public uint flags;
public IntPtr hwndActive;
public IntPtr hwndFocus;
public IntPtr hwndCapture;
public IntPtr hwndMenuOwner;
public IntPtr hwndMoveSize;
public IntPtr hwndCaret;
public RECT rcCaret;
}

[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();

public static bool GetInfo(out GUITHREADINFO lpgui, uint? threadId = null)
{
lpgui = new GUITHREADINFO();
lpgui.cbSize = Marshal.SizeOf(lpgui);

return GetGUIThreadInfo(threadId ?? GetCurrentThreadId(), ref lpgui);
}

public static IntPtr GetThreadFocusWindow(uint? threadId = null)
{
if (!GetInfo(out var info, threadId))
return IntPtr.Zero;

return info.hwndFocus;
}
}
}
3 changes: 1 addition & 2 deletions src/Eto.Wpf/Forms/ApplicationHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,8 @@ void OnCurrentDomainUnhandledException(object sender, System.UnhandledExceptionE

void HandleStartup(object sender, sw.StartupEventArgs e)
{
IsActive = true;
IsStarted = true;
_isActive = Win32.ApplicationIsActivated();
IsActive = Win32.ApplicationIsActivated();
Control.Activated += (sender2, e2) => IsActive = true;
Control.Deactivated += (sender2, e2) => IsActive = false;
if (delayShownWindows != null)
Expand Down
43 changes: 35 additions & 8 deletions src/Eto.Wpf/Forms/FloatingFormHandler.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
using System;
using System.Windows;
using System.Windows.Interop;
using Eto.Forms;

namespace Eto.Wpf.Forms
{
public class FloatingFormHandler : FormHandler, FloatingForm.IHandler
{
static readonly object Visible_Key = new object();

bool _wasActive;

protected override void Initialize()
{
base.Initialize();
Expand All @@ -31,19 +37,40 @@ public override void OnUnLoad(EventArgs e)

private void Application_IsActiveChanged(object sender, EventArgs e)
{
base.Visible = ApplicationHandler.Instance.IsActive && _visible;
SetVisibility();
}

bool _visible = true;


public override bool Visible
{
get => _visible;
get => Widget.Properties.Get<bool>(Visible_Key, true);
set
{
_visible = value;
if (ApplicationHandler.Instance.IsActive)
base.Visible = value;
if (Widget.Properties.TrySet(Visible_Key, value, true))
SetVisibility();
}
}

void SetVisibility()
{
var currentlyVisible = base.Visible;
var isVisible = ApplicationHandler.Instance.IsActive && Visible;
if (isVisible == currentlyVisible)
return;

if (!isVisible)
{
if (currentlyVisible)
{
_wasActive = Win32.GetThreadFocusWindow() == NativeHandle;
}
base.Visible = isVisible;
}
else
{
var oldShowActivated = Control.ShowActivated;
Control.ShowActivated = _wasActive;
base.Visible = isVisible;
Control.ShowActivated = oldShowActivated;
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/Eto.Wpf/Forms/FormHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ public virtual void Show()
{
Control.WindowStartupLocation = sw.WindowStartupLocation.Manual;
if (ApplicationHandler.Instance.IsStarted)
{
Control.Show();
}
else
ApplicationHandler.Instance.DelayShownWindows.Add(Control);
WpfFrameworkElementHelper.ShouldCaptureMouse = false;
Expand All @@ -65,9 +67,8 @@ public bool CanFocus
get { return Control.Focusable; }
set
{
SetStyle(Win32.WS_EX.NOACTIVATE, !value);
SetStyle(Win32.WS.CHILD, !value);
Control.Focusable = value;
SetStyleEx(Win32.WS_EX.NOACTIVATE, !value);
}
}
}
Expand Down
139 changes: 78 additions & 61 deletions src/Eto.Wpf/Forms/WpfFrameworkElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -439,51 +439,17 @@ public override void AttachEvent(string id)
HandleEvent(Eto.Forms.Control.MouseDownEvent);
break;
case Eto.Forms.Control.MouseEnterEvent:
ContainerControl.MouseEnter += (sender, e) =>
{
if (isMouseOver != Control.IsMouseOver)
{
var args = e.ToEto(Control);
Callback.OnMouseEnter(Widget, args);
e.Handled = args.Handled;
isMouseOver = Control.IsMouseOver;
}
};
ContainerControl.MouseEnter += HandleMouseEnter;
break;
case Eto.Forms.Control.MouseLeaveEvent:
HandleEvent(Eto.Forms.Control.MouseEnterEvent);
ContainerControl.MouseLeave += (sender, e) =>
{
if (isMouseOver != Control.IsMouseOver)
{
var args = e.ToEto(Control);
Callback.OnMouseLeave(Widget, args);
e.Handled = args.Handled;
isMouseOver = Control.IsMouseOver;
}
};
ContainerControl.MouseLeave += HandleMouseLeave;
break;
case Eto.Forms.Control.MouseWheelEvent:
ContainerControl.PreviewMouseWheel += (sender, e) =>
{
var args = e.ToEto(Control);
Callback.OnMouseWheel(Widget, args);
e.Handled = args.Handled;
};
ContainerControl.PreviewMouseWheel += HandlePreviewMouseWheel;
break;
case Eto.Forms.Control.SizeChangedEvent:
ContainerControl.SizeChanged += (sender, e) =>
{
//
var size = e.NewSize.ToEtoSize();
var prev = e.PreviousSize.ToEtoSize();
if (size != prev) // WPF calls this event even if it hasn't changed
{
newSize = size; // so we can report this back in Control.Size
Callback.OnSizeChanged(Widget, EventArgs.Empty);
newSize = null;
}
};
ContainerControl.SizeChanged += HandleSizeChanged;
break;
case Eto.Forms.Control.KeyDownEvent:
if (UseKeyPreview)
Expand All @@ -504,25 +470,10 @@ public override void AttachEvent(string id)
Control.KeyUp += HandleKeyUp;
break;
case Eto.Forms.Control.ShownEvent:
ContainerControl.IsVisibleChanged += (sender, e) =>
{
if ((bool)e.NewValue)
{
Callback.OnShown(Widget, EventArgs.Empty);
}
};
ContainerControl.IsVisibleChanged += HandleIsVisibleChanged;
break;
case Eto.Forms.Control.GotFocusEvent:
Control.IsKeyboardFocusWithinChanged += (sender, e) =>
{
// sometimes this gets called after disposed? Happens with designer in VS 2017
if (Control == null)
return;
if (HasFocus)
Callback.OnGotFocus(Widget, EventArgs.Empty);
else
Callback.OnLostFocus(Widget, EventArgs.Empty);
};
Control.IsKeyboardFocusWithinChanged += HandleIsKeyboardFocusWithinChanged;
break;
case Eto.Forms.Control.LostFocusEvent:
HandleEvent(Eto.Forms.Control.GotFocusEvent);
Expand Down Expand Up @@ -551,6 +502,70 @@ public override void AttachEvent(string id)
}
}

private void HandleIsKeyboardFocusWithinChanged(object sender, sw.DependencyPropertyChangedEventArgs e)
{
// sometimes this gets called after disposed? Happens with designer in VS 2017
if (Control == null)
return;
if (HasFocus)
Callback.OnGotFocus(Widget, EventArgs.Empty);
else
Callback.OnLostFocus(Widget, EventArgs.Empty);
}

private void HandlePreviewMouseWheel(object sender, swi.MouseWheelEventArgs e)
{
if (Control == null)
return;
var args = e.ToEto(Control);
Callback.OnMouseWheel(Widget, args);
e.Handled = args.Handled;
}

private void HandleIsVisibleChanged(object sender, sw.DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
Callback.OnShown(Widget, EventArgs.Empty);
}
}

private void HandleSizeChanged(object sender, sw.SizeChangedEventArgs e)
{
if (Control == null)
return;
var size = e.NewSize.ToEtoSize();
var prev = e.PreviousSize.ToEtoSize();
if (size != prev) // WPF calls this event even if it hasn't changed
{
newSize = size; // so we can report this back in Control.Size
Callback.OnSizeChanged(Widget, EventArgs.Empty);
newSize = null;
}
}

private void HandleMouseLeave(object sender, swi.MouseEventArgs e)
{
if (Control == null || isMouseOver == Control.IsMouseOver)
return;

var args = e.ToEto(Control);
Callback.OnMouseLeave(Widget, args);
e.Handled = args.Handled;
isMouseOver = Control.IsMouseOver;
}

private void HandleMouseEnter(object sender, swi.MouseEventArgs e)
{
if (Control == null || isMouseOver == Control.IsMouseOver)
return;

var args = e.ToEto(Control);
Callback.OnMouseEnter(Widget, args);
e.Handled = args.Handled;
isMouseOver = Control.IsMouseOver;
}

private void Control_DragDrop(object sender, sw.DragEventArgs e)
{
var args = GetDragEventArgs(e, null);
Expand All @@ -565,6 +580,8 @@ private void Control_DragOver(object sender, sw.DragEventArgs e)

private void Control_IsEnabledChanged(object sender, sw.DependencyPropertyChangedEventArgs e)
{
if (Control == null)
return;
Callback.OnEnabledChanged(Widget, EventArgs.Empty);
}

Expand Down Expand Up @@ -696,8 +713,8 @@ protected virtual void HandleDragOver(sw.DragEventArgs e, DragEventArgs args)
}

protected virtual DragEventArgs GetDragEventArgs(sw.DragEventArgs data, object controlObject)
{
var dragData = (data.Data as sw.DataObject).ToEto();
{
var dragData = (data.Data as sw.DataObject).ToEto();

Control source = WpfFrameworkElement.DragSourceControl;

Expand All @@ -717,9 +734,9 @@ protected virtual DragEventArgs GetDragEventArgs(sw.DragEventArgs data, object c
if (data.KeyStates.HasFlag(sw.DragDropKeyStates.MiddleMouseButton))
buttons |= MouseButtons.Middle;
return new WpfDragEventArgs(source, dragData, data.AllowedEffects.ToEto(), location, modifiers, buttons, controlObject);
}
}

void HandleTextInput(object sender, swi.TextCompositionEventArgs e)
void HandleTextInput(object sender, swi.TextCompositionEventArgs e)
{
var tiargs = new TextInputEventArgs(e.Text);
Callback.OnTextInput(Widget, tiargs);
Expand Down Expand Up @@ -812,7 +829,7 @@ protected virtual void HandleMouseDown(object sender, swi.MouseButtonEventArgs e
}
}

protected override void Initialize()
protected override void Initialize()
{
base.Initialize();
Control.Tag = this;
Expand Down Expand Up @@ -934,7 +951,7 @@ public virtual int TabIndex
public virtual IEnumerable<Control> VisualControls => Enumerable.Empty<Control>();

public void DoDragDrop(DataObject data, DragEffects allowedAction, Image image, PointF offset)
{
{
WpfFrameworkElementHelper.ShouldCaptureMouse = false;
var dataObject = data.ToWpf();

Expand Down
Loading

0 comments on commit fc3d728

Please sign in to comment.