Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7465bb5

Browse files
committed
added new settings dialog + settings manager
1 parent74b8658 commit7465bb5

File tree

10 files changed

+349
-3
lines changed

10 files changed

+349
-3
lines changed

‎App/App.xaml.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ public App()
9090
// FileSyncListMainPage is created by FileSyncListWindow.
9191
services.AddTransient<FileSyncListWindow>();
9292

93+
services.AddSingleton<ISettingsManager>(_=>newSettingsManager("CoderDesktop"));
94+
// SettingsWindow views and view models
95+
services.AddTransient<SettingsViewModel>();
96+
// SettingsMainPage is created by SettingsWindow.
97+
services.AddTransient<SettingsWindow>();
98+
9399
// DirectoryPickerWindow views and view models are created by FileSyncListViewModel.
94100

95101
// TrayWindow views and view models

‎App/Services/SettingsManager.cs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
usingSystem;
2+
usingSystem.Collections.Generic;
3+
usingSystem.IO;
4+
usingSystem.Linq;
5+
usingSystem.Text;
6+
usingSystem.Text.Json;
7+
usingSystem.Threading.Tasks;
8+
9+
namespaceCoder.Desktop.App.Services;
10+
/// <summary>
11+
/// Generic persistence contract for simple key/value settings.
12+
/// </summary>
13+
publicinterfaceISettingsManager
14+
{
15+
/// <summary>
16+
/// Saves <paramref name="value"/> under <paramref name="name"/> and returns the value.
17+
/// </summary>
18+
TSave<T>(stringname,Tvalue);
19+
20+
/// <summary>
21+
/// Reads the setting or returns <paramref name="defaultValue"/> when the key is missing.
22+
/// </summary>
23+
TRead<T>(stringname,TdefaultValue);
24+
}
25+
/// <summary>
26+
/// JSON‑file implementation that works in unpackaged Win32/WinUI 3 apps.
27+
/// </summary>
28+
publicsealedclassSettingsManager:ISettingsManager
29+
{
30+
privatereadonlystring_settingsFilePath;
31+
privatereadonlystring_fileName="app-settings.json";
32+
privatereadonlyobject_lock=new();
33+
privateDictionary<string,JsonElement>_cache;
34+
35+
/// <param name="appName">
36+
/// Sub‑folder under %LOCALAPPDATA% (e.g. "coder-desktop").
37+
/// If <c>null</c> the folder name defaults to the executable name.
38+
/// For unit‑tests you can pass an absolute path that already exists.
39+
/// </param>
40+
publicSettingsManager(string?appName=null)
41+
{
42+
// Allow unit‑tests to inject a fully‑qualified path.
43+
if(appNameis notnull&&Path.IsPathRooted(appName))
44+
{
45+
_settingsFilePath=Path.Combine(appName,_fileName);
46+
Directory.CreateDirectory(Path.GetDirectoryName(_settingsFilePath)!);
47+
}
48+
else
49+
{
50+
stringfolder=Path.Combine(
51+
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
52+
appName??AppDomain.CurrentDomain.FriendlyName.ToLowerInvariant());
53+
Directory.CreateDirectory(folder);
54+
_settingsFilePath=Path.Combine(folder,_fileName);
55+
}
56+
57+
_cache=Load();
58+
}
59+
60+
publicTSave<T>(stringname,Tvalue)
61+
{
62+
lock(_lock)
63+
{
64+
_cache[name]=JsonSerializer.SerializeToElement(value);
65+
Persist();
66+
returnvalue;
67+
}
68+
}
69+
70+
publicTRead<T>(stringname,TdefaultValue)
71+
{
72+
lock(_lock)
73+
{
74+
if(_cache.TryGetValue(name,outvarelement))
75+
{
76+
try
77+
{
78+
returnelement.Deserialize<T>()??defaultValue;
79+
}
80+
catch
81+
{
82+
// Malformed value – fall back.
83+
returndefaultValue;
84+
}
85+
}
86+
returndefaultValue;// key not found – return caller‑supplied default (false etc.)
87+
}
88+
}
89+
90+
privateDictionary<string,JsonElement>Load()
91+
{
92+
if(!File.Exists(_settingsFilePath))
93+
returnnew();
94+
95+
try
96+
{
97+
usingvarfs=File.OpenRead(_settingsFilePath);
98+
returnJsonSerializer.Deserialize<Dictionary<string,JsonElement>>(fs)??new();
99+
}
100+
catch
101+
{
102+
// Corrupted file – start fresh.
103+
returnnew();
104+
}
105+
}
106+
107+
privatevoidPersist()
108+
{
109+
usingvarfs=File.Create(_settingsFilePath);
110+
varoptions=newJsonSerializerOptions{WriteIndented=true};
111+
JsonSerializer.Serialize(fs,_cache,options);
112+
}
113+
}

