Skip to content

Commit da22d17

Browse files
authored
Feature: Set focus to embedded window when application got focus (#1515)
* Feature: Set focus to embedded window when application got focus * Chore: Code cleanup * Feature: Set focus to embedded window when application got focus * Chore: Code cleanup * Feature: Set focus to embedded window when application got focus * Docs: Add #1515 to changelog * Fix: TextBox focus in MainWindow when ContextMenu is opened * Fix: Don't focus embedded window when ContextMenu is opened or TextBox is focused
1 parent c3dbc06 commit da22d17

19 files changed

+377
-115
lines changed

Source/NETworkManager.Utilities/NativeMethods.cs

+6
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ public enum WM : uint
4343
#endregion
4444

4545
#region Pinvoke/Win32 Methods
46+
[DllImport("user32.dll")]
47+
public static extern IntPtr GetForegroundWindow();
48+
4649
[DllImport("user32.dll", SetLastError = true)]
4750
public static extern long SetParent(IntPtr hWndChild, IntPtr hWndParent);
4851

@@ -72,6 +75,9 @@ public static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
7275

7376
[DllImport("user32.dll")]
7477
public static extern bool ShowWindow(IntPtr hWnd, WindowShowStyle nCmdShow);
78+
79+
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
80+
public static extern bool SetForegroundWindow(IntPtr hWnd);
7581
#endregion
7682
}
7783
}

Source/NETworkManager/Controls/DragablzTabHostWindow.xaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
1212
xmlns:wpfHelpers="clr-namespace:NETworkManager.Utilities.WPF;assembly=NETworkManager.Utilities.WPF"
1313
mc:Ignorable="d"
14-
Style="{DynamicResource DefaultWindow}" MinWidth="800" Width="1024" Height="768" MinHeight="600" TitleAlignment="Left"
14+
Style="{DynamicResource DefaultWindow}" MinWidth="800" Width="1024" Height="768" MinHeight="600" TitleAlignment="Left" Activated="MetroWindow_Activated"
1515
d:DataContext="{d:DesignInstance controls:DragablzTabHostWindow}">
1616
<mah:MetroWindow.WindowButtonCommands>
1717
<mah:WindowButtonCommands Template="{DynamicResource MahApps.Templates.WindowButtonCommands.Win10}" />

Source/NETworkManager/Controls/DragablzTabHostWindow.xaml.cs

+25
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,34 @@ private void WebConsole_RefreshAction(object view)
279279
#endregion
280280
#endregion
281281

282+
private async void FocusEmbeddedWindow()
283+
{
284+
// Delay the focus to prevent blocking the ui
285+
do
286+
{
287+
await Task.Delay(250);
288+
} while (Mouse.LeftButton == MouseButtonState.Pressed);
289+
290+
// Switch by name
291+
switch (ApplicationName)
292+
{
293+
case ApplicationName.PowerShell:
294+
((PowerShellControl)((DragablzTabItem)TabsContainer?.SelectedItem)?.View)?.FocusEmbeddedWindow();
295+
break;
296+
case ApplicationName.PuTTY:
297+
((PuTTYControl)((DragablzTabItem)TabsContainer?.SelectedItem)?.View)?.FocusEmbeddedWindow();
298+
break;
299+
}
300+
}
301+
282302
#region Events
283303
private void SettingsManager_PropertyChanged(object sender, PropertyChangedEventArgs e)
284304
{
305+
306+
}
307+
private void MetroWindow_Activated(object sender, EventArgs e)
308+
{
309+
FocusEmbeddedWindow();
285310
}
286311
#endregion
287312
}

Source/NETworkManager/Controls/PowerShellControl.xaml.cs

+12-16
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName
3737
private Process _process;
3838
private IntPtr _appWin;
3939

40-
private readonly DispatcherTimer _resizeTimer = new DispatcherTimer();
41-
4240
private bool _isConnected;
4341
public bool IsConnected
4442
{
@@ -78,9 +76,6 @@ public PowerShellControl(PowerShellSessionInfo info)
7876

7977
_sessionInfo = info;
8078

81-
_resizeTimer.Tick += ResizeTimer_Tick;
82-
_resizeTimer.Interval = new TimeSpan(0, 0, 0, 0, 500);
83-
8479
Dispatcher.ShutdownStarted += Dispatcher_ShutdownStarted;
8580
}
8681

