wip
This commit is contained in:
parent
11651e5c30
commit
0f931aa2d4
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -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>
|
||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1,13 +1,12 @@
|
|||||||
<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">
|
||||||
<Grid HorizontalAlignment="Stretch" >
|
<Grid HorizontalAlignment="Stretch">
|
||||||
<Canvas x:Name="LVCanvas" Background="Bisque" />
|
<Canvas x:Name="LVCanvas" Background="Bisque" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
@ -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,24 +40,24 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
127
src/CamBooth/CamBooth.App/Features/LiveView/WrappingStream.cs
Normal file
127
src/CamBooth/CamBooth.App/Features/LiveView/WrappingStream.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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" />
|
||||||
@ -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,24 +94,26 @@ 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)
|
||||||
};
|
};
|
||||||
|
|
||||||
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();
|
||||||
});
|
});
|
||||||
@ -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
|
||||||
@ -75,16 +75,17 @@ public class PictureGalleryService
|
|||||||
bitmap.Freeze(); // Threadsicher machen
|
bitmap.Freeze(); // Threadsicher machen
|
||||||
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
|
||||||
{
|
{
|
||||||
Symbol = symbolRegular,
|
Symbol = symbolRegular,
|
||||||
Foreground = new SolidColorBrush(foregroundColor),
|
Foreground = new SolidColorBrush(foregroundColor),
|
||||||
Width = 24,
|
Width = 24,
|
||||||
Height = 24,
|
Height = 24,
|
||||||
FontSize = 24
|
FontSize = 24
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user