‎App/ViewModels/SettingsViewModel.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
usingSystem;
2+
usingCoder.Desktop.App.Services;
3+
usingCommunityToolkit.Mvvm.ComponentModel;
4+
usingCommunityToolkit.Mvvm.Input;
5+
usingMicrosoft.UI.Dispatching;
6+
usingMicrosoft.UI.Xaml;
7+
usingMicrosoft.UI.Xaml.Controls;
8+
9+
namespaceCoder.Desktop.App.ViewModels;
10+
11+
publicpartialclassSettingsViewModel:ObservableObject
12+
{
13+
privateWindow?_window;
14+
privateDispatcherQueue?_dispatcherQueue;
15+
16+
privateISettingsManager_settingsManager;
17+
18+
publicSettingsViewModel(ISettingsManagersettingsManager)
19+
{
20+
_settingsManager=settingsManager;
21+
}
22+
23+
publicvoidInitialize(Windowwindow,DispatcherQueuedispatcherQueue)
24+
{
25+
_window=window;
26+
_dispatcherQueue=dispatcherQueue;
27+
if(!_dispatcherQueue.HasThreadAccess)
28+
thrownewInvalidOperationException("Initialize must be called from the UI thread");
29+
}
30+
31+
[RelayCommand]
32+
privatevoidSaveSetting()
33+
{
34+
//_settingsManager.Save();
35+
}
36+
37+
[RelayCommand]
38+
privatevoidShowSettingsDialog()
39+
{
40+
if(_windowisnull||_dispatcherQueueisnull)
41+
thrownewInvalidOperationException("Initialize must be called before showing the settings dialog.");
42+
// Here you would typically open a settings dialog or page.
43+
// For example, you could navigate to a SettingsPage in your app.
44+
// This is just a placeholder for demonstration purposes.
45+
// Display MessageBox and show a message
46+
varmessage=$"Settings dialog opened. Current setting:{_settingsManager.Read("SomeSetting",false)}\n"+
47+
"You can implement your settings dialog here.";
48+
vardialog=newContentDialog();
49+
dialog.Title="Settings";
50+
dialog.Content=message;
51+
dialog.XamlRoot=_window.Content.XamlRoot;
52+
_=dialog.ShowAsync();
53+
}
54+
}

‎App/ViewModels/TrayWindowViewModel.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ public partial class TrayWindowViewModel : ObservableObject, IAgentExpanderHost
3939

4040
privateFileSyncListWindow?_fileSyncListWindow;
4141

42+
privateSettingsWindow?_settingsWindow;
43+
4244
privateDispatcherQueue?_dispatcherQueue;
4345

4446
// When we transition from 0 online workspaces to >0 online workspaces, the
@@ -359,6 +361,22 @@ private void ShowFileSyncListWindow()
359361
_fileSyncListWindow.Activate();
360362
}
361363