@@ -157,7 +152,7 @@ private async Task Connect()
157152
if (IntPtr.Zero != _appWin)
158153
break;
159154

160-
await Task.Delay(50);
155+
await Task.Delay(100);
161156
}
162157
}
163158

@@ -175,7 +170,9 @@ private async Task Connect()
175170

176171
IsConnected = true;
177172

178-
// Resize embedded application & refresh
173+
// Resize embedded application & refresh
174+
// Requires a short delay because it's not applied immediately
175+
await Task.Delay(250);
179176
ResizeEmbeddedWindow();
180177
}
181178
}
@@ -209,6 +206,12 @@ private void Process_Exited(object sender, EventArgs e)
209206
IsConnected = false;
210207
}
211208

209+
public void FocusEmbeddedWindow()
210+
{
211+
if (IsConnected)
212+
NativeMethods.SetForegroundWindow(_process.MainWindowHandle);
213+
}
214+
212215
public void ResizeEmbeddedWindow()
213216
{
214217
if (IsConnected)
@@ -217,7 +220,7 @@ public void ResizeEmbeddedWindow()
217220

218221
public void Disconnect()
219222
{
220-
if (_process != null && !_process.HasExited)
223+
if (IsConnected)
221224
_process.Kill();
222225
}
223226

@@ -240,16 +243,9 @@ public void CloseTab()
240243
#region Events
241244
private void WindowGrid_SizeChanged(object sender, SizeChangedEventArgs e)
242245
{
243-
if (_process != null)
246+
if (IsConnected)
244247
ResizeEmbeddedWindow();
245248
}
246-
247-
private void ResizeTimer_Tick(object sender, EventArgs e)
248-
{
249-
_resizeTimer.Stop();
250-
251-
ResizeEmbeddedWindow();
252-
}
253249
#endregion
254250
}
255251
}

Source/NETworkManager/Controls/PuTTYControl.xaml.cs

+12-18
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName
3838
private Process _process;
3939
private IntPtr _appWin;
4040

41-
private readonly DispatcherTimer _resizeTimer = new DispatcherTimer();
42-
4341
private bool _isConnected;
4442
public bool IsConnected
4543
{
@@ -79,9 +77,6 @@ public PuTTYControl(PuTTYSessionInfo info)
7977

8078
_sessionInfo = info;
8179

82-
_resizeTimer.Tick += ResizeTimer_Tick;
83-
_resizeTimer.Interval = new TimeSpan(0, 0, 0, 0, 500);
84-
8580
Dispatcher.ShutdownStarted += Dispatcher_ShutdownStarted;
8681
}
8782

@@ -160,15 +155,15 @@ private async Task Connect()
160155
if (IntPtr.Zero != _appWin)
161156
break;
162157

163-
await Task.Delay(50);
158+
await Task.Delay(100);
164159
}
165160
}
166161

167162
if (_appWin != IntPtr.Zero)
168163
{
169164
while (!_process.HasExited && _process.MainWindowTitle.IndexOf(" - PuTTY", StringComparison.Ordinal) == -1)
170165
{
171-
await Task.Delay(50);
166+
await Task.Delay(100);
172167

173168
_process.Refresh();
174169
}
@@ -188,8 +183,8 @@ private async Task Connect()
188183
IsConnected = true;
189184

190185
// Resize embedded application & refresh
191-
// Requires a short delay because it'S not applied immediately
192-
await Task.Delay(500);
186+
// Requires a short delay because it's not applied immediately
187+
await Task.Delay(250);
193188

194189
ResizeEmbeddedWindow();
195190
}
@@ -225,6 +220,12 @@ private void Process_Exited(object sender, EventArgs e)
225220
IsConnected = false;
226221
}
227222

