Skip to content

Commit 876c910

Browse files
authored
Merge pull request #60 from TheJoeFin/mvvm-main-page
MVVM the MainPage
2 parents d01665c + 058e0f6 commit 876c910

29 files changed

Lines changed: 1435 additions & 1039 deletions

Simple Icon File Maker/Simple Icon File Maker (Package)/Simple Icon File Maker (Package).wapproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,10 @@
120120
</ProjectReference>
121121
</ItemGroup>
122122
<ItemGroup>
123-
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251003001">
123+
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002">
124124
<IncludeAssets>build</IncludeAssets>
125125
</PackageReference>
126-
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6584">
126+
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175">
127127
<IncludeAssets>build</IncludeAssets>
128128
</PackageReference>
129129
<PackageReference Include="WinUIEx" Version="2.9.0" />

Simple Icon File Maker/Simple Icon File Maker/Activation/ActivationHandler.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,21 @@ public abstract class ActivationHandler<T> : IActivationHandler
44
where T : class
55
{
66
// Override this method to add the logic for whether to handle the activation.
7-
protected virtual bool CanHandleInternal(T args) => true;
7+
protected virtual bool CanHandleInternal(T args)
8+
{
9+
return true;
10+
}
811

912
// Override this method to add the logic for your activation handler.
1013
protected abstract Task HandleInternalAsync(T args);
1114

12-
public bool CanHandle(object args) => args is T && CanHandleInternal((args as T)!);
15+
public bool CanHandle(object args)
16+
{
17+
return args is T && CanHandleInternal((args as T)!);
18+
}
1319

14-
public async Task HandleAsync(object args) => await HandleInternalAsync((args as T)!);
20+
public async Task HandleAsync(object args)
21+
{
22+
await HandleInternalAsync((args as T)!);
23+
}
1524
}

Simple Icon File Maker/Simple Icon File Maker/Activation/DefaultActivationHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ protected override bool CanHandleInternal(LaunchActivatedEventArgs args)
2121
return _navigationService.Frame?.Content == null;
2222
}
2323

24-
protected async override Task HandleInternalAsync(LaunchActivatedEventArgs args)
24+
protected override async Task HandleInternalAsync(LaunchActivatedEventArgs args)
2525
{
2626
_navigationService.NavigateTo(typeof(MainViewModel).FullName!, args.Arguments);
2727

Simple Icon File Maker/Simple Icon File Maker/App.xaml.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
using Microsoft.Extensions.DependencyInjection;
22
using Microsoft.Extensions.Hosting;
33
using Microsoft.UI.Xaml;
4-
5-
using Simple_Icon_File_Maker.Contracts.Services;
6-
using Simple_Icon_File_Maker.Services;
74
using Simple_Icon_File_Maker.Activation;
5+
using Simple_Icon_File_Maker.Contracts.Services;
86
using Simple_Icon_File_Maker.Models;
9-
using Simple_Icon_File_Maker.Views;
7+
using Simple_Icon_File_Maker.Services;
108
using Simple_Icon_File_Maker.ViewModels;
9+
using Simple_Icon_File_Maker.Views;
1110

1211
namespace Simple_Icon_File_Maker;
1312

@@ -90,7 +89,7 @@ private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledEx
9089
e.Handled = true;
9190
}
9291