364+
[RelayCommand]
365+
privatevoidShowSettingsWindow()
366+
{
367+
// This is safe against concurrent access since it all happens in the
368+
// UI thread.
369+
if(_settingsWindow!=null)
370+
{
371+
_settingsWindow.Activate();
372+
return;
373+
}
374+
375+
_settingsWindow=_services.GetRequiredService<SettingsWindow>();
376+
_settingsWindow.Closed+=(_,_)=>_settingsWindow=null;
377+
_settingsWindow.Activate();
378+
}
379+
362380
[RelayCommand]
363381
privateasyncTaskSignOut()
364382
{

‎App/Views/Pages/SettingsMainPage.xaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<Page
4+
x:Class="Coder.Desktop.App.Views.Pages.SettingsMainPage"
5+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
6+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
7+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
8+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
9+
xmlns:viewmodels="using:Coder.Desktop.App.ViewModels"
10+
xmlns:converters="using:Coder.Desktop.App.Converters"
11+
mc:Ignorable="d"
12+
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
13+
14+
<Grid>
15+
<HyperlinkButton
16+
Command="{x:Bind ViewModel.ShowSettingsDialogCommand, Mode=OneWay}"
17+
HorizontalAlignment="Stretch"
18+
HorizontalContentAlignment="Left">
19+
20+
<TextBlockText="Show settings"Foreground="{ThemeResource DefaultTextForegroundThemeBrush}" />
21+
</HyperlinkButton>
22+
</Grid>
23+
</Page>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
usingCoder.Desktop.App.ViewModels;
2+
usingMicrosoft.UI.Xaml.Controls;
3+
4+
namespaceCoder.Desktop.App.Views.Pages;
5+
6+
publicsealedpartialclassSettingsMainPage:Page
7+
{
8+
publicSettingsViewModelViewModel;
9+
10+
publicSettingsMainPage(SettingsViewModelviewModel)
11+
{
12+
ViewModel=viewModel;// already initialized
13+
InitializeComponent();
14+
}
15+
}

‎App/Views/Pages/TrayWindowMainPage.xaml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
Orientation="Vertical"
2626
HorizontalAlignment="Stretch"
2727
VerticalAlignment="Top"
28-
Padding="20,20,20,30"
28+
Padding="20,20,20,20"
2929
Spacing="10">
3030

3131
<Grid>
@@ -331,9 +331,18 @@
331331

332332
<controls:HorizontalRule />
333333

334+
<HyperlinkButton
335+
Command="{x:Bind ViewModel.ShowSettingsWindowCommand, Mode=OneWay}"
336+
Margin="-12,-4,-12,-4"
337+
HorizontalAlignment="Stretch"
338+
HorizontalContentAlignment="Left">
339+
340+
<TextBlockText="Settings"Foreground="{ThemeResource DefaultTextForegroundThemeBrush}" />
341+
</HyperlinkButton>
342+
334343
<HyperlinkButton
335344
Command="{x:Bind ViewModel.SignOutCommand, Mode=OneWay}"
336-
Margin="-12,0"
345+
Margin="-12,-4,-12,-4"
337346
HorizontalAlignment="Stretch"
338347
HorizontalContentAlignment="Left">
339348

@@ -342,7 +351,7 @@
342351

343352
<HyperlinkButton
344353
Command="{x:Bind ViewModel.ExitCommand, Mode=OneWay}"
345-
Margin="-12,-8,-12,-5"
354+
Margin="-12,-4,-12,-4"
346355
HorizontalAlignment="Stretch"
347356
HorizontalContentAlignment="Left">
348357

‎App/Views/SettingsWindow.xaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<winuiex:WindowEx
4+
x:Class="Coder.Desktop.App.Views.SettingsWindow"
5+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
6+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
7+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
8+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
9+
xmlns:winuiex="using:WinUIEx"
10+
mc:Ignorable="d"
11+
Title="Coder Settings"
12+
Width="1000"Height="300"
13+
MinWidth="1000"MinHeight="300">
14+
15+
<Window.SystemBackdrop>
16+
<DesktopAcrylicBackdrop />
17+
</Window.SystemBackdrop>
18+
19+
<Framex:Name="RootFrame" />
20+
</winuiex:WindowEx>

‎App/Views/SettingsWindow.xaml.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
usingCoder.Desktop.App.Utils;
2+
usingCoder.Desktop.App.ViewModels;
3+
usingCoder.Desktop.App.Views.Pages;
4+
usingMicrosoft.UI.Xaml.Media;
5+
usingWinUIEx;
6+
7+
namespaceCoder.Desktop.App.Views;
8+
9+
publicsealedpartialclassSettingsWindow:WindowEx
10+
{
11+
publicreadonlySettingsViewModelViewModel;
12+
13+
publicSettingsWindow(SettingsViewModelviewModel)
14+
{
15+
ViewModel=viewModel;
16+
InitializeComponent();
17+
TitleBarIcon.SetTitlebarIcon(this);
18+
19+
SystemBackdrop=newDesktopAcrylicBackdrop();
20+
21+
ViewModel.Initialize(this,DispatcherQueue);
22+
RootFrame.Content=newSettingsMainPage(ViewModel);
23+
24+
this.CenterOnScreen();
25+
}
26+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp