Skip to content

Commit a08ae89

Browse files
committed
feat(QR/barcode): Add page state persistence function
- Implement the saving and restoration of QR code and barcode page generation results - Store the image in Base64 format in the local state file - Modify the button layout on the URL encoding and decoding page, replacing icons with text labels
1 parent 6338a04 commit a08ae89

4 files changed

Lines changed: 140 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
44

55
---
66

7+
## [1.2.0] - 2025-03-20
8+
9+
### Added
10+
- QR Code and Barcode page state persistence
11+
- Generated results are now saved and restored on app restart
12+
- Images stored as Base64 in local state file
13+
14+
### Changed
15+
- URL Encode/Decode page button layout
16+
- Button order changed to: URLEncode | URLDecode | Copy
17+
- Buttons now use text labels instead of icons for better clarity
18+
19+
---
20+
721
## [1.1.0] - 2025-03-12
822

923
### Added

Pages/BarcodePage.xaml.cs

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,30 @@ private void SaveState()
4343
{
4444
{ "InputText", InputText.Text ?? string.Empty }
4545
};
46+
47+
var logsData = new List<Dictionary<string, string>>();
48+
foreach (var entry in _logs)
49+
{
50+
if (entry.Bitmap != null)
51+
{
52+
using var ms = new MemoryStream();
53+
entry.Bitmap.Save(ms, ImageFormat.Png);
54+
var base64 = Convert.ToBase64String(ms.ToArray());
55+
logsData.Add(new Dictionary<string, string>
56+
{
57+
{ "Text", entry.Text ?? string.Empty },
58+
{ "Timestamp", entry.Timestamp.ToString("O") },
59+
{ "ImageBase64", base64 },
60+
{ "IsImageVisible", entry.IsImageVisible.ToString() }
61+
});
62+
}
63+
}
64+
65+
if (logsData.Count > 0)
66+
{
67+
state["Logs"] = System.Text.Json.JsonSerializer.Serialize(logsData);
68+
}
69+
4670
PageStateManager.SavePageState(this, state);
4771
}
4872

@@ -52,6 +76,44 @@ private void LoadState()
5276
if (state != null)
5377
{
5478
InputText.Text = state.GetValueOrDefault("InputText", string.Empty);
79+
80+
if (state.TryGetValue("Logs", out var logsJson) && !string.IsNullOrEmpty(logsJson))
81+
{
82+
try
83+
{
84+
var logsData = System.Text.Json.JsonSerializer.Deserialize<List<Dictionary<string, string>>>(logsJson);
85+
if (logsData != null)
86+
{
87+
foreach (var logData in logsData)
88+
{
89+
var text = logData.GetValueOrDefault("Text", string.Empty);
90+
var timestampStr = logData.GetValueOrDefault("Timestamp", string.Empty);
91+
var base64 = logData.GetValueOrDefault("ImageBase64", string.Empty);
92+
var isVisibleStr = logData.GetValueOrDefault("IsImageVisible", "true");
93+
94+
if (DateTime.TryParse(timestampStr, out var timestamp) && !string.IsNullOrEmpty(base64))
95+
{
96+
var bytes = Convert.FromBase64String(base64);
97+
using var ms = new MemoryStream(bytes);
98+
var bitmap = new Bitmap(ms);
99+
var imageSource = BitmapToImageSource(bitmap);
100+
101+
var entry = new BarcodeLogEntry
102+
{
103+
Bitmap = bitmap,
104+
Image = imageSource,
105+
Text = text,
106+
Timestamp = timestamp,
107+
IsImageVisible = bool.Parse(isVisibleStr)
108+
};
109+
entry.TimestampString = entry.Timestamp.ToString("yyyy-MM-dd HH:mm:ss");
110+
_logs.Add(entry);
111+
}
112+
}
113+
}
114+
}
115+
catch { }
116+
}
55117
}
56118
}
57119

@@ -104,7 +166,6 @@ private void Generate_Click(object sender, RoutedEventArgs e)
104166

105167
entry.TimestampString = entry.Timestamp.ToString("yyyy-MM-dd HH:mm:ss");
106168

107-
// insert at beginning so newest appear first
108169
_logs.Insert(0, entry);
109170
}
110171
catch (Exception ex)
@@ -200,14 +261,12 @@ private BitmapSource BitmapToImageSource(Bitmap bitmap)
200261
}
201262
finally
202263
{
203-
// delete HBitmap to avoid memory leaks
204264
NativeMethods.DeleteObject(handle);
205265
}
206266
}
207267

