Skip to content

Commit c7f68c3

Browse files
committed
Add Data Binding demo window and update README with MVVM support
1 parent e3ae96f commit c7f68c3

3 files changed

Lines changed: 211 additions & 0 deletions

File tree

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
using System.ComponentModel;
2+
using System.Runtime.CompilerServices;
3+
using SharpConsoleUI;
4+
using SharpConsoleUI.Builders;
5+
using SharpConsoleUI.Controls;
6+
using SharpConsoleUI.DataBinding;
7+
using SharpConsoleUI.Layout;
8+
9+
namespace DemoApp.DemoWindows;
10+
11+
internal static class DataBindingWindow
12+
{
13+
// Simple ViewModel — standard .NET INotifyPropertyChanged, nothing framework-specific
14+
private class SystemMonitorVM : INotifyPropertyChanged
15+
{
16+
public event PropertyChangedEventHandler? PropertyChanged;
17+
18+
private double _cpuUsage;
19+
private double _memoryUsage;
20+
private double _networkKBps;
21+
private string _statusText = "Initializing...";
22+
private bool _monitoringEnabled = true;
23+
private int _updateCount;
24+
25+
public double CpuUsage
26+
{
27+
get => _cpuUsage;
28+
set { _cpuUsage = value; OnPropertyChanged(); }
29+
}
30+
31+
public double MemoryUsage
32+
{
33+
get => _memoryUsage;
34+
set { _memoryUsage = value; OnPropertyChanged(); }
35+
}
36+
37+
public double NetworkKBps
38+
{
39+
get => _networkKBps;
40+
set { _networkKBps = value; OnPropertyChanged(); }
41+
}
42+
43+
public string StatusText
44+
{
45+
get => _statusText;
46+
set { _statusText = value; OnPropertyChanged(); }
47+
}
48+
49+
public bool MonitoringEnabled
50+
{
51+
get => _monitoringEnabled;
52+
set { _monitoringEnabled = value; OnPropertyChanged(); }
53+
}
54+
55+
public int UpdateCount
56+
{
57+
get => _updateCount;
58+
set { _updateCount = value; OnPropertyChanged(); }
59+
}
60+
61+
private void OnPropertyChanged([CallerMemberName] string? name = null)
62+
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
63+
}
64+
65+
public static Window Create(ConsoleWindowSystem ws)
66+
{
67+
var vm = new SystemMonitorVM();
68+
69+
// --- Header ---
70+
var header = Controls.Header("MVVM Data Binding Demo", "cyan");
71+
72+
var description = Controls.Markup()
73+
.AddLine("[dim]ViewModel properties drive all controls below via Bind/BindTwoWay.[/]")
74+
.AddLine("[dim]No manual control updates — bindings handle everything.[/]")
75+
.Build();
76+
77+
var rule1 = Controls.Rule("One-Way Bindings (VM → Control)");
78+
79+
// --- One-way: VM.CpuUsage → ProgressBar.Value ---
80+
var cpuBar = Controls.ProgressBar()
81+
.WithHeader("CPU")
82+
.Stretch()
83+
.ShowPercentage()
84+
.WithFilledColor(Color.Green)
85+
.Build()
86+
.Bind(vm, v => v.CpuUsage, c => c.Value);
87+
88+
// --- One-way: VM.MemoryUsage → ProgressBar.Value ---
89+
var memBar = Controls.ProgressBar()
90+
.WithHeader("Memory")
91+
.Stretch()
92+
.ShowPercentage()
93+
.WithFilledColor(Color.DodgerBlue1)
94+
.Build()
95+
.Bind(vm, v => v.MemoryUsage, c => c.Value);
96+
97+
// --- One-way with converter: VM.NetworkKBps → MarkupControl.Text ---
98+
var networkLabel = Controls.Markup()
99+
.Centered()
100+
.Build()
101+
.Bind(vm, v => v.NetworkKBps, c => c.Text,
102+
v => $"[bold yellow]Network:[/] {v:F1} KB/s");
103+
104+
// --- One-way with converter: VM.UpdateCount → MarkupControl.Text ---
105+
var counterLabel = Controls.Markup()
106+
.Centered()
107+
.Build()
108+
.Bind(vm, v => v.UpdateCount, c => c.Text,
109+
v => $"[dim]Updates received: {v}[/]");
110+
111+
// --- One-way: VM.StatusText → MarkupControl.Text ---
112+
var statusLabel = Controls.Markup()
113+
.Centered()
114+
.Build()
115+
.Bind(vm, v => v.StatusText, c => c.Text);
116+
117+
var rule2 = Controls.Rule("Two-Way Binding (VM ↔ Control)");
118+
119+
// --- Two-way: VM.MonitoringEnabled ↔ Checkbox.Checked ---
120+
var enabledCheckbox = Controls.Checkbox("Monitoring Enabled")
121+
.Build();
122+
enabledCheckbox.BindTwoWay(vm, v => v.MonitoringEnabled, c => c.Checked);
123+
124+
// Show the VM state driven by the checkbox
125+
var checkboxStatus = Controls.Markup()
126+
.Build()
127+
.Bind(vm, v => v.MonitoringEnabled, c => c.Text,
128+
v => v ? "[green] VM.MonitoringEnabled = true[/] [dim](uncheck to pause updates)[/]"
129+
: "[red] VM.MonitoringEnabled = false[/] [dim](check to resume)[/]");
130+
131+
var rule3 = Controls.Rule();
132+
133+
var footer = Controls.Markup(
134+
"[dim]Async thread updates VM properties | Bindings push changes to controls | Press [bold]ESC[/] to close[/]")
135+
.Centered()
136+
.Build();
137+
138+
return new WindowBuilder(ws)
139+
.WithTitle("Data Binding (MVVM)")
140+
.WithSize(80, 24)
141+
.Centered()
142+
.AddControls(
143+
header, description, rule1,
144+
cpuBar, memBar,
145+
networkLabel, counterLabel, statusLabel,
146+
rule2,
147+
enabledCheckbox, checkboxStatus,
148+
rule3, footer)
149+
.WithAsyncWindowThread(async (window, ct) =>
150+
{
151+
var random = new Random();
152+
double cpuBase = 35, memBase = 55, netBase = 120;
153+
154+
vm.StatusText = "[green]Monitoring active[/]";
155+
156+
while (!ct.IsCancellationRequested)
157+
{
158+
await Task.Delay(500, ct);
159+
160+
if (!vm.MonitoringEnabled)
161+
{
162+
vm.StatusText = "[yellow]Monitoring paused[/]";
163+
continue;
164+
}
165+
166+
vm.StatusText = "[green]Monitoring active[/]";
167+
168+
cpuBase = Math.Clamp(cpuBase + random.Next(-8, 9), 0, 100);
169+
memBase = Math.Clamp(memBase + random.Next(-3, 4), 0, 100);
170+
netBase = Math.Clamp(netBase + random.Next(-30, 31), 0, 500);
171+
172+
// Just set VM properties — bindings push to controls automatically
173+
vm.CpuUsage = cpuBase;
174+
vm.MemoryUsage = memBase;
175+
vm.NetworkKBps = netBase;
176+
vm.UpdateCount++;
177+
}
178+
})
179+
.OnKeyPressed((s, e) =>
180+
{
181+
if (e.KeyInfo.Key == ConsoleKey.Escape)
182+
{
183+
ws.CloseWindow((Window)s!);
184+
e.Handled = true;
185+
}
186+
})
187+
.BuildAndShow();
188+
}
189+
}

