Skip to content

Commit 70733f1

Browse files
committed
Implement save&restore of window placement and columns width.
1 parent 79d7d2e commit 70733f1

8 files changed

Lines changed: 199 additions & 18 deletions

File tree

App.config

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@
99
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
1010
<bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" />
1111
</dependentAssembly>
12+
<dependentAssembly>
13+
<assemblyIdentity name="JetBrains.Annotations" publicKeyToken="1010a0d8d6380325" culture="neutral" />
14+
<bindingRedirect oldVersion="0.0.0.0-2020.1.0.0" newVersion="2020.1.0.0" />
15+
</dependentAssembly>
16+
<dependentAssembly>
17+
<assemblyIdentity name="IX.StandardExtensions" publicKeyToken="029c2b9bf120ba32" culture="neutral" />
18+
<bindingRedirect oldVersion="0.0.0.0-0.5.5.0" newVersion="0.5.5.0" />
19+
</dependentAssembly>
1220
</assemblyBinding>
1321
</runtime>
1422
</configuration>

PipeExplorer.csproj

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,33 @@
4949
<Reference Include="DynamicData, Version=6.16.0.0, Culture=neutral, processorArchitecture=MSIL">
5050
<HintPath>packages\DynamicData.6.16.6\lib\net461\DynamicData.dll</HintPath>
5151
</Reference>
52+
<Reference Include="IX.Abstractions, Version=0.5.3.0, Culture=neutral, PublicKeyToken=029c2b9bf120ba32, processorArchitecture=MSIL">
53+
<HintPath>packages\IX.Abstractions.0.5.3\lib\net472\IX.Abstractions.dll</HintPath>
54+
</Reference>
55+
<Reference Include="IX.Abstractions.Collections, Version=0.5.3.0, Culture=neutral, PublicKeyToken=029c2b9bf120ba32, processorArchitecture=MSIL">
56+
<HintPath>packages\IX.Abstractions.Collections.0.5.3\lib\net472\IX.Abstractions.Collections.dll</HintPath>
57+
</Reference>
58+
<Reference Include="IX.Abstractions.Threading, Version=0.5.3.0, Culture=neutral, PublicKeyToken=029c2b9bf120ba32, processorArchitecture=MSIL">
59+
<HintPath>packages\IX.Abstractions.Threading.0.5.3\lib\net472\IX.Abstractions.Threading.dll</HintPath>
60+
</Reference>
61+
<Reference Include="IX.Observable, Version=0.5.3.0, Culture=neutral, PublicKeyToken=029c2b9bf120ba32, processorArchitecture=MSIL">
62+
<HintPath>packages\IX.Observable.0.5.3\lib\net472\IX.Observable.dll</HintPath>
63+
</Reference>
64+
<Reference Include="IX.StandardExtensions, Version=0.5.5.0, Culture=neutral, PublicKeyToken=029c2b9bf120ba32, processorArchitecture=MSIL">
65+
<HintPath>packages\IX.StandardExtensions.0.5.5\lib\net472\IX.StandardExtensions.dll</HintPath>
66+
</Reference>
67+
<Reference Include="IX.StandardExtensions.ComponentModel, Version=0.5.3.0, Culture=neutral, PublicKeyToken=029c2b9bf120ba32, processorArchitecture=MSIL">
68+
<HintPath>packages\IX.StandardExtensions.ComponentModel.0.5.3\lib\net472\IX.StandardExtensions.ComponentModel.dll</HintPath>
69+
</Reference>
70+
<Reference Include="IX.StandardExtensions.Threading, Version=0.5.3.0, Culture=neutral, PublicKeyToken=029c2b9bf120ba32, processorArchitecture=MSIL">
71+
<HintPath>packages\IX.StandardExtensions.Threading.0.5.3\lib\net472\IX.StandardExtensions.Threading.dll</HintPath>
72+
</Reference>
73+
<Reference Include="IX.Undoable, Version=0.5.3.0, Culture=neutral, PublicKeyToken=029c2b9bf120ba32, processorArchitecture=MSIL">
74+
<HintPath>packages\IX.Undoable.0.5.3\lib\net472\IX.Undoable.dll</HintPath>
75+
</Reference>
76+
<Reference Include="JetBrains.Annotations, Version=2020.1.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
77+
<HintPath>packages\JetBrains.Annotations.2020.1.0\lib\net20\JetBrains.Annotations.dll</HintPath>
78+
</Reference>
5279
<Reference Include="MahApps.Metro, Version=2.0.0.0, Culture=neutral, PublicKeyToken=51482d6f650b2b3f, processorArchitecture=MSIL">
5380
<HintPath>packages\MahApps.Metro.2.2.0\lib\net47\MahApps.Metro.dll</HintPath>
5481
</Reference>
@@ -81,17 +108,27 @@
81108
<HintPath>packages\Splat.9.5.20\lib\net461\Splat.dll</HintPath>
82109
</Reference>
83110
<Reference Include="System" />
111+
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
112+
<HintPath>packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
113+
</Reference>
84114
<Reference Include="System.ComponentModel.DataAnnotations" />
85115
<Reference Include="System.Configuration" />
86116
<Reference Include="System.Data" />
87117
<Reference Include="System.Drawing" />
118+
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
119+
<HintPath>packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath>
120+
</Reference>
88121
<Reference Include="System.Numerics" />
122+
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
123+
<HintPath>packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
124+
</Reference>
89125
<Reference Include="System.Reactive, Version=4.4.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
90126
<HintPath>packages\System.Reactive.4.4.1\lib\net46\System.Reactive.dll</HintPath>
91127
</Reference>
92128
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
93129
<HintPath>packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
94130
</Reference>
131+
<Reference Include="System.Runtime.Serialization" />
95132
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
96133
<HintPath>packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
97134
</Reference>

