Skip to content

Commit 9c39ea6

Browse files
committed
feat: PendingBox ShowAsync
Under development
1 parent b7143a8 commit 9c39ea6

File tree

10 files changed

+223
-20
lines changed

10 files changed

+223
-20
lines changed

src/Wpf.Ui.Test/MainWindow.xaml

+1
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,7 @@
976976
<vio:Loading />
977977
<Button Command="{Binding ShowPendingBoxCommand}" Content="Show PendingBox" />
978978
<Button Command="{Binding ShowPendingBoxWithCancelCommand}" Content="Show PendingBox with Cancel" />
979+
<Button Command="{Binding ShowAsyncPendingBoxCommand}" Content="Show PendingBoxAsync" />
979980
</ui:StackPanel>
980981
</StackPanel>
981982
</StackPanel>

src/Wpf.Ui.Test/MainWindow.xaml.cs

+9-1
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
using System.Collections.ObjectModel;
77
using System.IO;
88
using System.Linq;
9-
using System.Reactive.Disposables;
109
using System.Threading;
1110
using System.Threading.Tasks;
1211
using Wpf.Ui.Appearance;
1312
using Wpf.Ui.Controls;
1413
using Wpf.Ui.Violeta.Appearance;
1514
using Wpf.Ui.Violeta.Controls;
15+
using Wpf.Ui.Violeta.Threading;
1616
using ContentDialog = Wpf.Ui.Violeta.Controls.ContentDialog;
1717
using ContentDialogButton = Wpf.Ui.Violeta.Controls.ContentDialogButton;
1818

@@ -524,6 +524,14 @@ private async Task ShowPendingBoxWithCancelAsync()
524524
using IPendingHandler pending = PendingBox.Show("Doing something", "I'm a title", isShowCancel: true);
525525
await Task.Delay(3000);
526526
}
527+
528+
[Obsolete("Under development")]
529+
[RelayCommand]
530+
private async Task ShowAsyncPendingBoxAsync()
531+
{
532+
using STAThread<IPendingHandler> pending = PendingBox.ShowAsync();
533+
await Task.Delay(3000);
534+
}
527535
}
528536

529537
public partial class RegistryModel : ITreeModel

src/Wpf.Ui.Violeta/Controls/PendingBox/PendingBox.cs

+146-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
using System.Diagnostics.CodeAnalysis;
1+
using System;
2+
using System.Diagnostics.CodeAnalysis;
23
using System.Linq;
4+
using System.Runtime.InteropServices;
35
using System.Windows;
46
using System.Windows.Interop;
57
using System.Windows.Threading;
68
using Wpf.Ui.Violeta.Resources.Localization;
9+
using Wpf.Ui.Violeta.Threading;
710
using Wpf.Ui.Violeta.Win32;
811

912
namespace Wpf.Ui.Violeta.Controls;
@@ -45,7 +48,6 @@ private static IPendingHandler ShowCore(Window? owner, string? message, string?
4548
CloseOnCanceled = closeOnCanceled,
4649
};
4750

