This commit is contained in:
Tobias Wohlleben 2025-01-21 14:38:30 +01:00
parent 11651e5c30
commit 0f931aa2d4
13 changed files with 266 additions and 259 deletions

View File

@ -1,9 +1,9 @@
using System.Windows; using System.Windows;
using CamBooth.App.Core.AppSettings; using CamBooth.App.Core.AppSettings;
using CamBooth.App.Camera;
using CamBooth.App.Core.Logging; using CamBooth.App.Core.Logging;
using CamBooth.App.PictureGallery; using CamBooth.App.Features.Camera;
using CamBooth.App.Features.PictureGallery;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;

View File

@ -30,6 +30,7 @@
<ItemGroup> <ItemGroup>
<Folder Include="Core\Models\" /> <Folder Include="Core\Models\" />
<Folder Include="Features\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -41,4 +42,16 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Page Update="Features\DebugConsole\DebugConsolePage.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Update="Features\LiveView\LiveViewPage.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Update="Features\PictureGallery\PictureGalleryPage.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
</Project> </Project>

View File

@ -1,46 +1,42 @@
using System.IO; using System.IO;
using System.Windows; using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using CamBooth.App.Core.AppSettings; using CamBooth.App.Core.AppSettings;
using CamBooth.App.Core.Logging; using CamBooth.App.Core.Logging;
using CamBooth.App.LiveView; using CamBooth.App.Features.PictureGallery;
using CamBooth.App.PictureGallery;
using EOSDigital.API; using EOSDigital.API;
using EOSDigital.SDK; using EOSDigital.SDK;
namespace CamBooth.App.Camera; namespace CamBooth.App.Features.Camera;
public class CameraService : IDisposable public class CameraService : IDisposable
{ {
private readonly Logger _logger;
private readonly AppSettingsService _appSettings; private readonly AppSettingsService _appSettings;
private readonly Logger _logger;
private readonly PictureGalleryService _pictureGalleryService; private readonly PictureGalleryService _pictureGalleryService;
CanonAPI APIHandler; private readonly CanonAPI APIHandler;
private CameraValue[] AvList;
private int BulbTime = 30;
private List<EOSDigital.API.Camera> CamList;
private int ErrCount;
private object ErrLock = new();
private bool IsInit;
private CameraValue[] ISOList;
public EOSDigital.API.Camera MainCamera; public EOSDigital.API.Camera MainCamera;
bool IsInit = false; private CameraValue[] TvList;
List<EOSDigital.API.Camera> CamList;
CameraValue[] AvList;
CameraValue[] TvList;
CameraValue[] ISOList;
int BulbTime = 30;
int ErrCount;
object ErrLock = new object();
public CameraService(Logger logger, AppSettingsService appSettings, PictureGalleryService pictureGalleryService) public CameraService(Logger logger, AppSettingsService appSettings, PictureGalleryService pictureGalleryService)
@ -50,8 +46,8 @@ public class CameraService : IDisposable
this._pictureGalleryService = pictureGalleryService; this._pictureGalleryService = pictureGalleryService;
try try
{ {
APIHandler = new CanonAPI(); this.APIHandler = new CanonAPI();
IsInit = true; this.IsInit = true;
} }
catch (DllNotFoundException) catch (DllNotFoundException)
{ {
@ -64,12 +60,22 @@ public class CameraService : IDisposable
} }
/// <summary> Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. </summary>
public void Dispose()
{
this.CloseSession();
this.IsInit = false;
this.APIHandler.Dispose();
this.MainCamera.Dispose();
}
public void ConnectCamera() public void ConnectCamera()
{ {
ErrorHandler.SevereErrorHappened += ErrorHandler_SevereErrorHappened; ErrorHandler.SevereErrorHappened += this.ErrorHandler_SevereErrorHappened;
ErrorHandler.NonSevereErrorHappened += ErrorHandler_NonSevereErrorHappened; ErrorHandler.NonSevereErrorHappened += this.ErrorHandler_NonSevereErrorHappened;
RefreshCamera(); this.RefreshCamera();
List<EOSDigital.API.Camera> cameraList = this.APIHandler.GetCameraList(); List<EOSDigital.API.Camera> cameraList = this.APIHandler.GetCameraList();
if (cameraList.Any()) if (cameraList.Any())
{ {
@ -79,20 +85,20 @@ public class CameraService : IDisposable
} }
string cameraDeviceNames = string.Join(", ", cameraList.Select(cam => cam.DeviceName)); string cameraDeviceNames = string.Join(", ", cameraList.Select(cam => cam.DeviceName));
_logger.Info(cameraDeviceNames == string.Empty ? "No cameras / devices found" : cameraDeviceNames); this._logger.Info(cameraDeviceNames == string.Empty ? "No cameras / devices found" : cameraDeviceNames);
} }
private void SetSettingSaveToComputer() private void SetSettingSaveToComputer()
{ {
MainCamera.SetSetting(PropertyID.SaveTo, (int)SaveTo.Host); this.MainCamera.SetSetting(PropertyID.SaveTo, (int)SaveTo.Host);
MainCamera.SetCapacity(4096, int.MaxValue); this.MainCamera.SetCapacity(4096, int.MaxValue);
} }
public void CloseSession() public void CloseSession()
{ {
MainCamera.CloseSession(); this.MainCamera.CloseSession();
// AvCoBox.Items.Clear(); // AvCoBox.Items.Clear();
// TvCoBox.Items.Clear(); // TvCoBox.Items.Clear();
@ -108,7 +114,7 @@ public class CameraService : IDisposable
private void RefreshCamera() private void RefreshCamera()
{ {
// CameraListBox.Items.Clear(); // CameraListBox.Items.Clear();
CamList = APIHandler.GetCameraList(); this.CamList = this.APIHandler.GetCameraList();
// foreach (Camera cam in CamList) CameraListBox.Items.Add(cam.DeviceName); // foreach (Camera cam in CamList) CameraListBox.Items.Add(cam.DeviceName);
// if (MainCamera?.SessionOpen == true) CameraListBox.SelectedIndex = CamList.FindIndex(t => t.ID == MainCamera.ID); // if (MainCamera?.SessionOpen == true) CameraListBox.SelectedIndex = CamList.FindIndex(t => t.ID == MainCamera.ID);
@ -118,17 +124,17 @@ public class CameraService : IDisposable
private void OpenSession() private void OpenSession()
{ {
MainCamera = CamList[0]; this.MainCamera = this.CamList[0];
MainCamera.OpenSession(); this.MainCamera.OpenSession();
//MainCamera.ProgressChanged += MainCamera_ProgressChanged; //MainCamera.ProgressChanged += MainCamera_ProgressChanged;
MainCamera.StateChanged += MainCamera_StateChanged; this.MainCamera.StateChanged += this.MainCamera_StateChanged;
MainCamera.DownloadReady += MainCamera_DownloadReady; this.MainCamera.DownloadReady += this.MainCamera_DownloadReady;
//SessionLabel.Content = MainCamera.DeviceName; //SessionLabel.Content = MainCamera.DeviceName;
AvList = MainCamera.GetSettingsList(PropertyID.Av); this.AvList = this.MainCamera.GetSettingsList(PropertyID.Av);
TvList = MainCamera.GetSettingsList(PropertyID.Tv); this.TvList = this.MainCamera.GetSettingsList(PropertyID.Tv);
ISOList = MainCamera.GetSettingsList(PropertyID.ISO); this.ISOList = this.MainCamera.GetSettingsList(PropertyID.ISO);
// foreach (var Av in AvList) AvCoBox.Items.Add(Av.StringValue); // foreach (var Av in AvList) AvCoBox.Items.Add(Av.StringValue);
// foreach (var Tv in TvList) TvCoBox.Items.Add(Tv.StringValue); // foreach (var Tv in TvList) TvCoBox.Items.Add(Tv.StringValue);
@ -151,13 +157,13 @@ public class CameraService : IDisposable
{ {
try try
{ {
if (!MainCamera.IsLiveViewOn) if (!this.MainCamera.IsLiveViewOn)
{ {
MainCamera.StartLiveView(); this.MainCamera.StartLiveView();
} }
else else
{ {
MainCamera.StopLiveView(); this.MainCamera.StopLiveView();
//LVCanvas.Background = Brushes.LightGray; //LVCanvas.Background = Brushes.LightGray;
} }
@ -169,6 +175,20 @@ public class CameraService : IDisposable
} }
public void TakePhoto()
{
try
{
this.MainCamera.TakePhoto();
}
catch (Exception ex)
{
this.ReportError(ex.Message);
throw;
}
}
#region API Events #region API Events
// private void APIHandler_CameraAdded(CanonAPI sender) // private void APIHandler_CameraAdded(CanonAPI sender)
@ -187,7 +207,7 @@ public class CameraService : IDisposable
{ {
try try
{ {
if (eventID == StateEventID.Shutdown && IsInit) if (eventID == StateEventID.Shutdown && this.IsInit)
{ {
Application.Current.Dispatcher.Invoke(() => this.CloseSession()); Application.Current.Dispatcher.Invoke(() => this.CloseSession());
@ -266,28 +286,4 @@ public class CameraService : IDisposable
} }
#endregion #endregion
public void TakePhoto()
{
try
{
MainCamera.TakePhoto();
}
catch (Exception ex)
{
this.ReportError(ex.Message);
throw;
}
}
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public void Dispose()
{
this.CloseSession();
IsInit = false;
this.APIHandler.Dispose();
this.MainCamera.Dispose();
}
} }

View File

@ -1,9 +1,8 @@
<Page x:Class="CamBooth.App.DebugConsole.DebugConsolePage" <Page x:Class="CamBooth.App.Features.DebugConsole.DebugConsolePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CamBooth.App.DebugConsole"
mc:Ignorable="d" mc:Ignorable="d"
Title="DebugConsolePage" Width="1600" Height="170" HorizontalAlignment="Stretch"> Title="DebugConsolePage" Width="1600" Height="170" HorizontalAlignment="Stretch">
<StackPanel> <StackPanel>

View File

@ -3,15 +3,15 @@ using System.Windows.Media;
using CamBooth.App.Core.Logging; using CamBooth.App.Core.Logging;
namespace CamBooth.App.DebugConsole; namespace CamBooth.App.Features.DebugConsole;
public partial class DebugConsolePage : Page public partial class DebugConsolePage : Page
{ {
public DebugConsolePage(Logger logger) public DebugConsolePage(Logger logger)
{ {
logger.InfoLog += Logger_OnInfoLog; logger.InfoLog += this.Logger_OnInfoLog;
logger.ErrorLog += Logger_OnErrorLog; logger.ErrorLog += this.Logger_OnErrorLog;
InitializeComponent(); this.InitializeComponent();
} }

View File

@ -1,9 +1,8 @@
<Page x:Class="CamBooth.App.LiveView.LiveViewPage" <Page x:Class="CamBooth.App.Features.LiveView.LiveViewPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CamBooth.App.LiveView"
mc:Ignorable="d" mc:Ignorable="d"
Title="LiveViewPage" Width="1350" Height="900" Title="LiveViewPage" Width="1350" Height="900"
Background="PaleVioletRed"> Background="PaleVioletRed">

View File

@ -1,31 +1,26 @@
using System.ComponentModel; using System.IO;
using System.Configuration;
using System.IO;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using CamBooth.App.Core.AppSettings; using CamBooth.App.Core.AppSettings;
using CamBooth.App.Camera;
using CamBooth.App.Core.Logging; using CamBooth.App.Core.Logging;
using CamBooth.App.Features.Camera;
using EOSDigital.API; namespace CamBooth.App.Features.LiveView;
using EOSDigital.SDK;
namespace CamBooth.App.LiveView;
public partial class LiveViewPage : Page public partial class LiveViewPage : Page
{ {
private readonly Logger _logger;
private readonly AppSettingsService _appSettings; private readonly AppSettingsService _appSettings;
private readonly CameraService _cameraService; private readonly CameraService _cameraService;
Action<BitmapImage> SetImageAction; private readonly Logger _logger;
private ImageBrush bgbrush = new ImageBrush(); private readonly ImageBrush bgbrush = new();
private readonly Action<BitmapImage> SetImageAction;
public LiveViewPage(Logger logger, AppSettingsService appSettings, CameraService cameraService) public LiveViewPage(Logger logger, AppSettingsService appSettings, CameraService cameraService)
@ -33,11 +28,11 @@ public partial class LiveViewPage : Page
this._logger = logger; this._logger = logger;
this._appSettings = appSettings; this._appSettings = appSettings;
this._cameraService = cameraService; this._cameraService = cameraService;
InitializeComponent(); this.InitializeComponent();
SetImageAction = (BitmapImage img) => { bgbrush.ImageSource = img; }; this.SetImageAction = img => { this.bgbrush.ImageSource = img; };
LVCanvas.Background = bgbrush; this.LVCanvas.Background = this.bgbrush;
cameraService.ConnectCamera(); cameraService.ConnectCamera();
cameraService.MainCamera.LiveViewUpdated += MainCamera_OnLiveViewUpdated; cameraService.MainCamera.LiveViewUpdated += this.MainCamera_OnLiveViewUpdated;
} }
@ -45,21 +40,21 @@ public partial class LiveViewPage : Page
{ {
try try
{ {
using (WrapStream s = new WrapStream(img)) using (WrapStream s = new(img))
{ {
img.Position = 0; img.Position = 0;
BitmapImage EvfImage = new BitmapImage(); BitmapImage EvfImage = new();
EvfImage.BeginInit(); EvfImage.BeginInit();
EvfImage.StreamSource = s; EvfImage.StreamSource = s;
EvfImage.CacheOption = BitmapCacheOption.OnLoad; EvfImage.CacheOption = BitmapCacheOption.OnLoad;
EvfImage.EndInit(); EvfImage.EndInit();
EvfImage.Freeze(); EvfImage.Freeze();
Application.Current.Dispatcher.BeginInvoke(SetImageAction, EvfImage); Application.Current.Dispatcher.BeginInvoke(this.SetImageAction, EvfImage);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex.Message); this._logger.Error(ex.Message);
} }
} }

View File

@ -0,0 +1,127 @@
using System.IO;
namespace CamBooth.App.Features.LiveView;
/// <summary>
/// A stream that does nothing more but wrap another stream (needed for a WPF memory leak)
/// </summary>
public sealed class WrapStream : Stream
{
private readonly Stream Base;
/// <summary>
/// Creates a new instance of the <see cref="WrapStream" /> class.
/// </summary>
/// <param name="inStream"> The stream that gets wrapped </param>
public WrapStream(Stream inStream)
{
this.Base = inStream;
}
/// <summary>
/// Gets a value indicating whether the current stream supports reading.
/// </summary>
public override bool CanRead => this.Base.CanRead;
/// <summary>
/// Gets a value indicating whether the current stream supports seeking.
/// </summary>
public override bool CanSeek => this.Base.CanSeek;
/// <summary>
/// Gets a value indicating whether the current stream supports writing.
/// </summary>
public override bool CanWrite => this.Base.CanWrite;
/// <summary>
/// Gets the length in bytes of the stream.
/// </summary>
public override long Length => this.Base.Length;
/// <summary>
/// Gets or sets the position within the current stream.
/// </summary>
public override long Position
{
get => this.Base.Position;
set => this.Base.Position = value;
}
/// <summary>
/// reads a sequence of bytes from the current stream and advances
/// the position within the stream by the number of bytes read.
/// </summary>
/// <param name="buffer">
/// An array of bytes. When this method returns, the buffer contains the specified
/// byte array with the values between offset and (offset + count - 1) replaced
/// by the bytes read from the current source.
/// </param>
/// <param name="offset">
/// The zero-based byte offset in buffer at which to begin storing the data read
/// from the current stream.
/// </param>
/// <param name="count"> The maximum number of bytes to be read from the current stream. </param>
/// <returns>
/// The total number of bytes read into the buffer. This can be less than the
/// number of bytes requested if that many bytes are not currently available,
/// or zero (0) if the end of the stream has been reached.
/// </returns>
public override int Read(byte[] buffer, int offset, int count)
{
return this.Base.Read(buffer, offset, count);
}
/// <summary>
/// When overridden in a derived class, writes a sequence of bytes to the current
/// stream and advances the current position within this stream by the number
/// of bytes written.
/// </summary>
/// <param name="buffer"> An array of bytes. This method copies count bytes from buffer to the current stream. </param>
/// <param name="offset">
/// The zero-based byte offset in buffer at which to begin copying bytes to the
/// current stream.
/// </param>
/// <param name="count"> The number of bytes to be written to the current stream. </param>
public override void Write(byte[] buffer, int offset, int count)
{
this.Base.Write(buffer, offset, count);
}
/// <summary>
/// sets the position within the current stream.
/// </summary>
/// <param name="offset"> A byte offset relative to the origin parameter. </param>
/// <param name="origin">
/// A value of type System.IO.SeekOrigin indicating the reference point used
/// to obtain the new position.
/// </param>
/// <returns> The new position within the current stream. </returns>
public override long Seek(long offset, SeekOrigin origin)
{
return this.Base.Seek(offset, origin);
}
/// <summary>
/// Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
/// </summary>
public override void Flush()
{
this.Base.Flush();
}
/// <summary>
/// Sets the length of the current stream.
/// </summary>
/// <param name="value"> The desired length of the current stream in bytes. </param>
public override void SetLength(long value)
{
this.Base.SetLength(value);
}
}

View File

@ -1,12 +1,10 @@
<Page x:Class="CamBooth.App.PictureGallery.PictureGalleryPage" <Page x:Class="CamBooth.App.Features.PictureGallery.PictureGalleryPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CamBooth.App.PictureGallery"
mc:Ignorable="d" mc:Ignorable="d"
Title="PictureGalleryPage" Width="1600" Height="795" Title="PictureGalleryPage" Width="1600" Height="795"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
Background="Black"> Background="Black">
<Grid> <Grid>
<WrapPanel VerticalAlignment="Stretch" x:Name="PicturesPanel" Orientation="Horizontal" /> <WrapPanel VerticalAlignment="Stretch" x:Name="PicturesPanel" Orientation="Horizontal" />

View File

@ -1,8 +1,6 @@
using System.IO; using System.Windows;
using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using CamBooth.App.Core.AppSettings; using CamBooth.App.Core.AppSettings;
@ -10,12 +8,11 @@ using CamBooth.App.Core.Logging;
using Wpf.Ui.Controls; using Wpf.Ui.Controls;
using Button = Wpf.Ui.Controls.Button;
using Image = Wpf.Ui.Controls.Image; using Image = Wpf.Ui.Controls.Image;
using MessageBox = System.Windows.MessageBox; using MessageBox = System.Windows.MessageBox;
using TextBlock = Wpf.Ui.Controls.TextBlock; using TextBlock = Wpf.Ui.Controls.TextBlock;
namespace CamBooth.App.PictureGallery; namespace CamBooth.App.Features.PictureGallery;
public partial class PictureGalleryPage : Page public partial class PictureGalleryPage : Page
{ {
@ -31,7 +28,7 @@ public partial class PictureGalleryPage : Page
this._appSettingsService = appSettingsService; this._appSettingsService = appSettingsService;
this._logger = logger; this._logger = logger;
this._pictureGalleryService = pictureGalleryService; this._pictureGalleryService = pictureGalleryService;
InitializeComponent(); this.InitializeComponent();
this.Initialize(); this.Initialize();
} }
@ -40,7 +37,7 @@ public partial class PictureGalleryPage : Page
{ {
try try
{ {
LoadPictures(12); this.LoadPictures(12);
} }
catch (Exception e) catch (Exception e)
{ {
@ -69,12 +66,12 @@ public partial class PictureGalleryPage : Page
{ {
BitmapImage thumbnail = this._pictureGalleryService.ThumbnailsOrderedByNewestDescending[loop]; BitmapImage thumbnail = this._pictureGalleryService.ThumbnailsOrderedByNewestDescending[loop];
var textBlock = new TextBlock(); TextBlock? textBlock = new();
var hyperlink = new Hyperlink(); Hyperlink? hyperlink = new();
hyperlink.Click += Hyperlink_OnClick; hyperlink.Click += this.Hyperlink_OnClick;
hyperlink.Tag = thumbnail.UriSource; hyperlink.Tag = thumbnail.UriSource;
hyperlink.TextDecorations = null; hyperlink.TextDecorations = null;
var imageControl = new Wpf.Ui.Controls.Image Image? imageControl = new()
{ {
Source = thumbnail, Source = thumbnail,
Width = 381, Width = 381,
@ -97,8 +94,8 @@ public partial class PictureGalleryPage : Page
Application.Current.Dispatcher.BeginInvoke( Application.Current.Dispatcher.BeginInvoke(
async () => async () =>
{ {
var contentDialog = new ContentDialog(RootContentDialogPresenter); ContentDialog? contentDialog = new(this.RootContentDialogPresenter);
var imageToShow = new Image Image? imageToShow = new()
{ {
VerticalAlignment = VerticalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
Source = PictureGalleryService.CreateThumbnail(picturePathUri.AbsolutePath, 499, 300) Source = PictureGalleryService.CreateThumbnail(picturePathUri.AbsolutePath, 499, 300)
@ -107,14 +104,16 @@ public partial class PictureGalleryPage : Page
contentDialog.DialogWidth = 940; contentDialog.DialogWidth = 940;
contentDialog.PrimaryButtonAppearance = ControlAppearance.Success; contentDialog.PrimaryButtonAppearance = ControlAppearance.Success;
contentDialog.CloseButtonAppearance = ControlAppearance.Primary; contentDialog.CloseButtonAppearance = ControlAppearance.Primary;
//contentDialog.SetCurrentValue(ContentDialog.TitleProperty, "Hello World"); //contentDialog.SetCurrentValue(ContentDialog.TitleProperty, "Hello World");
contentDialog.SetCurrentValue(ContentControl.ContentProperty, imageToShow); contentDialog.SetCurrentValue(ContentControl.ContentProperty, imageToShow);
contentDialog.SetCurrentValue(ContentDialog.CloseButtonTextProperty, "Schließen"); contentDialog.SetCurrentValue(ContentDialog.CloseButtonTextProperty, "Schließen");
contentDialog.SetCurrentValue(ContentDialog.PrimaryButtonTextProperty, "Drucken"); contentDialog.SetCurrentValue(ContentDialog.PrimaryButtonTextProperty, "Drucken");
// contentDialog.SetCurrentValue(ContentDialog.PrimaryButtonIconProperty, PictureGalleryService.CreateRegularSymbolIcon(SymbolRegular.Print48, Colors.Tomato)); // contentDialog.SetCurrentValue(ContentDialog.PrimaryButtonIconProperty, PictureGalleryService.CreateRegularSymbolIcon(SymbolRegular.Print48, Colors.Tomato));
contentDialog.Tag = picturePathUri.AbsolutePath; contentDialog.Tag = picturePathUri.AbsolutePath;
contentDialog.ButtonClicked += ContentDialog_OnButtonClicked; contentDialog.ButtonClicked += this.ContentDialog_OnButtonClicked;
await contentDialog.ShowAsync(); await contentDialog.ShowAsync();
}); });

View File

@ -1,5 +1,4 @@
using System.IO; using System.IO;
using System.Windows;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
@ -8,7 +7,7 @@ using CamBooth.App.Core.Logging;
using Wpf.Ui.Controls; using Wpf.Ui.Controls;
namespace CamBooth.App.PictureGallery; namespace CamBooth.App.Features.PictureGallery;
public class PictureGalleryService public class PictureGalleryService
{ {
@ -16,12 +15,7 @@ public class PictureGalleryService
private readonly Logger _logger; private readonly Logger _logger;
public List<BitmapImage> ThumbnailsOrderedByNewestDescending => private readonly Dictionary<DateTime, BitmapImage> thumbnails = new();
this.thumbnails.OrderByDescending(map => map.Key)
.Select(ordered => ordered.Value)
.ToList();
private Dictionary<DateTime, BitmapImage> thumbnails = new();
public PictureGalleryService(AppSettingsService appSettings, Logger logger) public PictureGalleryService(AppSettingsService appSettings, Logger logger)
@ -31,6 +25,12 @@ public class PictureGalleryService
} }
public List<BitmapImage> ThumbnailsOrderedByNewestDescending =>
this.thumbnails.OrderByDescending(map => map.Key)
.Select(ordered => ordered.Value)
.ToList();
public async Task LoadThumbnailsToCache(int cacheSize = 0) public async Task LoadThumbnailsToCache(int cacheSize = 0)
{ {
this._logger.Info("Start load thumbnails into cache"); this._logger.Info("Start load thumbnails into cache");
@ -51,7 +51,7 @@ public class PictureGalleryService
// add if not exists // add if not exists
if (!this.thumbnails.ContainsKey(creationTime)) if (!this.thumbnails.ContainsKey(creationTime))
{ {
this.thumbnails.Add(creationTime, CreateThumbnail(picturePath, 244, 0)); this.thumbnails.Add(creationTime, PictureGalleryService.CreateThumbnail(picturePath, 244, 0));
this._logger.Info($"Thumbnail '{picturePath}' successfully created"); this._logger.Info($"Thumbnail '{picturePath}' successfully created");
} }
@ -65,7 +65,7 @@ public class PictureGalleryService
public static BitmapImage CreateThumbnail(string filePath, int maxWidth, int maxHeight) public static BitmapImage CreateThumbnail(string filePath, int maxWidth, int maxHeight)
{ {
var bitmap = new BitmapImage(); BitmapImage? bitmap = new();
bitmap.BeginInit(); bitmap.BeginInit();
bitmap.UriSource = new Uri(filePath); bitmap.UriSource = new Uri(filePath);
bitmap.DecodePixelWidth = maxWidth; // Größe des ThumbnailsOrderedByNewestDescending direkt beim Dekodieren festlegen bitmap.DecodePixelWidth = maxWidth; // Größe des ThumbnailsOrderedByNewestDescending direkt beim Dekodieren festlegen
@ -76,6 +76,7 @@ public class PictureGalleryService
return bitmap; return bitmap;
} }
public static SymbolIcon CreateRegularSymbolIcon(SymbolRegular symbolRegular, Color foregroundColor = default) public static SymbolIcon CreateRegularSymbolIcon(SymbolRegular symbolRegular, Color foregroundColor = default)
{ {
return new SymbolIcon return new SymbolIcon

View File

@ -1,120 +0,0 @@

using System.IO;
namespace CamBooth.App.LiveView
{
/// <summary>
/// A stream that does nothing more but wrap another stream (needed for a WPF memory leak)
/// </summary>
public sealed class WrapStream : Stream
{
/// <summary>
/// Gets a value indicating whether the current stream supports reading.
/// </summary>
public override bool CanRead
{
get { return this.Base.CanRead; }
}
/// <summary>
/// Gets a value indicating whether the current stream supports seeking.
/// </summary>
public override bool CanSeek
{
get { return this.Base.CanSeek; }
}
/// <summary>
/// Gets a value indicating whether the current stream supports writing.
/// </summary>
public override bool CanWrite
{
get { return this.Base.CanWrite; }
}
/// <summary>
/// Gets the length in bytes of the stream.
/// </summary>
public override long Length
{
get { return this.Base.Length; }
}
/// <summary>
/// Gets or sets the position within the current stream.
/// </summary>
public override long Position
{
get { return this.Base.Position; }
set { this.Base.Position = value; }
}
private Stream Base;
/// <summary>
/// Creates a new instance of the <see cref="WrapStream"/> class.
/// </summary>
/// <param name="inStream">The stream that gets wrapped</param>
public WrapStream(Stream inStream)
{
this.Base = inStream;
}
/// <summary>
/// reads a sequence of bytes from the current stream and advances
/// the position within the stream by the number of bytes read.
/// </summary>
/// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified
/// byte array with the values between offset and (offset + count - 1) replaced
/// by the bytes read from the current source.</param>
/// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read
/// from the current stream.</param>
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
/// <returns>The total number of bytes read into the buffer. This can be less than the
/// number of bytes requested if that many bytes are not currently available,
/// or zero (0) if the end of the stream has been reached.</returns>
public override int Read(byte[] buffer, int offset, int count)
{
return this.Base.Read(buffer, offset, count);
}
/// <summary>
/// When overridden in a derived class, writes a sequence of bytes to the current
/// stream and advances the current position within this stream by the number
/// of bytes written.
/// </summary>
/// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream.</param>
/// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the
/// current stream.</param>
/// <param name="count">The number of bytes to be written to the current stream.</param>
public override void Write(byte[] buffer, int offset, int count)
{
this.Base.Write(buffer, offset, count);
}
/// <summary>
/// sets the position within the current stream.
/// </summary>
/// <param name="offset">A byte offset relative to the origin parameter.</param>
/// <param name="origin">A value of type System.IO.SeekOrigin indicating the reference point used
/// to obtain the new position.</param>
/// <returns>The new position within the current stream.</returns>
public override long Seek(long offset, System.IO.SeekOrigin origin)
{
return this.Base.Seek(offset, origin);
}
/// <summary>
/// Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
/// </summary>
public override void Flush()
{
this.Base.Flush();
}
/// <summary>
/// Sets the length of the current stream.
/// </summary>
/// <param name="value">The desired length of the current stream in bytes.</param>
public override void SetLength(long value)
{
this.Base.SetLength(value);
}
}
}

View File

@ -1,12 +1,12 @@
using System.ComponentModel; using System.ComponentModel;
using System.Windows; using System.Windows;
using CamBooth.App.Camera;
using CamBooth.App.Core.AppSettings; using CamBooth.App.Core.AppSettings;
using CamBooth.App.Core.Logging; using CamBooth.App.Core.Logging;
using CamBooth.App.DebugConsole; using CamBooth.App.Features.Camera;
using CamBooth.App.LiveView; using CamBooth.App.Features.DebugConsole;
using CamBooth.App.PictureGallery; using CamBooth.App.Features.LiveView;
using CamBooth.App.Features.PictureGallery;
namespace CamBooth.App; namespace CamBooth.App;