93-
protected async override void OnLaunched(LaunchActivatedEventArgs args)
92+
protected override async void OnLaunched(LaunchActivatedEventArgs args)
9493
{
9594
base.OnLaunched(args);
9695
await App.GetService<IActivationService>().ActivateAsync(args);

Simple Icon File Maker/Simple Icon File Maker/Controls/PreviewImage.xaml.cs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ namespace Simple_Icon_File_Maker.Controls;
1717

1818
public sealed partial class PreviewImage : UserControl
1919
{
20-
readonly string OriginalName = string.Empty;
21-
readonly StorageFile _imageFile;
22-
readonly int _sideLength = 0;
20+
private readonly string OriginalName = string.Empty;
21+
private readonly StorageFile _imageFile;
22+
private readonly int _sideLength = 0;
2323

2424
public PreviewImage(StorageFile imageFile, int sideLength, string originalName)
2525
{
@@ -35,10 +35,7 @@ public PreviewImage(StorageFile imageFile, int sideLength, string originalName)
3535

3636
public bool ZoomPreview
3737
{
38-
get
39-
{
40-
return isZooming;
41-
}
38+
get => isZooming;
4239
set
4340
{
4441
if (value != isZooming)
@@ -148,8 +145,10 @@ private void LoadImageOnToCanvas()
148145
};
149146

150147
// add the visual as a child to canvas
151-
Grid tempGrid = new();
152-
tempGrid.Background = new SolidColorBrush(Colors.Transparent);
148+
Grid tempGrid = new()
149+
{
150+
Background = new SolidColorBrush(Colors.Transparent)
151+
};
153152
ElementCompositionPreview.SetElementChildVisual(tempGrid, imageVisual);
154153
mainImageCanvas.Children.Add(tempGrid);
155154
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System.Diagnostics;
2+
using Windows.ApplicationModel.DataTransfer;
3+
using Windows.Storage;
4+
using Windows.Storage.Streams;
5+
6+
namespace Simple_Icon_File_Maker.Helpers;
7+
8+
public static class ClipboardHelper
9+
{
10+
public static async Task<string?> TryGetImageFromClipboardAsync()
11+
{
12+
try
13+
{
14+
DataPackageView dataPackageView = Clipboard.GetContent();
15+
16+
if (dataPackageView == null)
17+
return null;
18+
19+
// Try to get bitmap data from clipboard
20+
if (dataPackageView.Contains(StandardDataFormats.Bitmap))
21+
{
22+
Debug.WriteLine("Clipboard contains Bitmap");
23+
24+
IRandomAccessStreamReference bitmapStreamRef = await dataPackageView.GetBitmapAsync();
25+
26+
if (bitmapStreamRef != null)
27+
{
28+
using IRandomAccessStreamWithContentType bitmapStream = await bitmapStreamRef.OpenReadAsync();
29+
30+
// Save the bitmap to a temporary file
31+
StorageFolder tempFolder = ApplicationData.Current.LocalCacheFolder;
32+
string tempFileName = $"clipboard_paste_{DateTime.Now:yyyyMMddHHmmss}.png";
33+
StorageFile tempFile = await tempFolder.CreateFileAsync(tempFileName, CreationCollisionOption.ReplaceExisting);
34+
35+
using (IRandomAccessStream fileStream = await tempFile.OpenAsync(FileAccessMode.ReadWrite))
36+
{
37+
await RandomAccessStream.CopyAsync(bitmapStream, fileStream);
38+
await fileStream.FlushAsync();
39+
}
40+
41+
return tempFile.Path;
42+
}
43+
}
44+
// Try to get storage items (files) from clipboard
45+
else if (dataPackageView.Contains(StandardDataFormats.StorageItems))
46+
{
47+
Debug.WriteLine("Clipboard contains StorageItems");
48+
IReadOnlyList<IStorageItem> storageItems = await dataPackageView.GetStorageItemsAsync();
49+
50+
if (storageItems != null && storageItems.Count > 0)
51+
{
52+
return await StorageItemHelper.TryGetImagePathFromStorageItems(storageItems);
53+
}
54+
}
55+
56+
return null;
57+
}
58+
catch (Exception ex)
59+
{
60+
Debug.WriteLine($"Error pasting from clipboard: {ex.Message}");
61+
return null;
62+
}
63+
}
64+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using Windows.Storage;
2+
using Windows.Storage.Pickers;
3+
4+
namespace Simple_Icon_File_Maker.Helpers;
5+
6+
public static class FilePickerHelper
7+
{
8+
public static async Task TrySetSuggestedFolderFromSourceImage(FileSavePicker savePicker, string imagePath)
9+
{
10+
if (string.IsNullOrWhiteSpace(imagePath))
11+
return;
12+
13+
try
14+
{
15+
// Use the source image file itself to suggest the folder
16+
// This makes the picker open in the source image's folder
17+
if (File.Exists(imagePath))
18+
{
19+
StorageFile sourceFile = await StorageFile.GetFileFromPathAsync(imagePath);
20+
savePicker.SuggestedSaveFile = sourceFile;
21+
}
22+
}
23+
catch
24+
{
25+
// If file access fails, fall back to default picker behavior
26+
}
27+
}
28+
}

Simple Icon File Maker/Simple Icon File Maker/Helpers/FrameExtensions.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@ namespace Simple_Icon_File_Maker.Helpers;
44

55
public static class FrameExtensions
66
{
7-
public static object? GetPageViewModel(this Frame frame) => frame?.Content?.GetType().GetProperty("ViewModel")?.GetValue(frame.Content, null);
7+
public static object? GetPageViewModel(this Frame frame)
8+
{
9+
return frame?.Content?.GetType().GetProperty("ViewModel")?.GetValue(frame.Content, null);
10+
}
811
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
using ImageMagick;
2+
using Microsoft.UI.Xaml.Controls;
3+
using Windows.Storage;
4+
5+
namespace Simple_Icon_File_Maker.Helpers;
6+
7+
public static class ImageHelper
8+
{
9+
public static async Task<MagickImage?> LoadImageAsync(string imagePath)
10+
{
11+
if (string.IsNullOrWhiteSpace(imagePath))
12+
return null;
13+
14+
try
15+
{
16+
MagickImage image;
17+
// For .ico files, load the largest frame instead of the first one
18+
if (Path.GetExtension(imagePath).Equals(".ico", StringComparison.InvariantCultureIgnoreCase))
19+
{
20+
MagickImageCollection collection = new(imagePath);
21+
// Find the largest frame by area (width * height)
22+
MagickImage? largestFrame = collection.Cast<MagickImage>()
23+
.OrderByDescending(img => (int)img.Width * (int)img.Height)
24+
.FirstOrDefault();
25+
26+
if (largestFrame != null)
27+
{
28+
// Create a new image from the largest frame to avoid disposal issues
29+
image = (MagickImage)largestFrame.Clone();
30+
}
31+
else
32+
{
33+
// Fallback to the first frame if something goes wrong
34+
image = new(imagePath);
35+
}
36+
}
37+
else
38+
{
39+
image = new(imagePath);
40+
}
41+
42+
// If the image is smaller than 512px, scale it up using NearestNeighbor
43+
// to maintain sharp pixels when displayed
44+
int smallerDimension = (int)Math.Min(image.Width, image.Height);
45+
if (smallerDimension is < 512 and > 0)
46+
{
47+
// Scale up to 512px using NearestNeighbor (point sampling) to keep pixels sharp
48+
int targetSize = 512;
49+
image.FilterType = FilterType.Point; // Point filter = NearestNeighbor
50+
image.Resize((uint)targetSize, (uint)targetSize);
51+
}
52+
53+
return image;
54+
}
55+
catch
56+
{
57+
return null;
58+
}
59+
}
60+
61+
public static int GetSmallerImageSide(string imagePath)
62+
{
63+
try
64+
{
65+
MagickImage image = new(imagePath);
66+
return (int)Math.Min(image.Width, image.Height);
67+
}
68+
catch
69+
{
70+
return 0;
71+
}
72+
}
73+
74+
public static async Task<string> ApplyGrayscaleAsync(string imagePath, Image? displayImage = null)
75+
{
76+
StorageFolder sf = ApplicationData.Current.LocalCacheFolder;
77+
string fileName = Path.GetFileNameWithoutExtension(imagePath);
78+
string extension = Path.GetExtension(imagePath);
79+
string grayFilePath = Path.Combine(sf.Path, $"{fileName}_gray{extension}");
80+
MagickImage image = new(imagePath);
81+
82+
image.Grayscale();
83+
await image.WriteAsync(grayFilePath);
84+
85+
if (displayImage != null)
86+
displayImage.Source = image.ToImageSource();
87+
88+
return grayFilePath;
89+
}
90+
91+
public static async Task<string> ApplyBlackWhiteOtsuAsync(string imagePath, Image? displayImage = null)
92+
{
93+
StorageFolder sf = ApplicationData.Current.LocalCacheFolder;
94+
string fileName = Path.GetFileNameWithoutExtension(imagePath);
95+
string extension = Path.GetExtension(imagePath);
96+
string bwFilePath = Path.Combine(sf.Path, $"{fileName}_bw{extension}");
97+
MagickImage image = new(imagePath);
98+
99+
image.Grayscale();
100+
image.AutoThreshold(AutoThresholdMethod.OTSU);
101+
await image.WriteAsync(bwFilePath);
102+
103+
if (displayImage != null)
104+
displayImage.Source = image.ToImageSource();
105+
106+
return bwFilePath;
107+
}
108+
109+
public static async Task<string> ApplyBlackWhiteKapurAsync(string imagePath, Image? displayImage = null)
110+
{
111+
StorageFolder sf = ApplicationData.Current.LocalCacheFolder;
112+
string fileName = Path.GetFileNameWithoutExtension(imagePath);
113+
string extension = Path.GetExtension(imagePath);
114+
string bwkFilePath = Path.Combine(sf.Path, $"{fileName}_bwk{extension}");
115+
MagickImage image = new(imagePath);
116+
117+
image.Grayscale();
118+
image.AutoThreshold(AutoThresholdMethod.Kapur);
119+
await image.WriteAsync(bwkFilePath);
120+
121+
if (displayImage != null)
122+
displayImage.Source = image.ToImageSource();
123+
124+
return bwkFilePath;
125+
}
126+
127+
public static async Task<string> ApplyInvertAsync(string imagePath, Image? displayImage = null)
128+
{
129+
StorageFolder sf = ApplicationData.Current.LocalCacheFolder;
130+
string fileName = Path.GetFileNameWithoutExtension(imagePath);
131+
string extension = Path.GetExtension(imagePath);
132+
string invFilePath = Path.Combine(sf.Path, $"{fileName}_inv{extension}");
133+
MagickImage image = new(imagePath);
134+
135+
image.Negate(Channels.RGB);
136+
await image.WriteAsync(invFilePath);
137+
138+
if (displayImage != null)
139+
displayImage.Source = image.ToImageSource();
140+
141+
return invFilePath;
142+
}
143+
}

Simple Icon File Maker/Simple Icon File Maker/Helpers/RuntimeHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public static bool IsMSIX
1212
{
1313
get
1414
{
15-
var length = 0;
15+
int length = 0;
1616

1717
return GetCurrentPackageFullName(ref length, null) != 15700L;
1818
}

0 commit comments

Comments
 (0)