208268
private void BarcodePage_Unloaded(object? sender, RoutedEventArgs e)
209269
{
210-
// dispose bitmaps in logs to free GDI resources
211270
foreach (var l in _logs)
212271
{
213272
try
@@ -247,7 +306,6 @@ internal static class NativeMethods
247306
internal static extern bool DeleteObject(IntPtr hObject);
248307
}
249308

250-
// converters used by XAML to preserve layout when hiding images
251309
public class BoolToHiddenVisibilityConverter : IValueConverter
252310
{
253311
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

Pages/QrPage.xaml.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,30 @@ private void SaveState()
4343
{
4444
{ "InputText", InputText.Text ?? string.Empty }
4545
};
46+
47+
var logsData = new List<Dictionary<string, string>>();
48+
foreach (var entry in _logs)
49+
{
50+
if (entry.Bitmap != null)
51+
{
52+
using var ms = new MemoryStream();
53+
entry.Bitmap.Save(ms, ImageFormat.Png);
54+
var base64 = Convert.ToBase64String(ms.ToArray());
55+
logsData.Add(new Dictionary<string, string>
56+
{
57+
{ "Text", entry.Text ?? string.Empty },
58+
{ "Timestamp", entry.Timestamp.ToString("O") },
59+
{ "ImageBase64", base64 },
60+
{ "IsImageVisible", entry.IsImageVisible.ToString() }
61+
});
62+
}
63+
}
64+
65+
if (logsData.Count > 0)
66+
{
67+
state["Logs"] = System.Text.Json.JsonSerializer.Serialize(logsData);
68+
}
69+
4670
PageStateManager.SavePageState(this, state);
4771
}
4872

@@ -52,6 +76,44 @@ private void LoadState()
5276
if (state != null)
5377
{
5478
InputText.Text = state.GetValueOrDefault("InputText", string.Empty);
79+
80+
if (state.TryGetValue("Logs", out var logsJson) && !string.IsNullOrEmpty(logsJson))
81+
{
82+
try
83+
{
84+
var logsData = System.Text.Json.JsonSerializer.Deserialize<List<Dictionary<string, string>>>(logsJson);
85+
if (logsData != null)
86+
{
87+
foreach (var logData in logsData)
88+
{
89+
var text = logData.GetValueOrDefault("Text", string.Empty);
90+
var timestampStr = logData.GetValueOrDefault("Timestamp", string.Empty);
91+
var base64 = logData.GetValueOrDefault("ImageBase64", string.Empty);
92+
var isVisibleStr = logData.GetValueOrDefault("IsImageVisible", "true");
93+
94+
if (DateTime.TryParse(timestampStr, out var timestamp) && !string.IsNullOrEmpty(base64))
95+
{
96+
var bytes = Convert.FromBase64String(base64);
97+
using var ms = new MemoryStream(bytes);
98+
var bitmap = new Bitmap(ms);
99+
var imageSource = BitmapToImageSource(bitmap);
100+
101+
var entry = new QrLogEntry
102+
{
103+
Bitmap = bitmap,
104+
Image = imageSource,
105+
Text = text,
106+
Timestamp = timestamp,
107+
IsImageVisible = bool.Parse(isVisibleStr)
108+
};
109+
entry.TimestampString = entry.Timestamp.ToString("yyyy-MM-dd HH:mm:ss");
110+
_logs.Add(entry);
111+
}
112+
}
113+
}
114+
}
115+
catch { }
116+
}
55117
}
56118
}
57119

Pages/UrlEncodePage.xaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@
3030

3131
<StackPanel Grid.Row="4" Orientation="Horizontal" Margin="0,10">
3232
<Button Click="Encode_Click" Width="100" Height="32" ToolTip="{x:Static resources:Strings.URLEncode}">
33-
<TextBlock FontFamily="{StaticResource FontAwesomeSolid}" Text="&#xf023;" FontSize="16" FontWeight="Bold" Foreground="#FFFFFF" />
33+
<TextBlock Text="{x:Static resources:Strings.URLEncode}" FontSize="12" FontWeight="Bold" Foreground="#FFFFFF" />
3434
</Button>
3535
<Button Click="Decode_Click" Margin="8,0,0,0" Width="100" Height="32" ToolTip="{x:Static resources:Strings.URLDecode}">
36-
<TextBlock FontFamily="{StaticResource FontAwesomeSolid}" Text="&#xf0c0;" FontSize="16" FontWeight="Bold" Foreground="#FFFFFF" />
36+
<TextBlock Text="{x:Static resources:Strings.URLDecode}" FontSize="12" FontWeight="Bold" Foreground="#FFFFFF" />
3737
</Button>
3838
<Button Click="CopyOutput_Click" Margin="8,0,0,0" Width="80" Height="32" ToolTip="{x:Static resources:Strings.Copy}">
3939
<TextBlock FontFamily="{StaticResource FontAwesomeSolid}" Text="&#xf0c5;" FontSize="16" FontWeight="Bold" Foreground="#FFFFFF" />

0 commit comments

Comments
 (0)