48-
dialog.Owner = owner;
4951
dialog.Closing += (_, e) =>
5052
{
5153
if (!e.Cancel)
@@ -70,4 +72,146 @@ private static IPendingHandler ShowCore(Window? owner, string? message, string?
7072
return pending;
7173
});
7274
}
75+
76+
[Obsolete("Under development")]
77+
public static STAThread<IPendingHandler> ShowAsync(string? message = null, string? title = null, bool isShowCancel = false, bool closeOnCanceled = true)
78+
{
79+
return ShowAsync(null, message, title, isShowCancel, closeOnCanceled);
80+
}
81+
82+
[Obsolete("Under development")]
83+
public static STAThread<IPendingHandler> ShowAsync(Window? owner, string? message, string? title = null, bool isShowCancel = false, bool closeOnCanceled = true)
84+
{
85+
owner
86+
??= Application.Current.Windows.OfType<Window>().FirstOrDefault(window => window.IsActive)
87+
?? Application.Current.MainWindow;
88+
89+
STAThread<IPendingHandler> sta = new(sta =>
90+
{
91+
sta.Result = ShowCoreAsync(owner, message, title, isShowCancel, closeOnCanceled);
92+
sta.Result.Show();
93+
});
94+
sta.Start();
95+
return sta;
96+
}
97+
98+
[SuppressMessage("Performance", "CA1859:Use concrete types when possible for improved performance")]
99+
[SuppressMessage("Style", "IDE0060:Remove unused parameter")]
100+
[SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression")]
101+
private static IPendingHandler ShowCoreAsync(Window? owner, string? message, string? title, bool isShowCancel, bool closeOnCanceled = true)
102+
{
103+
Point? center = null;
104+
105+
try
106+
{
107+
owner?.Dispatcher.Invoke(() =>
108+
{
109+
RECT rect = new();
110+
_ = User32.GetWindowRect(new WindowInteropHelper(owner).Handle, ref rect);
111+
POINT p = new(rect.Left + (int)((rect.Right - rect.Left) / 2d), rect.Top + (int)((rect.Bottom - rect.Top) / 2d));
112+
center = new(p.X, p.Y);
113+
});
114+
}
115+
catch
116+
{
117+
///
118+
}
119+
120+
PendingBoxDialog dialog = new()
121+
{
122+
Title = title ?? string.Empty,
123+
Message = message ?? (SH.Loading + "..."),
124+
IsShowCancel = isShowCancel,
125+
WindowStartupLocation = WindowStartupLocation.CenterScreen,
126+
};
127+
PendingHandlerAsync pending = new(dialog);
128+
129+
dialog.Closing += (_, e) =>
130+
{
131+
if (!e.Cancel)
132+
{
133+
_ = User32.SetForegroundWindow(new WindowInteropHelper(owner).Handle);
134+
}
135+
};
136+
dialog.Closed += (s, e) =>
137+
{
138+
pending.RaiseClosedEvent(s, e);
139+
pending.Close();
140+
};
141+
dialog.Canceled += (s, e) =>
142+
{
143+
pending.RaiseCanceledEvent(s, e);
144+
pending.Close();
145+
};
146+
147+
if (center != null)
148+
{
149+
dialog.Loaded += (_, _) =>
150+
{
151+
try
152+
{
153+
LayoutHelper.MoveWindowCenter(dialog, (Point)center);
154+
}
155+
catch
156+
{
157+
///
158+
}
159+
};
160+
}
161+
return pending;
162+
}
163+
}
164+
165+
file static class LayoutHelper
166+
{
167+
public static void MoveWindowCenter(Window window, Point center)
168+
{
169+
if (window == null)
170+
{
171+
return;
172+
}
173+
174+
nint hwnd = new WindowInteropHelper(window).Handle;
175+
176+
if (hwnd == IntPtr.Zero)
177+
{
178+
return;
179+
}
180+
181+
RECT rect = default;
182+
_ = User32.GetWindowRect(hwnd, ref rect);
183+
184+
nint monitorHandle = User32.MonitorFromWindow(hwnd, User32.MonitorDefaultTo.Nearest);
185+
User32.MONITORINFO monitorInfo = new()
186+
{
187+
cbSize = Marshal.SizeOf<User32.MONITORINFO>()
188+
};
189+
_ = User32.GetMonitorInfo(monitorHandle, ref monitorInfo);
190+
RECT screen = monitorInfo.rcMonitor;
191+
192+
(int x, int y) = ((int)center.X, (int)center.Y);
193+
(int w, int h) = (rect.Right - rect.Left, rect.Bottom - rect.Top);
194+
195+
(x, y) = ((int)(x - w / 2d), (int)(y - h / 2d));
196+
197+
if (x + w > screen.Right)
198+
{
199+
x = screen.Right - w;
200+
}
201+
else if (x - w < screen.Left)
202+
{
203+
x = screen.Left + w;
204+
}
205+
206+
if (y + h > screen.Bottom)
207+
{
208+
y = screen.Bottom - h;
209+
}
210+
else if (y - h < screen.Top)
211+
{
212+
y = screen.Top + h;
213+
}
214+
215+
_ = User32.MoveWindow(hwnd, x, y, w, h, false);
216+
}
73217
}

src/Wpf.Ui.Violeta/Controls/PendingBox/PendingBoxDialog.xaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<Setter Property="SizeToContent" Value="WidthAndHeight" />
2424
<Setter Property="ResizeMode" Value="NoResize" />
2525
<Setter Property="WindowStyle" Value="SingleBorderWindow" />
26-
<Setter Property="Title" Value="{Binding Message}" />
26+
<Setter Property="Title" Value="{Binding Title}" />
2727
<Setter Property="SnapsToDevicePixels" Value="True" />
2828
<Setter Property="ShowInTaskbar" Value="True" />
2929
<Setter Property="FontSize" Value="20" />

src/Wpf.Ui.Violeta/Controls/Primitives/PopupPositioner.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -845,10 +845,10 @@ internal static Rect ToRect(this RECT rc)
845845
{
846846
Rect rect = new()
847847
{
848-
X = rc.left,
849-
Y = rc.top,
850-
Width = rc.right - rc.left,
851-
Height = rc.bottom - rc.top
848+
X = rc.Left,
849+
Y = rc.Top,
850+
Width = rc.Right - rc.Left,
851+
Height = rc.Bottom - rc.Top
852852
};
853853

854854
return rect;
+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
using System.Diagnostics.CodeAnalysis;
22

33
[assembly: SuppressMessage("Style", "IDE0130:Namespace does not match folder structure")]
4+
[assembly: SuppressMessage("Interoperability", "SYSLIB1054:Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time")]
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.Runtime.InteropServices;
2+
3+
namespace Wpf.Ui.Violeta.Win32;
4+
5+
[StructLayout(LayoutKind.Sequential)]
6+
public struct POINT(int x, int y)
7+
{
8+
public int X = x;
9+
public int Y = y;
10+
11+
public void Offset(int dx, int dy)
12+
{
13+
X += dx;
14+
Y += dy;
15+
}
16+
}

src/Wpf.Ui.Violeta/Win32/Structs/RECT.cs

+12-12
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@ namespace Wpf.Ui.Violeta.Win32;
55
[StructLayout(LayoutKind.Sequential)]
66
public struct RECT(int left, int top, int right, int bottom)
77
{
8-
public int left = left;
9-
public int top = top;
10-
public int right = right;
11-
public int bottom = bottom;
8+
public int Left = left;
9+
public int Top = top;
10+
public int Right = right;
11+
public int Bottom = bottom;
1212

13-
public int Width => right - left;
13+
public int Width => Right - Left;
1414

15-
public int Height => bottom - top;
15+
public int Height => Bottom - Top;
1616

1717
public void Offset(int dx, int dy)
1818
{
19-
left += dx;
20-
top += dy;
21-
right += dx;
22-
bottom += dy;
19+
Left += dx;
20+
Top += dy;
21+
Right += dx;
22+
Bottom += dy;
2323
}
2424

25-
public bool IsEmpty => left >= right || top >= bottom;
26-
}
25+
public bool IsEmpty => Left >= Right || Top >= Bottom;
26+
}

src/Wpf.Ui.Violeta/Win32/User32.cs

+29
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,19 @@ internal static class User32
5050
[DllImport("user32.dll", CharSet = CharSet.Auto)]
5151
public static extern bool EnableWindow(nint hWnd, bool bEnable);
5252

53+
[DllImport("user32.dll", CharSet = CharSet.Auto)]
54+
public static extern bool MoveWindow(nint hWnd, int x, int y, int width, int height, bool repaint);
55+
56+
[DllImport("user32.dll")]
57+
[return: MarshalAs(UnmanagedType.Bool)]
58+
public static extern bool GetWindowRect(nint hwnd, ref RECT rect);
59+
60+
[DllImport("user32.dll")]
61+
public static extern nint MonitorFromWindow(nint hwnd, MonitorDefaultTo dwFlags);
62+
63+
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
64+
public static extern bool GetMonitorInfo(nint hMonitor, ref MONITORINFO lpmi);
65+
5366
[Flags]
5467
public enum DialogBoxCommand : uint
5568
{
@@ -85,4 +98,20 @@ internal enum SET_WINDOW_POS_FLAGS : uint
8598
SWP_NOZORDER = 0x00000004,
8699
SWP_SHOWWINDOW = 0x00000040,
87100
}
101+
102+
public enum MonitorDefaultTo : uint
103+
{
104+
Null = 0,
105+
Primary = 1,
106+
Nearest = 2,
107+
}
108+
109+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
110+
public struct MONITORINFO
111+
{
112+
public int cbSize;
113+
public RECT rcMonitor;
114+
public RECT rcWork;
115+
public uint dwFlags;
116+
}
88117
}

src/Wpf.Ui.Violeta/Wpf.Ui.Violeta.csproj

+4
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,8 @@
116116
</PackageReference>
117117
</ItemGroup>
118118

119+
<ItemGroup>
120+
<Folder Include="Win32\Enums\" />
121+
</ItemGroup>
122+
119123
</Project>

0 commit comments

Comments
 (0)