App.xaml.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. using MediaBrowser.ApiInteraction;
  2. using MediaBrowser.Common.Extensions;
  3. using MediaBrowser.Common.IO;
  4. using MediaBrowser.Common.Kernel;
  5. using MediaBrowser.Common.Logging;
  6. using MediaBrowser.Common.UI;
  7. using MediaBrowser.IsoMounter;
  8. using MediaBrowser.Model.Configuration;
  9. using MediaBrowser.Model.Dto;
  10. using MediaBrowser.Model.Net;
  11. using MediaBrowser.Model.Weather;
  12. using MediaBrowser.UI.Controller;
  13. using MediaBrowser.UI.Controls;
  14. using MediaBrowser.UI.Pages;
  15. using MediaBrowser.UI.Uninstall;
  16. using System;
  17. using System.Collections.Generic;
  18. using System.ComponentModel;
  19. using System.IO;
  20. using System.Linq;
  21. using System.Threading;
  22. using System.Threading.Tasks;
  23. using System.Windows;
  24. using System.Windows.Controls;
  25. using System.Windows.Media.Imaging;
  26. namespace MediaBrowser.UI
  27. {
  28. /// <summary>
  29. /// Interaction logic for App.xaml
  30. /// </summary>
  31. public partial class App : BaseApplication, IApplication
  32. {
  33. /// <summary>
  34. /// Gets or sets the clock timer.
  35. /// </summary>
  36. /// <value>The clock timer.</value>
  37. private Timer ClockTimer { get; set; }
  38. /// <summary>
  39. /// Gets or sets the server configuration timer.
  40. /// </summary>
  41. /// <value>The server configuration timer.</value>
  42. private Timer ServerConfigurationTimer { get; set; }
  43. /// <summary>
  44. /// Gets the name of the product.
  45. /// </summary>
  46. /// <value>The name of the product.</value>
  47. protected override string ProductName
  48. {
  49. get { return Globals.ProductName; }
  50. }
  51. /// <summary>
  52. /// Gets the name of the publisher.
  53. /// </summary>
  54. /// <value>The name of the publisher.</value>
  55. protected override string PublisherName
  56. {
  57. get { return Globals.PublisherName; }
  58. }
  59. /// <summary>
  60. /// Gets the name of the suite.
  61. /// </summary>
  62. /// <value>The name of the suite.</value>
  63. protected override string SuiteName
  64. {
  65. get { return Globals.SuiteName; }
  66. }
  67. /// <summary>
  68. /// Gets the name of the uninstaller file.
  69. /// </summary>
  70. /// <value>The name of the uninstaller file.</value>
  71. protected override string UninstallerFileName
  72. {
  73. get { return "MediaBrowser.UI.Uninstall.exe"; }
  74. }
  75. /// <summary>
  76. /// Gets the instance.
  77. /// </summary>
  78. /// <value>The instance.</value>
  79. public static App Instance
  80. {
  81. get
  82. {
  83. return Current as App;
  84. }
  85. }
  86. /// <summary>
  87. /// Gets the API client.
  88. /// </summary>
  89. /// <value>The API client.</value>
  90. public ApiClient ApiClient
  91. {
  92. get { return UIKernel.Instance.ApiClient; }
  93. }
  94. /// <summary>
  95. /// Gets the application window.
  96. /// </summary>
  97. /// <value>The application window.</value>
  98. public MainWindow ApplicationWindow { get; private set; }
  99. /// <summary>
  100. /// Gets the hidden window.
  101. /// </summary>
  102. /// <value>The hidden window.</value>
  103. public HiddenWindow HiddenWindow { get; private set; }
  104. /// <summary>
  105. /// The _current user
  106. /// </summary>
  107. private UserDto _currentUser;
  108. /// <summary>
  109. /// Gets or sets the current user.
  110. /// </summary>
  111. /// <value>The current user.</value>
  112. public UserDto CurrentUser
  113. {
  114. get
  115. {
  116. return _currentUser;
  117. }
  118. set
  119. {
  120. _currentUser = value;
  121. if (UIKernel.Instance.ApiClient != null)
  122. {
  123. if (value == null)
  124. {
  125. UIKernel.Instance.ApiClient.CurrentUserId = null;
  126. }
  127. else
  128. {
  129. UIKernel.Instance.ApiClient.CurrentUserId = value.Id;
  130. }
  131. }
  132. OnPropertyChanged("CurrentUser");
  133. }
  134. }
  135. /// <summary>
  136. /// The _server configuration
  137. /// </summary>
  138. private ServerConfiguration _serverConfiguration;
  139. /// <summary>
  140. /// Gets or sets the server configuration.
  141. /// </summary>
  142. /// <value>The server configuration.</value>
  143. public ServerConfiguration ServerConfiguration
  144. {
  145. get
  146. {
  147. return _serverConfiguration;
  148. }
  149. set
  150. {
  151. _serverConfiguration = value;
  152. OnPropertyChanged("ServerConfiguration");
  153. }
  154. }
  155. /// <summary>
  156. /// The _current time
  157. /// </summary>
  158. private DateTime _currentTime = DateTime.Now;
  159. /// <summary>
  160. /// Gets the current time.
  161. /// </summary>
  162. /// <value>The current time.</value>
  163. public DateTime CurrentTime
  164. {
  165. get
  166. {
  167. return _currentTime;
  168. }
  169. private set
  170. {
  171. _currentTime = value;
  172. OnPropertyChanged("CurrentTime");
  173. }
  174. }
  175. /// <summary>
  176. /// The _current weather
  177. /// </summary>
  178. private WeatherInfo _currentWeather;
  179. /// <summary>
  180. /// Gets the current weather.
  181. /// </summary>
  182. /// <value>The current weather.</value>
  183. public WeatherInfo CurrentWeather
  184. {
  185. get
  186. {
  187. return _currentWeather;
  188. }
  189. private set
  190. {
  191. _currentWeather = value;
  192. OnPropertyChanged("CurrentWeather");
  193. }
  194. }
  195. /// <summary>
  196. /// The _current theme
  197. /// </summary>
  198. private BaseTheme _currentTheme;
  199. /// <summary>
  200. /// Gets the current theme.
  201. /// </summary>
  202. /// <value>The current theme.</value>
  203. public BaseTheme CurrentTheme
  204. {
  205. get
  206. {
  207. return _currentTheme;
  208. }
  209. private set
  210. {
  211. _currentTheme = value;
  212. OnPropertyChanged("CurrentTheme");
  213. }
  214. }
  215. /// <summary>
  216. /// Defines the entry point of the application.
  217. /// </summary>
  218. [STAThread]
  219. public static void Main()
  220. {
  221. RunApplication<App>("MediaBrowserUI");
  222. }
  223. /// <summary>
  224. /// Instantiates the kernel.
  225. /// </summary>
  226. /// <returns>IKernel.</returns>
  227. protected override IKernel InstantiateKernel()
  228. {
  229. return new UIKernel(new PismoIsoManager(LogManager.GetLogger("PismoIsoManager")));
  230. }
  231. /// <summary>
  232. /// Instantiates the main window.
  233. /// </summary>
  234. /// <returns>Window.</returns>
  235. protected override Window InstantiateMainWindow()
  236. {
  237. HiddenWindow = new HiddenWindow { };
  238. return HiddenWindow;
  239. }
  240. /// <summary>
  241. /// Shows the application window.
  242. /// </summary>
  243. private void ShowApplicationWindow()
  244. {
  245. var win = new MainWindow { };
  246. var config = UIKernel.Instance.Configuration;
  247. // Restore window position/size
  248. if (config.WindowState.HasValue)
  249. {
  250. // Set window state
  251. win.WindowState = config.WindowState.Value;
  252. // Set position if not maximized
  253. if (config.WindowState.Value != WindowState.Maximized)
  254. {
  255. double left = 0;
  256. double top = 0;
  257. // Set left
  258. if (config.WindowLeft.HasValue)
  259. {
  260. win.WindowStartupLocation = WindowStartupLocation.Manual;
  261. win.Left = left = Math.Max(config.WindowLeft.Value, 0);
  262. }
  263. // Set top
  264. if (config.WindowTop.HasValue)
  265. {
  266. win.WindowStartupLocation = WindowStartupLocation.Manual;
  267. win.Top = top = Math.Max(config.WindowTop.Value, 0);
  268. }
  269. // Set width
  270. if (config.WindowWidth.HasValue)
  271. {
  272. win.Width = Math.Min(config.WindowWidth.Value, SystemParameters.VirtualScreenWidth - left);
  273. }
  274. // Set height
  275. if (config.WindowHeight.HasValue)
  276. {
  277. win.Height = Math.Min(config.WindowHeight.Value, SystemParameters.VirtualScreenHeight - top);
  278. }
  279. }
  280. }
  281. win.LocationChanged += ApplicationWindow_LocationChanged;
  282. win.StateChanged += ApplicationWindow_LocationChanged;
  283. win.SizeChanged += ApplicationWindow_LocationChanged;
  284. ApplicationWindow = win;
  285. ApplicationWindow.Show();
  286. ApplicationWindow.Owner = HiddenWindow;
  287. SyncHiddenWindowLocation();
  288. }
  289. /// <summary>
  290. /// Handles the LocationChanged event of the ApplicationWindow control.
  291. /// </summary>
  292. /// <param name="sender">The source of the event.</param>
  293. /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
  294. void ApplicationWindow_LocationChanged(object sender, EventArgs e)
  295. {
  296. SyncHiddenWindowLocation();
  297. }
  298. /// <summary>
  299. /// Syncs the hidden window location.
  300. /// </summary>
  301. public void SyncHiddenWindowLocation()
  302. {
  303. HiddenWindow.Width = ApplicationWindow.Width;
  304. HiddenWindow.Height = ApplicationWindow.Height;
  305. HiddenWindow.Top = ApplicationWindow.Top;
  306. HiddenWindow.Left = ApplicationWindow.Left;
  307. HiddenWindow.WindowState = ApplicationWindow.WindowState;
  308. ApplicationWindow.Activate();
  309. }
  310. /// <summary>
  311. /// Loads the kernel.
  312. /// </summary>
  313. protected override async void LoadKernel()
  314. {
  315. // Without this the app will shutdown after the splash screen closes
  316. ShutdownMode = ShutdownMode.OnExplicitShutdown;
  317. Kernel = InstantiateKernel();
  318. try
  319. {
  320. var now = DateTime.UtcNow;
  321. await Kernel.Init();
  322. Logger.Info("Kernel.Init completed in {0} seconds.", (DateTime.UtcNow - now).TotalSeconds);
  323. ShutdownMode = System.Windows.ShutdownMode.OnLastWindowClose;
  324. await OnKernelLoaded();
  325. InstantiateMainWindow().Show();
  326. ShowApplicationWindow();
  327. await ApplicationWindow.LoadInitialUI().ConfigureAwait(false);
  328. }
  329. catch (Exception ex)
  330. {
  331. Logger.ErrorException("Error launching application", ex);
  332. MessageBox.Show("There was an error launching Media Browser: " + ex.Message);
  333. // Shutdown the app with an error code
  334. Shutdown(1);
  335. }
  336. }
  337. /// <summary>
  338. /// Called when [kernel loaded].
  339. /// </summary>
  340. /// <returns>Task.</returns>
  341. protected override async Task OnKernelLoaded()
  342. {
  343. await base.OnKernelLoaded().ConfigureAwait(false);
  344. PropertyChanged += AppPropertyChanged;
  345. // Update every 10 seconds
  346. ClockTimer = new Timer(ClockTimerCallback, null, 0, 10000);
  347. // Update every 30 minutes
  348. ServerConfigurationTimer = new Timer(ServerConfigurationTimerCallback, null, 0, 1800000);
  349. CurrentTheme = UIKernel.Instance.Themes.First();
  350. foreach (var resource in CurrentTheme.GetGlobalResources())
  351. {
  352. Resources.MergedDictionaries.Add(resource);
  353. }
  354. }
  355. /// <summary>
  356. /// Raises the <see cref="E:System.Windows.Application.Exit" /> event.
  357. /// </summary>
  358. /// <param name="e">An <see cref="T:System.Windows.ExitEventArgs" /> that contains the event data.</param>
  359. protected override void OnExit(ExitEventArgs e)
  360. {
  361. var win = ApplicationWindow;
  362. if (win != null)
  363. {
  364. // Save window position
  365. var config = UIKernel.Instance.Configuration;
  366. config.WindowState = win.WindowState;
  367. config.WindowTop = win.Top;
  368. config.WindowLeft = win.Left;
  369. config.WindowWidth = win.Width;
  370. config.WindowHeight = win.Height;
  371. UIKernel.Instance.SaveConfiguration();
  372. }
  373. base.OnExit(e);
  374. }
  375. /// <summary>
  376. /// Apps the property changed.
  377. /// </summary>
  378. /// <param name="sender">The sender.</param>
  379. /// <param name="e">The <see cref="PropertyChangedEventArgs" /> instance containing the event data.</param>
  380. async void AppPropertyChanged(object sender, PropertyChangedEventArgs e)
  381. {
  382. if (e.PropertyName.Equals("ServerConfiguration"))
  383. {
  384. if (string.IsNullOrEmpty(ServerConfiguration.WeatherLocation))
  385. {
  386. CurrentWeather = null;
  387. }
  388. else
  389. {
  390. try
  391. {
  392. CurrentWeather = await ApiClient.GetWeatherInfoAsync(ServerConfiguration.WeatherLocation);
  393. }
  394. catch (HttpException ex)
  395. {
  396. Logger.ErrorException("Error downloading weather information", ex);
  397. }
  398. }
  399. }
  400. }
  401. /// <summary>
  402. /// Clocks the timer callback.
  403. /// </summary>
  404. /// <param name="stateInfo">The state info.</param>
  405. private void ClockTimerCallback(object stateInfo)
  406. {
  407. CurrentTime = DateTime.Now;
  408. }
  409. /// <summary>
  410. /// Servers the configuration timer callback.
  411. /// </summary>
  412. /// <param name="stateInfo">The state info.</param>
  413. private async void ServerConfigurationTimerCallback(object stateInfo)
  414. {
  415. try
  416. {
  417. ServerConfiguration = await ApiClient.GetServerConfigurationAsync();
  418. }
  419. catch (HttpException ex)
  420. {
  421. Logger.ErrorException("Error refreshing server configuration", ex);
  422. }
  423. }
  424. /// <summary>
  425. /// Logouts the user.
  426. /// </summary>
  427. /// <returns>Task.</returns>
  428. public async Task LogoutUser()
  429. {
  430. CurrentUser = null;
  431. await Dispatcher.InvokeAsync(() => Navigate(CurrentTheme.GetLoginPage()));
  432. }
  433. /// <summary>
  434. /// Navigates the specified page.
  435. /// </summary>
  436. /// <param name="page">The page.</param>
  437. public void Navigate(Page page)
  438. {
  439. _remoteImageCache = new FileSystemRepository(UIKernel.Instance.ApplicationPaths.RemoteImageCachePath);
  440. ApplicationWindow.Navigate(page);
  441. }
  442. /// <summary>
  443. /// Navigates to settings page.
  444. /// </summary>
  445. public void NavigateToSettingsPage()
  446. {
  447. Navigate(new SettingsPage());
  448. }
  449. /// <summary>
  450. /// Navigates to internal player page.
  451. /// </summary>
  452. public void NavigateToInternalPlayerPage()
  453. {
  454. Navigate(CurrentTheme.GetInternalPlayerPage());
  455. }
  456. /// <summary>
  457. /// Navigates to image viewer.
  458. /// </summary>
  459. /// <param name="imageUrl">The image URL.</param>
  460. /// <param name="caption">The caption.</param>
  461. public void OpenImageViewer(Uri imageUrl, string caption)
  462. {
  463. var tuple = new Tuple<Uri, string>(imageUrl, caption);
  464. OpenImageViewer(new[] { tuple });
  465. }
  466. /// <summary>
  467. /// Navigates to image viewer.
  468. /// </summary>
  469. /// <param name="images">The images.</param>
  470. public void OpenImageViewer(IEnumerable<Tuple<Uri, string>> images)
  471. {
  472. new ImageViewerWindow(images).ShowModal(ApplicationWindow);
  473. }
  474. /// <summary>
  475. /// Navigates to item.
  476. /// </summary>
  477. /// <param name="item">The item.</param>
  478. public void NavigateToItem(BaseItemDto item)
  479. {
  480. if (item.IsRoot.HasValue && item.IsRoot.Value)
  481. {
  482. NavigateToHomePage();
  483. }
  484. else if (item.IsFolder)
  485. {
  486. Navigate(CurrentTheme.GetListPage(item));
  487. }
  488. else
  489. {
  490. Navigate(CurrentTheme.GetDetailPage(item));
  491. }
  492. }
  493. /// <summary>
  494. /// Displays the weather.
  495. /// </summary>
  496. public void DisplayWeather()
  497. {
  498. CurrentTheme.DisplayWeather();
  499. }
  500. /// <summary>
  501. /// Navigates to home page.
  502. /// </summary>
  503. public void NavigateToHomePage()
  504. {
  505. Navigate(CurrentTheme.GetHomePage());
  506. }
  507. /// <summary>
  508. /// Shows a notification message that will disappear on it's own
  509. /// </summary>
  510. /// <param name="text">The text.</param>
  511. /// <param name="caption">The caption.</param>
  512. /// <param name="icon">The icon.</param>
  513. public void ShowNotificationMessage(string text, string caption = null, MessageBoxIcon icon = MessageBoxIcon.None)
  514. {
  515. ApplicationWindow.ShowModalMessage(text, caption: caption, icon: icon);
  516. }
  517. /// <summary>
  518. /// Shows a notification message that will disappear on it's own
  519. /// </summary>
  520. /// <param name="text">The text.</param>
  521. /// <param name="caption">The caption.</param>
  522. /// <param name="icon">The icon.</param>
  523. public void ShowNotificationMessage(UIElement text, string caption = null, MessageBoxIcon icon = MessageBoxIcon.None)
  524. {
  525. ApplicationWindow.ShowModalMessage(text, caption: caption, icon: icon);
  526. }
  527. /// <summary>
  528. /// Shows a modal message box and asynchronously returns a MessageBoxResult
  529. /// </summary>
  530. /// <param name="text">The text.</param>
  531. /// <param name="caption">The caption.</param>
  532. /// <param name="button">The button.</param>
  533. /// <param name="icon">The icon.</param>
  534. /// <returns>MessageBoxResult.</returns>
  535. public MessageBoxResult ShowModalMessage(string text, string caption = null, MessageBoxButton button = MessageBoxButton.OK, MessageBoxIcon icon = MessageBoxIcon.None)
  536. {
  537. return ApplicationWindow.ShowModalMessage(text, caption: caption, button: button, icon: icon);
  538. }
  539. /// <summary>
  540. /// Shows a modal message box and asynchronously returns a MessageBoxResult
  541. /// </summary>
  542. /// <param name="text">The text.</param>
  543. /// <param name="caption">The caption.</param>
  544. /// <param name="button">The button.</param>
  545. /// <param name="icon">The icon.</param>
  546. /// <returns>MessageBoxResult.</returns>
  547. public MessageBoxResult ShowModalMessage(UIElement text, string caption = null, MessageBoxButton button = MessageBoxButton.OK, MessageBoxIcon icon = MessageBoxIcon.None)
  548. {
  549. return ApplicationWindow.ShowModalMessage(text, caption: caption, button: button, icon: icon);
  550. }
  551. /// <summary>
  552. /// Shows the error message.
  553. /// </summary>
  554. /// <param name="message">The message.</param>
  555. /// <param name="caption">The caption.</param>
  556. public void ShowErrorMessage(string message, string caption = null)
  557. {
  558. caption = caption ?? "Error";
  559. ShowModalMessage(message, caption: caption, button: MessageBoxButton.OK, icon: MessageBoxIcon.Error);
  560. }
  561. /// <summary>
  562. /// Shows the default error message.
  563. /// </summary>
  564. public void ShowDefaultErrorMessage()
  565. {
  566. ShowErrorMessage("There was an error processing the request", "Error");
  567. }
  568. /// <summary>
  569. /// The _remote image cache
  570. /// </summary>
  571. private FileSystemRepository _remoteImageCache;
  572. /// <summary>
  573. /// Gets the remote image async.
  574. /// </summary>
  575. /// <param name="url">The URL.</param>
  576. /// <returns>Task{Image}.</returns>
  577. public async Task<Image> GetRemoteImageAsync(string url)
  578. {
  579. var bitmap = await GetRemoteBitmapAsync(url);
  580. return new Image { Source = bitmap };
  581. }
  582. /// <summary>
  583. /// Gets the remote image async.
  584. /// </summary>
  585. /// <param name="url">The URL.</param>
  586. /// <returns>Task{BitmapImage}.</returns>
  587. /// <exception cref="System.ArgumentNullException">url</exception>
  588. public Task<BitmapImage> GetRemoteBitmapAsync(string url)
  589. {
  590. if (string.IsNullOrEmpty(url))
  591. {
  592. throw new ArgumentNullException("url");
  593. }
  594. Logger.Info("Image url: " + url);
  595. return Task.Run(async () =>
  596. {
  597. var cachePath = _remoteImageCache.GetResourcePath(url.GetMD5().ToString());
  598. await _remoteImageCache.WaitForLockAsync(cachePath).ConfigureAwait(false);
  599. var releaseLock = true;
  600. try
  601. {
  602. using (var stream = new FileStream(cachePath, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, true))
  603. {
  604. return await GetRemoteBitmapAsync(stream).ConfigureAwait(false);
  605. }
  606. }
  607. catch (FileNotFoundException)
  608. {
  609. // Doesn't exist. No biggie
  610. releaseLock = false;
  611. }
  612. finally
  613. {
  614. if (releaseLock)
  615. {
  616. _remoteImageCache.ReleaseLock(cachePath);
  617. }
  618. }
  619. try
  620. {
  621. using (var httpStream = await UIKernel.Instance.ApiClient.GetImageStreamAsync(url + "&x=1"))
  622. {
  623. return await GetRemoteBitmapAsync(httpStream, cachePath);
  624. }
  625. }
  626. finally
  627. {
  628. _remoteImageCache.ReleaseLock(cachePath);
  629. }
  630. });
  631. }
  632. /// <summary>
  633. /// Gets the image async.
  634. /// </summary>
  635. /// <param name="sourceStream">The source stream.</param>
  636. /// <param name="cachePath">The cache path.</param>
  637. /// <returns>Task{BitmapImage}.</returns>
  638. private async Task<BitmapImage> GetRemoteBitmapAsync(Stream sourceStream, string cachePath = null)
  639. {
  640. byte[] bytes;
  641. using (var ms = new MemoryStream())
  642. {
  643. await sourceStream.CopyToAsync(ms).ConfigureAwait(false);
  644. bytes = ms.ToArray();
  645. }
  646. if (!string.IsNullOrEmpty(cachePath))
  647. {
  648. using (var fileStream = new FileStream(cachePath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, true))
  649. {
  650. await fileStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
  651. }
  652. }
  653. using (Stream stream = new MemoryStream(bytes))
  654. {
  655. var bitmapImage = new BitmapImage
  656. {
  657. CreateOptions = BitmapCreateOptions.DelayCreation
  658. };
  659. bitmapImage.BeginInit();
  660. bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
  661. bitmapImage.StreamSource = stream;
  662. bitmapImage.EndInit();
  663. bitmapImage.Freeze();
  664. return bitmapImage;
  665. }
  666. }
  667. }
  668. }