PipeExplorerMainWindow.xaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,10 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -->
124124
<ListView Grid.Row="1" ItemsSource="{Binding Pipes}">
125125
<ListView.View>
126126
<GridView AllowsColumnReorder="True">
127-
<GridViewColumn DisplayMemberBinding="{Binding Name}" Header="{x:Static res:Resources.PipeListHeaderName}" />
128-
<GridViewColumn DisplayMemberBinding="{Binding Connections}" Header="{x:Static res:Resources.PipeListHeaderConnections}" />
129-
<GridViewColumn DisplayMemberBinding="{Binding Created}" Header="{x:Static res:Resources.PipeListHeaderCreated}" />
130-
<GridViewColumn DisplayMemberBinding="{Binding Hint}" Header="{x:Static res:Resources.PipeListHeaderComment}" />
127+
<GridViewColumn x:Name="ColumnName" DisplayMemberBinding="{Binding Name}" Header="{x:Static res:Resources.PipeListHeaderName}" />
128+
<GridViewColumn x:Name="ColumnConnections" DisplayMemberBinding="{Binding Connections}" Header="{x:Static res:Resources.PipeListHeaderConnections}" />
129+
<GridViewColumn x:Name="ColumnCreated" DisplayMemberBinding="{Binding Created}" Header="{x:Static res:Resources.PipeListHeaderCreated}" />
130+
<GridViewColumn x:Name="ColumnHint" DisplayMemberBinding="{Binding Hint}" Header="{x:Static res:Resources.PipeListHeaderComment}" />
131131
</GridView>
132132
</ListView.View>
133133
<ListBox.ItemContainerStyle>

PipeExplorerMainWindow.xaml.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
*/
1717
#endregion
1818