223+
public void FocusEmbeddedWindow()
224+
{
225+
if (IsConnected)
226+
NativeMethods.SetForegroundWindow(_process.MainWindowHandle);
227+
}
228+
228229
public void ResizeEmbeddedWindow()
229230
{
230231
if (IsConnected)
@@ -233,7 +234,7 @@ public void ResizeEmbeddedWindow()
233234

234235
public void Disconnect()
235236
{
236-
if (_process != null && !_process.HasExited)
237+
if (IsConnected)
237238
_process.Kill();
238239
}
239240

@@ -262,16 +263,9 @@ public void CloseTab()
262263
#region Events
263264
private void WindowGrid_SizeChanged(object sender, SizeChangedEventArgs e)
264265
{
265-
if (_process != null)
266+
if (IsConnected)
266267
ResizeEmbeddedWindow();
267268
}
268-
269-
private void ResizeTimer_Tick(object sender, EventArgs e)
270-
{
271-
_resizeTimer.Stop();
272-
273-
ResizeEmbeddedWindow();
274-
}
275269
#endregion
276270
}
277271
}

Source/NETworkManager/Controls/RemoteDesktopControl.xaml.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ private async Task InitiateReconnection()
456456

457457
do // Prevent to many requests
458458
{
459-
await Task.Delay(500);
459+
await Task.Delay(250);
460460

461461
} while (Mouse.LeftButton == MouseButtonState.Pressed);
462462

Source/NETworkManager/Controls/TightVNCControl.xaml.cs

+9-31
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName
3737
private Process _process;
3838
private IntPtr _appWin;
3939

40-
private readonly DispatcherTimer _resizeTimer = new DispatcherTimer();
41-
4240
private bool _isConnected;
4341
public bool IsConnected
4442
{
@@ -78,9 +76,6 @@ public TigerVNCControl(TigerVNCSessionInfo info)
7876

7977
_sessionInfo = info;
8078

81-
_resizeTimer.Tick += ResizeTimer_Tick;
82-
_resizeTimer.Interval = new TimeSpan(0, 0, 0, 0, 500);
83-
8479
Dispatcher.ShutdownStarted += Dispatcher_ShutdownStarted;
8580
}
8681

@@ -145,18 +140,6 @@ private async Task Connect()
145140

146141
while ((DateTime.Now - startTime).TotalSeconds < 10)
147142
{
148-
// Fix for netcore3.1 https://stackoverflow.com/questions/60342879/process-mainwindowhandle-is-non-zero-in-net-framework-but-zero-in-net-core-unl
149-
/*
150-
try
151-
{
152-
_process = Process.GetProcessById(_process.Id);
153-
}
154-
catch
155-
{
156-
break; // Process has exited
157-
}
158-
*/
159-
160143
_process.Refresh();
161144

162145
if (_process.HasExited)
@@ -167,15 +150,15 @@ private async Task Connect()
167150
if (IntPtr.Zero != _appWin)
168151
break;
169152

170-
await Task.Delay(50);
153+
await Task.Delay(100);
171154
}
172155
}
173156

174157
if (_appWin != IntPtr.Zero)
175158
{
176159
while (!_process.HasExited && _process.MainWindowTitle.IndexOf(" - TigerVNC", StringComparison.Ordinal) == -1)
177160
{
178-
await Task.Delay(50);
161+
await Task.Delay(100);
179162

180163
_process.Refresh();
181164
}
@@ -197,7 +180,9 @@ private async Task Connect()
197180

198181
IsConnected = true;
199182

200-
// Resize embedded application & refresh
183+
// Resize embedded application & refresh
184+
// Requires a short delay because it's not applied immediately
185+
await Task.Delay(250);
201186
ResizeEmbeddedWindow();
202187
}
203188
}
@@ -212,10 +197,10 @@ private async Task Connect()
212197
if (!_closing)
213198
{
214199
var settings = AppearanceManager.MetroDialog;
215-
settings.AffirmativeButtonText = NETworkManager.Localization.Resources.Strings.OK;
200+
settings.AffirmativeButtonText = Localization.Resources.Strings.OK;
216201
ConfigurationManager.Current.FixAirspace = true;
217202

218-
await _dialogCoordinator.ShowMessageAsync(this, NETworkManager.Localization.Resources.Strings.Error,
203+
await _dialogCoordinator.ShowMessageAsync(this, Localization.Resources.Strings.Error,
219204
ex.Message, MessageDialogStyle.Affirmative, settings);
220205

221206
ConfigurationManager.Current.FixAirspace = false;
@@ -239,7 +224,7 @@ private void ResizeEmbeddedWindow()
239224

240225
public void Disconnect()
241226
{
242-
if (_process != null && !_process.HasExited)
227+
if (IsConnected)
243228
_process.Kill();
244229
}
245230

@@ -262,16 +247,9 @@ public void CloseTab()
262247
#region Events
263248
private void TigerVNCGrid_SizeChanged(object sender, SizeChangedEventArgs e)
264249
{
265-
if (_process != null)
250+
if (IsConnected)
266251
ResizeEmbeddedWindow();
267252
}
268-
269-
private void ResizeTimer_Tick(object sender, EventArgs e)
270-
{
271-
_resizeTimer.Stop();
272-
273-
ResizeEmbeddedWindow();
274-
}
275253
#endregion
276254
}
277255
}

Source/NETworkManager/MainWindow.xaml

+6-6
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
xmlns:resources="clr-namespace:NETworkManager.Properties"
1313
xmlns:networkManager="clr-namespace:NETworkManager"
1414
mc:Ignorable="d"
15-
Style="{DynamicResource DefaultWindow}" MinWidth="800" Width="1024" Height="768" MinHeight="600" SaveWindowPosition="True" TitleAlignment="Left" Closing="MetroWindowMain_Closing" StateChanged="MetroWindowMain_StateChanged"
15+
Style="{DynamicResource DefaultWindow}" MinWidth="800" Width="1024" Height="768" MinHeight="600" Activated="MetroMainWindow_Activated" SaveWindowPosition="True" TitleAlignment="Left" Closing="MetroWindowMain_Closing" StateChanged="MetroWindowMain_StateChanged"
1616
d:DataContext="{d:DesignInstance networkManager:MainWindow}">
1717
<!-- MetroDialogStyles.xaml must be adjusted if MinWidth/MinHeight is changed -->
1818
<Window.Resources>
@@ -103,7 +103,7 @@
103103
<TextBlock Text="{x:Static localization:Strings.UnlockProfile}" Style="{StaticResource LinkTextBlock}" Margin="5,0" />
104104
</StackPanel>
105105
</Button>
106-
<ComboBox ItemsSource="{Binding ProfileFiles}" SelectedItem="{Binding SelectedProfileFile, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Background="{DynamicResource MahApps.Brushes.Gray8}" BorderThickness="0" MinWidth="100" HorizontalAlignment="Left" Margin="0,0,10,0">
106+
<ComboBox ItemsSource="{Binding ProfileFiles}" SelectedItem="{Binding SelectedProfileFile, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Background="{DynamicResource MahApps.Brushes.Gray8}" IsDropDownOpen="{Binding IsProfileFileDropDownOpened}" BorderThickness="0" MinWidth="100" HorizontalAlignment="Left" Margin="0,0,10,0">
107107
<ComboBox.ItemTemplate>
108108
<DataTemplate>
109109
<Grid>
@@ -309,11 +309,11 @@
309309
</Style>
310310
</TextBox.Style>
311311
<interactivity:Interaction.Triggers>
312-
<interactivity:EventTrigger EventName="GotKeyboardFocus">
313-
<interactivity:InvokeCommandAction Command="{Binding TextBoxSearchGotKeyboardFocusCommand}" />
312+
<interactivity:EventTrigger EventName="GotFocus">
313+
<interactivity:InvokeCommandAction Command="{Binding TextBoxSearchGotFocusCommand}" />
314314
</interactivity:EventTrigger>
315-
<interactivity:EventTrigger EventName="LostKeyboardFocus">
316-
<interactivity:InvokeCommandAction Command="{Binding TextBoxSearchLostKeyboardFocusCommand}" />
315+
<interactivity:EventTrigger EventName="LostFocus">
316+
<interactivity:InvokeCommandAction Command="{Binding TextBoxSearchLostFocusCommand}" />
317317
</interactivity:EventTrigger>
318318
</interactivity:Interaction.Triggers>
319319
</TextBox>

0 commit comments

Comments
 (0)