/
valzem
/
MatControl
Обзор
Документация
Войти
/
valzem
/
MatControl
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/MatControl.UI/GridLayoutPersister.cs
326 строк
8 KB
MatControl Dev
Сортировка и поиск в списках, статусы в Движении, импорт с выбором файла, поле Отдел, скиллы
07 авг 2026, 16:45
07 авг 2026, 16:45
4bdbbb6
Код
Авторство
О чём код?
using System.Runtime.CompilerServices; using System.Text.Json; using MatControl.Data; namespace MatControl.UI; public static class GridLayoutPersister { private sealed class GridState { public string Key = string.Empty; public System.Windows.Forms.Timer? ResizeTimer; } private static readonly ConditionalWeakTable<DataGridView, GridState> GridKeys = new(); private static readonly Dictionary<string, Dictionary<string, int>> Layouts = new(StringComparer.OrdinalIgnoreCase); private static readonly object Sync = new(); private static readonly System.Windows.Forms.Timer SaveTimer; private static bool _dirty; private static bool _restoring; private static string SettingsPath => Path.Combine(DataPaths.GetDataDirectory(), "ui-settings.json"); static GridLayoutPersister() { SaveTimer = new System.Windows.Forms.Timer { Interval = 400 }; SaveTimer.Tick += (s, e) => { SaveTimer.Stop(); Flush(); }; Application.ApplicationExit += (s, e) => Flush(); Load(); } public static void Restore(DataGridView grid, string key) { var state = GridKeys.GetOrCreateValue(grid); state.Key = key; grid.ColumnWidthChanged -= Grid_ColumnWidthChanged; grid.ColumnWidthChanged += Grid_ColumnWidthChanged; grid.VisibleChanged -= Grid_VisibleChanged; grid.VisibleChanged += Grid_VisibleChanged; grid.Resize -= Grid_Resize; grid.Resize += Grid_Resize; ApplyLayout(grid, state); } public static void ApplyAll(Control root) { foreach (var grid in EnumerateDataGridViews(root)) { if (GridKeys.TryGetValue(grid, out var state) && !string.IsNullOrEmpty(state.Key)) { ApplyLayout(grid, state); } } } private static IEnumerable<DataGridView> EnumerateDataGridViews(Control control) { foreach (Control child in control.Controls) { if (child is DataGridView grid) { yield return grid; } foreach (var nested in EnumerateDataGridViews(child)) { yield return nested; } } } private static void Grid_VisibleChanged(object? sender, EventArgs e) { if (sender is not DataGridView grid || !grid.Visible) { return; } if (GridKeys.TryGetValue(grid, out var state)) { ApplyLayout(grid, state); } } private static void Grid_Resize(object? sender, EventArgs e) { if (sender is not DataGridView grid) { return; } if (!GridKeys.TryGetValue(grid, out var state)) { return; } lock (Sync) { if (Layouts.ContainsKey(state.Key)) { return; } } if (state.ResizeTimer is null) { state.ResizeTimer = new System.Windows.Forms.Timer { Interval = 150 }; state.ResizeTimer.Tick += (s, e2) => { state.ResizeTimer!.Stop(); ApplyLayout(grid, state); }; } state.ResizeTimer.Stop(); state.ResizeTimer.Start(); } private static void Grid_ColumnWidthChanged(object? sender, DataGridViewColumnEventArgs e) { if (_restoring || e.Column is null || sender is not DataGridView grid) { return; } if (!GridKeys.TryGetValue(grid, out var state) || string.IsNullOrEmpty(state.Key)) { return; } if (grid.AutoSizeColumnsMode != DataGridViewAutoSizeColumnsMode.None) { return; } lock (Sync) { var widths = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); foreach (DataGridViewColumn column in grid.Columns) { widths[column.Name] = column.Width; } Layouts[state.Key] = widths; } _dirty = true; if (!SaveTimer.Enabled) { SaveTimer.Start(); } } private static void ApplyLayout(DataGridView grid, GridState state) { if (grid.IsDisposed || grid.Disposing) { return; } if (grid.IsHandleCreated) { grid.BeginInvoke(new Action(() => ApplyLayoutCore(grid, state))); return; } ApplyLayoutCore(grid, state); } private static void ApplyLayoutCore(DataGridView grid, GridState state) { if (grid.IsDisposed || grid.Disposing) { return; } lock (Sync) { _restoring = true; try { if (Layouts.TryGetValue(state.Key, out var widths)) { if (grid.AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.Fill) { grid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None; } foreach (DataGridViewColumn column in grid.Columns) { if (widths.TryGetValue(column.Name, out var width)) { column.Width = width; } } return; } FitToContent(grid); } catch { } finally { _restoring = false; } } } private static void FitToContent(DataGridView grid) { var columns = grid.Columns.Cast<DataGridViewColumn>().ToList(); if (columns.Count == 0) { return; } if (grid.AutoSizeColumnsMode != DataGridViewAutoSizeColumnsMode.None) { grid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None; } var preferred = new int[columns.Count]; var total = 0; for (var i = 0; i < columns.Count; i++) { var width = columns[i].GetPreferredWidth(DataGridViewAutoSizeColumnMode.AllCells, false); if (width < 30) { width = 30; } preferred[i] = width; total += width; } var available = grid.ClientSize.Width; if (available <= 0) { available = grid.Parent is null ? total : grid.Parent.ClientSize.Width; } if (available > 0) { var rowHeight = Math.Max(1, grid.RowTemplate.Height); var maxRows = grid.ClientSize.Height > grid.ColumnHeadersHeight ? (grid.ClientSize.Height - grid.ColumnHeadersHeight) / rowHeight : 0; if (grid.Rows.Count > maxRows) { available -= SystemInformation.VerticalScrollBarWidth; } } var scale = available > 0 && total > available ? (double)available / total : 1.0; for (var i = 0; i < columns.Count; i++) { columns[i].Width = (int)(preferred[i] * scale); } } private static void Flush() { if (!_dirty) { return; } Save(); _dirty = false; } private static void Load() { try { var path = SettingsPath; if (!File.Exists(path)) { return; } var json = File.ReadAllText(path); var loaded = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, int>>>(json); if (loaded is null) { return; } lock (Sync) { Layouts.Clear(); foreach (var (key, widths) in loaded) { Layouts[key] = new Dictionary<string, int>(widths, StringComparer.OrdinalIgnoreCase); } } } catch { } } private static void Save() { try { lock (Sync) { var json = JsonSerializer.Serialize(Layouts); File.WriteAllText(SettingsPath, json); } } catch { } } }