19+
using System;
20+
using System.Windows;
21+
using PipeExplorer.Services;
22+
using Splat;
23+
1924
namespace PipeExplorer
2025
{
2126
/// <summary>
@@ -26,6 +31,64 @@ public partial class PipeExplorerMainWindow
2631
public PipeExplorerMainWindow()
2732
{
2833
InitializeComponent();
34+
35+
var settings = Locator.Current.GetService<ISettings>();
36+
// settings are loaded by our viewmodel already
37+
this.WindowState = settings.WindowState;
38+
if (WindowState == WindowState.Normal)
39+
{
40+
var rect = settings.WindowPosition;
41+
if (rect.Width > 0 && rect.Height > 0)
42+
{
43+
if (rect.Width > SystemParameters.VirtualScreenWidth)
44+
rect.Width = SystemParameters.VirtualScreenWidth;
45+
if (rect.Height > SystemParameters.VirtualScreenHeight)
46+
rect.Height = SystemParameters.VirtualScreenHeight;
47+
48+
if (rect.Right > SystemParameters.VirtualScreenWidth)
49+
rect.Offset(SystemParameters.VirtualScreenWidth - rect.Right, 0);
50+
else if (rect.Left < SystemParameters.VirtualScreenLeft)
51+
rect.Offset(SystemParameters.VirtualScreenLeft - rect.Left, 0);
52+
53+
if (rect.Bottom > SystemParameters.VirtualScreenHeight)
54+
rect.Offset(0, SystemParameters.VirtualScreenWidth - rect.Bottom);
55+
else if (rect.Top < SystemParameters.VirtualScreenTop)
56+
rect.Offset(0, SystemParameters.VirtualScreenTop - rect.Top);
57+
58+
Width = rect.Width;
59+
Height = rect.Height;
60+
Left = rect.Left;
61+
Top = rect.Top;
62+
}
63+
}
64+
65+
int width;
66+
if (settings.ColumnWidths.TryGetValue("Name", out width) && width >= 0)
67+
ColumnName.Width = width;
68+
if (settings.ColumnWidths.TryGetValue("Connections", out width) && width >= 0)
69+
ColumnConnections.Width = width;
70+
if (settings.ColumnWidths.TryGetValue("Created", out width) && width >= 0)
71+
ColumnCreated.Width = width;
72+
if (settings.ColumnWidths.TryGetValue("Hint", out width) && width >= 0)
73+
ColumnHint.Width = width;
74+
}
75+
76+
protected override void OnClosed(EventArgs e)
77+
{
78+
var settings = Locator.Current.GetService<ISettings>();
79+
80+
settings.WindowState = WindowState;
81+
if (WindowState == WindowState.Normal)
82+
settings.WindowPosition = new Rect(Left, Top, Width, Height);
83+
84+
settings.ColumnWidths.Clear();
85+
settings.ColumnWidths["Name"] = (int)ColumnName.ActualWidth;
86+
settings.ColumnWidths["Connections"] = (int)ColumnConnections.ActualWidth;
87+
settings.ColumnWidths["Created"] = (int)ColumnCreated.ActualWidth;
88+
settings.ColumnWidths["Hint"] = (int)ColumnHint.ActualWidth;
89+
90+
settings.Save();
91+
base.OnClosed(e);
2992
}
3093
}
3194
}

Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,5 @@
5151
// You can specify all the values or you can default the Build and Revision Numbers
5252
// by using the '*' as shown below:
5353
// [assembly: AssemblyVersion("1.0.*")]
54-
[assembly: AssemblyVersion("1.0.0.0")]
55-
[assembly: AssemblyFileVersion("1.0.0.0")]
54+
[assembly: AssemblyVersion("1.1.0.0")]
55+
[assembly: AssemblyFileVersion("1.1.0.0")]

Services/Settings.cs

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,18 @@
1717
#endregion
1818

1919
using System;
20+
using System.Collections;
21+
using System.Collections.Generic;
22+
using System.Collections.Specialized;
2023
using System.IO;
24+
using System.Linq;
25+
using System.Reactive.Linq;
2126
using System.Security;
27+
using System.Text.RegularExpressions;
28+
using System.Windows;
29+
using DynamicData;
30+
using DynamicData.Binding;
31+
using IX.Observable;
2232
using MahApps.Metro.Controls;
2333
using MahApps.Metro.Controls.Dialogs;
2434
using Microsoft.Win32;
@@ -34,32 +44,45 @@ interface ISettings : IReactiveObject
3444
TimeSpan HighlightDuration { get; set; }
3545
TimeSpan RefreshInterval { get; set; }
3646
bool StartImmediately { get; set; }
47+
WindowState WindowState { get; set; }
48+
Rect WindowPosition { get; set; }
49+
IDictionary<string, int> ColumnWidths { get; }
3750