Examples/DemoApp/DemoWindows/LauncherWindow.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ private static void BuildDemoTree(TreeControl tree)
8484
controls.AddChild("DataGrid");
8585
controls.AddChild("Nerd Fonts");
8686
controls.AddChild("Markup Syntax");
87+
controls.AddChild("Data Binding");
8788

8889
var dataViz = tree.AddRootNode("Data Visualization");
8990
dataViz.TextColor = Color.Yellow;
@@ -126,6 +127,7 @@ private static void LaunchSelectedDemo(ConsoleWindowSystem ws, TreeControl tree)
126127
"DataGrid" => DataGridWindow.Create(ws),
127128
"Nerd Fonts" => NerdFontWindow.Create(ws),
128129
"Markup Syntax" => MarkupSyntaxWindow.Create(ws),
130+
"Data Binding" => DataBindingWindow.Create(ws),
129131
"Graphs & Charts" => GraphsWindow.Create(ws),
130132
"Gradients" => GradientDemoWindow.Create(ws),
131133
"Animations" => AnimationDemoWindow.Create(ws),
@@ -278,6 +280,25 @@ private static void LaunchSelectedDemo(ConsoleWindowSystem ws, TreeControl tree)
278280
"",
279281
"[green][[Enter]] Launch Demo[/]"
280282
},
283+
"Data Binding" => new List<string>
284+
{
285+
"[bold cyan]MVVM Data Binding[/]",
286+
"",
287+
"Demonstrates INotifyPropertyChanged-based data",
288+
"binding between a ViewModel and UI controls.",
289+
"",
290+
"[dim]Features:[/]",
291+
" - One-way binding (VM to Control)",
292+
" - One-way with converter (formatting)",
293+
" - Two-way binding (VM and Control in sync)",
294+
" - Live async updates through VM properties",
295+
"",
296+
"[dim]Controls used:[/]",
297+
" - ProgressBarControl, MarkupControl",
298+
" - CheckboxControl",
299+
"",
300+
"[green][[Enter]] Launch Demo[/]"
301+
},
281302
"Graphs & Charts" => new List<string>
282303
{
283304
"[bold cyan]Graphs & Charts[/]",

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ The rendering engine follows the same architecture as desktop GUI frameworks lik
2323
- **Any Spectre.Console widget works as a control** — Tables, BarCharts, Trees, Panels — wrap any `IRenderable`
2424
- **30+ built-in controls** — buttons, lists, trees, tables, text editors, dropdowns, menus, tabs, and more
2525
- **Compositor effects** — PreBufferPaint/PostBufferPaint hooks for custom rendering, transitions, or even games
26+
- **MVVM-compatible** — all controls implement `INotifyPropertyChanged`; one-way and two-way data binding with `Bind()` / `BindTwoWay()`
2627
- **Fluent builders** for windows, controls, and layouts
2728

2829
**Visit the project website: [nickprotop.github.io/ConsoleEx](https://nickprotop.github.io/ConsoleEx/)**

0 commit comments

Comments
 (0)