Window.cs 16 KB

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