Window.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  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. namespace Avalonia.Controls
  16. {
  17. /// <summary>
  18. /// Determines how a <see cref="Window"/> will size itself to fit its content.
  19. /// </summary>
  20. public enum SizeToContent
  21. {
  22. /// <summary>
  23. /// The window will not automatically size itself to fit its content.
  24. /// </summary>
  25. Manual,
  26. /// <summary>
  27. /// The window will size itself horizontally to fit its content.
  28. /// </summary>
  29. Width,
  30. /// <summary>
  31. /// The window will size itself vertically to fit its content.
  32. /// </summary>
  33. Height,
  34. /// <summary>
  35. /// The window will size itself horizontally and vertically to fit its content.
  36. /// </summary>
  37. WidthAndHeight,
  38. }
  39. /// <summary>
  40. /// A top-level window.
  41. /// </summary>
  42. public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameScope
  43. {
  44. private static IList<Window> s_windows = new List<Window>();
  45. /// <summary>
  46. /// Retrieves an enumeration of all Windows in the currently running application.
  47. /// </summary>
  48. public static IList<Window> OpenWindows => s_windows;
  49. /// <summary>
  50. /// Defines the <see cref="SizeToContent"/> property.
  51. /// </summary>
  52. public static readonly StyledProperty<SizeToContent> SizeToContentProperty =
  53. AvaloniaProperty.Register<Window, SizeToContent>(nameof(SizeToContent));
  54. /// <summary>
  55. /// Enables of disables system window decorations (title bar, buttons, etc)
  56. /// </summary>
  57. public static readonly StyledProperty<bool> HasSystemDecorationsProperty =
  58. AvaloniaProperty.Register<Window, bool>(nameof(HasSystemDecorations), true);
  59. /// <summary>
  60. /// Defines the <see cref="Title"/> property.
  61. /// </summary>
  62. public static readonly StyledProperty<string> TitleProperty =
  63. AvaloniaProperty.Register<Window, string>(nameof(Title), "Window");
  64. /// <summary>
  65. /// Defines the <see cref="Icon"/> property.
  66. /// </summary>
  67. public static readonly StyledProperty<WindowIcon> IconProperty =
  68. AvaloniaProperty.Register<Window, WindowIcon>(nameof(Icon));
  69. private readonly NameScope _nameScope = new NameScope();
  70. private object _dialogResult;
  71. private readonly Size _maxPlatformClientSize;
  72. /// <summary>
  73. /// Initializes static members of the <see cref="Window"/> class.
  74. /// </summary>
  75. static Window()
  76. {
  77. BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White);
  78. TitleProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetTitle((string)e.NewValue));
  79. HasSystemDecorationsProperty.Changed.AddClassHandler<Window>(
  80. (s, e) => s.PlatformImpl?.SetSystemDecorations((bool) e.NewValue));
  81. IconProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl));
  82. }
  83. /// <summary>
  84. /// Initializes a new instance of the <see cref="Window"/> class.
  85. /// </summary>
  86. public Window()
  87. : this(PlatformManager.CreateWindow())
  88. {
  89. }
  90. /// <summary>
  91. /// Initializes a new instance of the <see cref="Window"/> class.
  92. /// </summary>
  93. /// <param name="impl">The window implementation.</param>
  94. public Window(IWindowImpl impl)
  95. : base(impl)
  96. {
  97. _maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size);
  98. }
  99. /// <inheritdoc/>
  100. event EventHandler<NameScopeEventArgs> INameScope.Registered
  101. {
  102. add { _nameScope.Registered += value; }
  103. remove { _nameScope.Registered -= value; }
  104. }
  105. /// <inheritdoc/>
  106. event EventHandler<NameScopeEventArgs> INameScope.Unregistered
  107. {
  108. add { _nameScope.Unregistered += value; }
  109. remove { _nameScope.Unregistered -= value; }
  110. }
  111. /// <summary>
  112. /// Gets the platform-specific window implementation.
  113. /// </summary>
  114. [CanBeNull]
  115. public new IWindowImpl PlatformImpl => (IWindowImpl)base.PlatformImpl;
  116. /// <summary>
  117. /// Gets or sets a value indicating how the window will size itself to fit its content.
  118. /// </summary>
  119. public SizeToContent SizeToContent
  120. {
  121. get { return GetValue(SizeToContentProperty); }
  122. set { SetValue(SizeToContentProperty, value); }
  123. }
  124. /// <summary>
  125. /// Gets or sets the title of the window.
  126. /// </summary>
  127. public string Title
  128. {
  129. get { return GetValue(TitleProperty); }
  130. set { SetValue(TitleProperty, value); }
  131. }
  132. /// <summary>
  133. /// Enables of disables system window decorations (title bar, buttons, etc)
  134. /// </summary>
  135. ///
  136. public bool HasSystemDecorations
  137. {
  138. get { return GetValue(HasSystemDecorationsProperty); }
  139. set { SetValue(HasSystemDecorationsProperty, value); }
  140. }
  141. /// <summary>
  142. /// Gets or sets the minimized/maximized state of the window.
  143. /// </summary>
  144. public WindowState WindowState
  145. {
  146. get { return PlatformImpl?.WindowState ?? WindowState.Normal; }
  147. set
  148. {
  149. if (PlatformImpl != null)
  150. PlatformImpl.WindowState = value;
  151. }
  152. }
  153. /// <summary>
  154. /// Gets or sets the icon of the window.
  155. /// </summary>
  156. public WindowIcon Icon
  157. {
  158. get { return GetValue(IconProperty); }
  159. set { SetValue(IconProperty, value); }
  160. }
  161. /// <inheritdoc/>
  162. Size ILayoutRoot.MaxClientSize => _maxPlatformClientSize;
  163. /// <inheritdoc/>
  164. Type IStyleable.StyleKey => typeof(Window);
  165. /// <summary>
  166. /// Closes the window.
  167. /// </summary>
  168. public void Close()
  169. {
  170. s_windows.Remove(this);
  171. PlatformImpl?.Dispose();
  172. IsVisible = false;
  173. }
  174. protected override void HandleApplicationExiting()
  175. {
  176. base.HandleApplicationExiting();
  177. Close();
  178. }
  179. /// <summary>
  180. /// Closes a dialog window with the specified result.
  181. /// </summary>
  182. /// <param name="dialogResult">The dialog result.</param>
  183. /// <remarks>
  184. /// When the window is shown with the <see cref="ShowDialog{TResult}"/> method, the
  185. /// resulting task will produce the <see cref="_dialogResult"/> value when the window
  186. /// is closed.
  187. /// </remarks>
  188. public void Close(object dialogResult)
  189. {
  190. _dialogResult = dialogResult;
  191. Close();
  192. }
  193. /// <summary>
  194. /// Hides the window but does not close it.
  195. /// </summary>
  196. public override void Hide()
  197. {
  198. using (BeginAutoSizing())
  199. {
  200. PlatformImpl?.Hide();
  201. }
  202. IsVisible = false;
  203. }
  204. /// <summary>
  205. /// Shows the window.
  206. /// </summary>
  207. public override void Show()
  208. {
  209. s_windows.Add(this);
  210. EnsureInitialized();
  211. IsVisible = true;
  212. LayoutManager.Instance.ExecuteInitialLayoutPass(this);
  213. using (BeginAutoSizing())
  214. {
  215. PlatformImpl?.Show();
  216. }
  217. }
  218. /// <summary>
  219. /// Shows the window as a dialog.
  220. /// </summary>
  221. /// <returns>
  222. /// A task that can be used to track the lifetime of the dialog.
  223. /// </returns>
  224. public Task ShowDialog()
  225. {
  226. return ShowDialog<object>();
  227. }
  228. /// <summary>
  229. /// Shows the window as a dialog.
  230. /// </summary>
  231. /// <typeparam name="TResult">
  232. /// The type of the result produced by the dialog.
  233. /// </typeparam>
  234. /// <returns>.
  235. /// A task that can be used to retrive the result of the dialog when it closes.
  236. /// </returns>
  237. public Task<TResult> ShowDialog<TResult>()
  238. {
  239. s_windows.Add(this);
  240. EnsureInitialized();
  241. IsVisible = true;
  242. LayoutManager.Instance.ExecuteInitialLayoutPass(this);
  243. using (BeginAutoSizing())
  244. {
  245. var affectedWindows = s_windows.Where(w => w.IsEnabled && w != this).ToList();
  246. var activated = affectedWindows.Where(w => w.IsActive).FirstOrDefault();
  247. SetIsEnabled(affectedWindows, false);
  248. var modal = PlatformImpl?.ShowDialog();
  249. var result = new TaskCompletionSource<TResult>();
  250. Observable.FromEventPattern<EventHandler, EventArgs>(
  251. x => this.Closed += x,
  252. x => this.Closed -= x)
  253. .Take(1)
  254. .Subscribe(_ =>
  255. {
  256. modal?.Dispose();
  257. SetIsEnabled(affectedWindows, true);
  258. activated?.Activate();
  259. result.SetResult((TResult)_dialogResult);
  260. });
  261. return result.Task;
  262. }
  263. }
  264. void SetIsEnabled(IEnumerable<Window> windows, bool isEnabled)
  265. {
  266. foreach (var window in windows)
  267. {
  268. window.IsEnabled = isEnabled;
  269. }
  270. }
  271. /// <inheritdoc/>
  272. void INameScope.Register(string name, object element)
  273. {
  274. _nameScope.Register(name, element);
  275. }
  276. /// <inheritdoc/>
  277. object INameScope.Find(string name)
  278. {
  279. return _nameScope.Find(name);
  280. }
  281. /// <inheritdoc/>
  282. void INameScope.Unregister(string name)
  283. {
  284. _nameScope.Unregister(name);
  285. }
  286. /// <inheritdoc/>
  287. protected override Size MeasureOverride(Size availableSize)
  288. {
  289. var sizeToContent = SizeToContent;
  290. var size = ClientSize;
  291. var desired = base.MeasureOverride(availableSize.Constrain(_maxPlatformClientSize));
  292. switch (sizeToContent)
  293. {
  294. case SizeToContent.Width:
  295. size = new Size(desired.Width, ClientSize.Height);
  296. break;
  297. case SizeToContent.Height:
  298. size = new Size(ClientSize.Width, desired.Height);
  299. break;
  300. case SizeToContent.WidthAndHeight:
  301. size = new Size(desired.Width, desired.Height);
  302. break;
  303. case SizeToContent.Manual:
  304. size = ClientSize;
  305. break;
  306. default:
  307. throw new InvalidOperationException("Invalid value for SizeToContent.");
  308. }
  309. return size;
  310. }
  311. protected override void HandleClosed()
  312. {
  313. IsVisible = false;
  314. base.HandleClosed();
  315. }
  316. /// <inheritdoc/>
  317. protected override void HandleResized(Size clientSize)
  318. {
  319. if (!AutoSizing)
  320. {
  321. SizeToContent = SizeToContent.Manual;
  322. }
  323. base.HandleResized(clientSize);
  324. }
  325. }
  326. }
  327. namespace Avalonia
  328. {
  329. public static class WindowApplicationExtensions
  330. {
  331. public static void RunWithMainWindow<TWindow>(this Application app) where TWindow : Avalonia.Controls.Window, new()
  332. {
  333. var window = new TWindow();
  334. window.Show();
  335. app.Run(window);
  336. }
  337. }
  338. }