38-
void Load();
3951
void Save();
4052
}
4153

4254
class Settings : ReactiveValidationObject<Settings>, ISettings
4355
{
4456
private const string regPath = @"SOFTWARE\PipeExplorer";
4557

46-
private bool isLoading;
47-
4858
[Reactive] public TimeSpan HighlightDuration { get; set; } = TimeSpan.FromSeconds(4); // sec
4959
[Reactive] public TimeSpan RefreshInterval { get; set; } = TimeSpan.FromSeconds(1); // sec
5060
[Reactive] public bool StartImmediately { get; set; } = true;
61+
[Reactive] public WindowState WindowState { get; set; } = WindowState.Normal;
62+
[Reactive] public Rect WindowPosition { get; set; }
63+
public ObservableDictionary<string, int> ColumnWidths { get; } = new ObservableDictionary<string, int>();
64+
65+
IDictionary<string, int> ISettings.ColumnWidths => ColumnWidths;
5166

5267
public Settings()
5368
{
5469
this.ValidationRule(x => x.HighlightDuration, duration => duration.Ticks >= 0, Properties.Resources.ErrorMustBeNonNegative);
5570
this.ValidationRule(x => x.RefreshInterval, interval => interval.Ticks > 0, Properties.Resources.ErrorMustBePositive);
5671

57-
PropertyChanged += delegate { if (!isLoading) Save(); };
72+
Load();
73+
74+
ColumnWidths.ToObservableChangeSet<ObservableDictionary<string, int>, KeyValuePair<string, int>>()
75+
.CastToObject()
76+
.Concat(this.WhenAnyPropertyChanged().ToObservableChangeSet().CastToObject())
77+
.Throttle(TimeSpan.FromSeconds(1))
78+
.Subscribe(_ => Save());
5879
}
5980

60-
public void Load()
81+
private static readonly Regex windowPosRegex = new Regex(@"^\s*([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s*$");
82+
private static readonly Regex colSpecRegex = new Regex(@"^(\w+)=(\d+)$");
83+
84+
private void Load()
6185
{
62-
isLoading = true;
6386
try
6487
{
6588
var regKey = Registry.CurrentUser.OpenSubKey(regPath) ?? Registry.LocalMachine.OpenSubKey(regPath);
@@ -75,15 +98,44 @@ public void Load()
7598
RefreshInterval = TimeSpan.FromSeconds(interval);
7699
if (regKey.GetValue("Start immediately", 1) is int n)
77100
StartImmediately = n != 0;
101+
if (regKey.GetValue("Window state") is string stateStr && Enum.TryParse<WindowState>(stateStr, true, out var state))
102+
WindowState = state;
103+
104+
if (WindowState == WindowState.Normal && regKey.GetValue("Window position") is string posStr)
105+
{
106+
var match = windowPosRegex.Match(posStr);
107+
if (match.Success)
108+
{
109+
var leftStr = match.Groups[1].Value;
110+
var topStr = match.Groups[2].Value;
111+
var widthStr = match.Groups[3].Value;
112+
var heightStr = match.Groups[4].Value;
113+
if (int.TryParse(leftStr, out var left) &&
114+
int.TryParse(topStr, out var top) &&
115+
int.TryParse(widthStr, out var width) && width > 0 &&
116+
int.TryParse(heightStr, out var height) && height > 0)
117+
{
118+
WindowPosition = new Rect(left, top, width, height);
119+
}
120+
}
121+
}
122+
123+
ColumnWidths.Clear();
124+
if (regKey.GetValue("Columns") is string colStr && !string.IsNullOrWhiteSpace(colStr))
125+
{
126+
var colSpecs = colStr.Split(" \t".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
127+
foreach (var spec in colSpecs)
128+
{
129+
var match = colSpecRegex.Match(spec);
130+
if (match.Success && int.TryParse(match.Groups[2].Value, out var width))
131+
ColumnWidths.Add(match.Groups[1].Value, width);
132+
}
133+
}
78134
}
79135
}
80136
catch (SecurityException) { }
81137
catch (UnauthorizedAccessException) { }
82138
catch (IOException) { }
83-
finally
84-
{
85-
isLoading = false;
86-
}
87139
}
88140

89141
public async void Save()
@@ -101,6 +153,17 @@ public async void Save()
101153
regKey.SetValue("Highlight duration", (int)HighlightDuration.TotalSeconds, RegistryValueKind.DWord);
102154
regKey.SetValue("Refresh interval", (int)RefreshInterval.TotalSeconds, RegistryValueKind.DWord);
103155
regKey.SetValue("Start immediately", StartImmediately, RegistryValueKind.DWord);
156+
regKey.SetValue("Window state", WindowState.ToString(), RegistryValueKind.String);
157+
158+
if (WindowState == WindowState.Normal)
159+
regKey.SetValue("Window position", $"{(int)WindowPosition.Left} {(int)WindowPosition.Top} {(int)WindowPosition.Width} {(int)WindowPosition.Height}", RegistryValueKind.String);
160+
else
161+
regKey.DeleteValue("Window position", false);
162+
163+
if (ColumnWidths.Count > 0)
164+
regKey.SetValue("Columns", string.Join(" ", ColumnWidths.Select(kv => $"{kv.Key}={kv.Value}")), RegistryValueKind.String);
165+
else
166+
regKey.DeleteValue("Columns", false);
104167
}
105168
}
106169
catch (SecurityException sex)

