Window.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Reactive.Linq;
  5. using System.Threading.Tasks;
  6. using Avalonia.Controls.Platform;
  7. using Avalonia.Input;
  8. using Avalonia.Layout;
  9. using Avalonia.Media;
  10. using Avalonia.Platform;
  11. using Avalonia.Styling;
  12. using System.Collections.Generic;
  13. using System.Linq;
  14. using JetBrains.Annotations;
  15. using System.ComponentModel;
  16. namespace Avalonia.Controls
  17. {
  18. /// <summary>
  19. /// Determines how a <see cref="Window"/> will size itself to fit its content.
  20. /// </summary>
  21. [Flags]
  22. public enum SizeToContent
  23. {
  24. /// <summary>
  25. /// The window will not automatically size itself to fit its content.
  26. /// </summary>
  27. Manual = 0,
  28. /// <summary>
  29. /// The window will size itself horizontally to fit its content.
  30. /// </summary>
  31. Width = 1,
  32. /// <summary>
  33. /// The window will size itself vertically to fit its content.
  34. /// </summary>
  35. Height = 2,
  36. /// <summary>
  37. /// The window will size itself horizontally and vertically to fit its content.
  38. /// </summary>
  39. WidthAndHeight = 3,
  40. }
  41. /// <summary>
  42. /// A top-level window.
  43. /// </summary>
  44. public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameScope
  45. {
  46. private static List<Window> s_windows = new List<Window>();
  47. /// <summary>
  48. /// Retrieves an enumeration of all Windows in the currently running application.
  49. /// </summary>
  50. public static IReadOnlyList<Window> OpenWindows => s_windows;
  51. /// <summary>
  52. /// Defines the <see cref="SizeToContent"/> property.
  53. /// </summary>
  54. public static readonly StyledProperty<SizeToContent> SizeToContentProperty =
  55. AvaloniaProperty.Register<Window, SizeToContent>(nameof(SizeToContent));
  56. /// <summary>
  57. /// Enables or disables system window decorations (title bar, buttons, etc)
  58. /// </summary>
  59. public static readonly StyledProperty<bool> HasSystemDecorationsProperty =
  60. AvaloniaProperty.Register<Window, bool>(nameof(HasSystemDecorations), true);
  61. /// <summary>
  62. /// Enables or disables the taskbar icon
  63. /// </summary>
  64. public static readonly StyledProperty<bool> ShowInTaskbarProperty =
  65. AvaloniaProperty.Register<Window, bool>(nameof(ShowInTaskbar), true);
  66. /// <summary>
  67. /// Enables or disables the taskbar icon
  68. /// </summary>
  69. public static readonly StyledProperty<WindowState> WindowStateProperty =
  70. AvaloniaProperty.Register<Window, WindowState>(nameof(WindowState));
  71. /// <summary>
  72. /// Defines the <see cref="Title"/> property.
  73. /// </summary>
  74. public static readonly StyledProperty<string> TitleProperty =
  75. AvaloniaProperty.Register<Window, string>(nameof(Title), "Window");
  76. /// <summary>
  77. /// Defines the <see cref="Icon"/> property.
  78. /// </summary>
  79. public static readonly StyledProperty<WindowIcon> IconProperty =
  80. AvaloniaProperty.Register<Window, WindowIcon>(nameof(Icon));
  81. /// <summary>
  82. /// Defines the <see cref="WindowStartupLocation"/> proeprty.
  83. /// </summary>
  84. public static readonly DirectProperty<Window, WindowStartupLocation> WindowStartupLocationProperty =
  85. AvaloniaProperty.RegisterDirect<Window, WindowStartupLocation>(
  86. nameof(WindowStartupLocation),
  87. o => o.WindowStartupLocation,
  88. (o, v) => o.WindowStartupLocation = v);
  89. public static readonly StyledProperty<bool> CanResizeProperty =
  90. AvaloniaProperty.Register<Window, bool>(nameof(CanResize), true);
  91. private readonly NameScope _nameScope = new NameScope();
  92. private object _dialogResult;
  93. private readonly Size _maxPlatformClientSize;
  94. private WindowStartupLocation _windowStartupLoction;
  95. /// <summary>
  96. /// Initializes static members of the <see cref="Window"/> class.
  97. /// </summary>
  98. static Window()
  99. {
  100. BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White);
  101. TitleProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetTitle((string)e.NewValue));
  102. HasSystemDecorationsProperty.Changed.AddClassHandler<Window>(
  103. (s, e) => s.PlatformImpl?.SetSystemDecorations((bool) e.NewValue));
  104. ShowInTaskbarProperty.Changed.AddClassHandler<Window>((w, e) => w.PlatformImpl?.ShowTaskbarIcon((bool)e.NewValue));
  105. IconProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl));
  106. CanResizeProperty.Changed.AddClassHandler<Window>((w, e) => w.PlatformImpl?.CanResize((bool)e.NewValue));
  107. WindowStateProperty.Changed.AddClassHandler<Window>(
  108. (w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.WindowState = (WindowState)e.NewValue; });
  109. }
  110. /// <summary>
  111. /// Initializes a new instance of the <see cref="Window"/> class.
  112. /// </summary>
  113. public Window()
  114. : this(PlatformManager.CreateWindow())
  115. {
  116. }
  117. /// <summary>
  118. /// Initializes a new instance of the <see cref="Window"/> class.
  119. /// </summary>
  120. /// <param name="impl">The window implementation.</param>
  121. public Window(IWindowImpl impl)
  122. : base(impl)
  123. {
  124. impl.Closing = HandleClosing;
  125. _maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size);
  126. Screens = new Screens(PlatformImpl?.Screen);
  127. if (PlatformImpl != null)
  128. PlatformImpl.WindowStateChanged = s => WindowState = s;
  129. }
  130. /// <inheritdoc/>
  131. event EventHandler<NameScopeEventArgs> INameScope.Registered
  132. {
  133. add { _nameScope.Registered += value; }
  134. remove { _nameScope.Registered -= value; }
  135. }
  136. /// <inheritdoc/>
  137. event EventHandler<NameScopeEventArgs> INameScope.Unregistered
  138. {
  139. add { _nameScope.Unregistered += value; }
  140. remove { _nameScope.Unregistered -= value; }
  141. }
  142. public Screens Screens { get; private set; }
  143. /// <summary>
  144. /// Gets the platform-specific window implementation.
  145. /// </summary>
  146. [CanBeNull]
  147. public new IWindowImpl PlatformImpl => (IWindowImpl)base.PlatformImpl;
  148. /// <summary>
  149. /// Gets or sets a value indicating how the window will size itself to fit its content.
  150. /// </summary>
  151. public SizeToContent SizeToContent
  152. {
  153. get { return GetValue(SizeToContentProperty); }
  154. set { SetValue(SizeToContentProperty, value); }
  155. }
  156. /// <summary>
  157. /// Gets or sets the title of the window.
  158. /// </summary>
  159. public string Title
  160. {
  161. get { return GetValue(TitleProperty); }
  162. set { SetValue(TitleProperty, value); }
  163. }
  164. /// <summary>
  165. /// Enables or disables system window decorations (title bar, buttons, etc)
  166. /// </summary>
  167. ///
  168. public bool HasSystemDecorations
  169. {
  170. get { return GetValue(HasSystemDecorationsProperty); }
  171. set { SetValue(HasSystemDecorationsProperty, value); }
  172. }
  173. /// <summary>
  174. /// Enables or disables the taskbar icon
  175. /// </summary>
  176. ///
  177. public bool ShowInTaskbar
  178. {
  179. get { return GetValue(ShowInTaskbarProperty); }
  180. set { SetValue(ShowInTaskbarProperty, value); }
  181. }
  182. /// <summary>
  183. /// Gets or sets the minimized/maximized state of the window.
  184. /// </summary>
  185. public WindowState WindowState
  186. {
  187. get { return GetValue(WindowStateProperty); }
  188. set { SetValue(WindowStateProperty, value); }
  189. }
  190. /// <summary>
  191. /// Enables or disables resizing of the window.
  192. /// Note that if <see cref="HasSystemDecorations"/> is set to False then this property
  193. /// has no effect and should be treated as a recommendation for the user setting HasSystemDecorations.
  194. /// </summary>
  195. public bool CanResize
  196. {
  197. get { return GetValue(CanResizeProperty); }
  198. set { SetValue(CanResizeProperty, value); }
  199. }
  200. /// <summary>
  201. /// Gets or sets the icon of the window.
  202. /// </summary>
  203. public WindowIcon Icon
  204. {
  205. get { return GetValue(IconProperty); }
  206. set { SetValue(IconProperty, value); }
  207. }
  208. /// <summary>
  209. /// Gets or sets the startup location of the window.
  210. /// </summary>
  211. public WindowStartupLocation WindowStartupLocation
  212. {
  213. get { return _windowStartupLoction; }
  214. set { SetAndRaise(WindowStartupLocationProperty, ref _windowStartupLoction, value); }
  215. }
  216. /// <inheritdoc/>
  217. Size ILayoutRoot.MaxClientSize => _maxPlatformClientSize;
  218. /// <inheritdoc/>
  219. Type IStyleable.StyleKey => typeof(Window);
  220. /// <summary>
  221. /// Fired before a window is closed.
  222. /// </summary>
  223. public event EventHandler<CancelEventArgs> Closing;
  224. /// <summary>
  225. /// Closes the window.
  226. /// </summary>
  227. public void Close()
  228. {
  229. Close(false);
  230. }
  231. protected override void HandleApplicationExiting()
  232. {
  233. base.HandleApplicationExiting();
  234. Close(true);
  235. }
  236. /// <summary>
  237. /// Closes a dialog window with the specified result.
  238. /// </summary>
  239. /// <param name="dialogResult">The dialog result.</param>
  240. /// <remarks>
  241. /// When the window is shown with the <see cref="ShowDialog{TResult}"/> method, the
  242. /// resulting task will produce the <see cref="_dialogResult"/> value when the window
  243. /// is closed.
  244. /// </remarks>
  245. public void Close(object dialogResult)
  246. {
  247. _dialogResult = dialogResult;
  248. Close(false);
  249. }
  250. internal void Close(bool ignoreCancel)
  251. {
  252. var cancelClosing = false;
  253. try
  254. {
  255. cancelClosing = HandleClosing();
  256. }
  257. finally
  258. {
  259. if (ignoreCancel || !cancelClosing)
  260. {
  261. s_windows.Remove(this);
  262. PlatformImpl?.Dispose();
  263. IsVisible = false;
  264. }
  265. }
  266. }
  267. /// <summary>
  268. /// Handles a closing notification from <see cref="IWindowImpl.Closing"/>.
  269. /// </summary>
  270. protected virtual bool HandleClosing()
  271. {
  272. var args = new CancelEventArgs();
  273. Closing?.Invoke(this, args);
  274. return args.Cancel;
  275. }
  276. /// <summary>
  277. /// Hides the window but does not close it.
  278. /// </summary>
  279. public override void Hide()
  280. {
  281. if (!IsVisible)
  282. {
  283. return;
  284. }
  285. using (BeginAutoSizing())
  286. {
  287. Renderer?.Stop();
  288. PlatformImpl?.Hide();
  289. }
  290. IsVisible = false;
  291. }
  292. /// <summary>
  293. /// Shows the window.
  294. /// </summary>
  295. public override void Show()
  296. {
  297. if (IsVisible)
  298. {
  299. return;
  300. }
  301. s_windows.Add(this);
  302. EnsureInitialized();
  303. SetWindowStartupLocation();
  304. IsVisible = true;
  305. LayoutManager.Instance.ExecuteInitialLayoutPass(this);
  306. using (BeginAutoSizing())
  307. {
  308. PlatformImpl?.Show();
  309. Renderer?.Start();
  310. }
  311. }
  312. /// <summary>
  313. /// Shows the window as a dialog.
  314. /// </summary>
  315. /// <returns>
  316. /// A task that can be used to track the lifetime of the dialog.
  317. /// </returns>
  318. public Task ShowDialog()
  319. {
  320. return ShowDialog<object>();
  321. }
  322. /// <summary>
  323. /// Shows the window as a dialog.
  324. /// </summary>
  325. /// <typeparam name="TResult">
  326. /// The type of the result produced by the dialog.
  327. /// </typeparam>
  328. /// <returns>.
  329. /// A task that can be used to retrive the result of the dialog when it closes.
  330. /// </returns>
  331. public Task<TResult> ShowDialog<TResult>()
  332. {
  333. if (IsVisible)
  334. {
  335. throw new InvalidOperationException("The window is already being shown.");
  336. }
  337. s_windows.Add(this);
  338. EnsureInitialized();
  339. SetWindowStartupLocation();
  340. IsVisible = true;
  341. LayoutManager.Instance.ExecuteInitialLayoutPass(this);
  342. using (BeginAutoSizing())
  343. {
  344. var affectedWindows = s_windows.Where(w => w.IsEnabled && w != this).ToList();
  345. var activated = affectedWindows.Where(w => w.IsActive).FirstOrDefault();
  346. SetIsEnabled(affectedWindows, false);
  347. var modal = PlatformImpl?.ShowDialog();
  348. var result = new TaskCompletionSource<TResult>();
  349. Renderer?.Start();
  350. Observable.FromEventPattern<EventHandler, EventArgs>(
  351. x => this.Closed += x,
  352. x => this.Closed -= x)
  353. .Take(1)
  354. .Subscribe(_ =>
  355. {
  356. modal?.Dispose();
  357. SetIsEnabled(affectedWindows, true);
  358. activated?.Activate();
  359. result.SetResult((TResult)(_dialogResult ?? default(TResult)));
  360. });
  361. return result.Task;
  362. }
  363. }
  364. void SetIsEnabled(IEnumerable<Window> windows, bool isEnabled)
  365. {
  366. foreach (var window in windows)
  367. {
  368. window.IsEnabled = isEnabled;
  369. }
  370. }
  371. void SetWindowStartupLocation()
  372. {
  373. if (WindowStartupLocation == WindowStartupLocation.CenterScreen)
  374. {
  375. var screen = Screens.ScreenFromPoint(Bounds.Position);
  376. if (screen != null)
  377. Position = screen.WorkingArea.CenterRect(new Rect(ClientSize)).Position;
  378. }
  379. else if (WindowStartupLocation == WindowStartupLocation.CenterOwner)
  380. {
  381. if (Owner != null)
  382. {
  383. var positionAsSize = Owner.ClientSize / 2 - ClientSize / 2;
  384. Position = Owner.Position + new Point(positionAsSize.Width, positionAsSize.Height);
  385. }
  386. }
  387. }
  388. /// <inheritdoc/>
  389. void INameScope.Register(string name, object element)
  390. {
  391. _nameScope.Register(name, element);
  392. }
  393. /// <inheritdoc/>
  394. object INameScope.Find(string name)
  395. {
  396. return _nameScope.Find(name);
  397. }
  398. /// <inheritdoc/>
  399. void INameScope.Unregister(string name)
  400. {
  401. _nameScope.Unregister(name);
  402. }
  403. /// <inheritdoc/>
  404. protected override Size MeasureOverride(Size availableSize)
  405. {
  406. var sizeToContent = SizeToContent;
  407. var clientSize = ClientSize;
  408. Size constraint = clientSize;
  409. if ((sizeToContent & SizeToContent.Width) != 0)
  410. {
  411. constraint = constraint.WithWidth(double.PositiveInfinity);
  412. }
  413. if ((sizeToContent & SizeToContent.Height) != 0)
  414. {
  415. constraint = constraint.WithHeight(double.PositiveInfinity);
  416. }
  417. var result = base.MeasureOverride(constraint);
  418. if ((sizeToContent & SizeToContent.Width) == 0)
  419. {
  420. result = result.WithWidth(clientSize.Width);
  421. }
  422. if ((sizeToContent & SizeToContent.Height) == 0)
  423. {
  424. result = result.WithHeight(clientSize.Height);
  425. }
  426. return result;
  427. }
  428. protected override void HandleClosed()
  429. {
  430. IsVisible = false;
  431. s_windows.Remove(this);
  432. base.HandleClosed();
  433. }
  434. /// <inheritdoc/>
  435. protected override void HandleResized(Size clientSize)
  436. {
  437. if (!AutoSizing)
  438. {
  439. SizeToContent = SizeToContent.Manual;
  440. }
  441. base.HandleResized(clientSize);
  442. }
  443. }
  444. }
  445. namespace Avalonia
  446. {
  447. public static class WindowApplicationExtensions
  448. {
  449. public static void RunWithMainWindow<TWindow>(this Application app) where TWindow : Avalonia.Controls.Window, new()
  450. {
  451. var window = new TWindow();
  452. window.Show();
  453. app.Run(window);
  454. }
  455. }
  456. }