ViewModels/PipeExplorerViewModel.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,6 @@ static PipeExplorerViewModel()
120120

121121
public PipeExplorerViewModel()
122122
{
123-
settings.Load();
124-
125123
pipeWatcher = Locator.Current.GetService<IPipeWatcher>();
126124
pipeWatcher.RefreshInterval = settings.RefreshInterval;
127125
pipeWatcher.Created += Pipe_Created;

packages.config

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@
33
<package id="ControlzEx" version="4.3.2" targetFramework="net48" />
44
<package id="DynamicData" version="6.16.6" targetFramework="net48" />
55
<package id="Fody" version="6.2.4" targetFramework="net48" developmentDependency="true" />
6+
<package id="IX.Abstractions" version="0.5.3" targetFramework="net48" />
7+
<package id="IX.Abstractions.Collections" version="0.5.3" targetFramework="net48" />
8+
<package id="IX.Abstractions.Threading" version="0.5.3" targetFramework="net48" />
9+
<package id="IX.Observable" version="0.5.3" targetFramework="net48" />
10+
<package id="IX.StandardExtensions" version="0.5.5" targetFramework="net48" />
11+
<package id="IX.StandardExtensions.ComponentModel" version="0.5.3" targetFramework="net48" />
12+
<package id="IX.StandardExtensions.Threading" version="0.5.3" targetFramework="net48" />
13+
<package id="IX.Undoable" version="0.5.3" targetFramework="net48" />
14+
<package id="JetBrains.Annotations" version="2020.1.0" targetFramework="net48" />
615
<package id="MahApps.Metro" version="2.2.0" targetFramework="net48" />
716
<package id="Microsoft.Xaml.Behaviors.Wpf" version="1.1.19" targetFramework="net48" />
817
<package id="PInvoke.AdvApi32" version="0.6.49" targetFramework="net48" />
@@ -13,6 +22,9 @@
1322
<package id="ReactiveUI.Validation" version="1.5.5" targetFramework="net48" />
1423
<package id="ReactiveUI.WPF" version="11.5.26" targetFramework="net48" />
1524
<package id="Splat" version="9.5.20" targetFramework="net48" />
25+
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
26+
<package id="System.Memory" version="4.5.4" targetFramework="net48" />
27+
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
1628
<package id="System.Reactive" version="4.4.1" targetFramework="net48" />
1729
<package id="System.Runtime.CompilerServices.Unsafe" version="4.7.1" targetFramework="net48" />
1830
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net48" />

0 commit comments

Comments
 (0)