309 foreach (var demo
in demos)
311 var seedDemo = seed.FirstOrDefault(x => x.Id == demo.Id);
312 if (seedDemo is
null)
continue;
317 changed |=
SyncCodeField(seedDemo.BlazorCode, demo.BlazorCode, v => demo.BlazorCode = v);
318 changed |=
SyncCodeField(seedDemo.VmCode, demo.VmCode, v => demo.VmCode = v);
319 changed |=
SyncCodeField(seedDemo.WpfCode, demo.WpfCode, v => demo.WpfCode = v);
320 changed |=
SyncCodeField(seedDemo.WinFormsCode, demo.WinFormsCode, v => demo.WinFormsCode = v);
321 changed |=
SyncCodeField(seedDemo.MauiCode, demo.MauiCode, v => demo.MauiCode = v);
324 if (
string.IsNullOrWhiteSpace(demo.TitleEn) && !
string.IsNullOrWhiteSpace(seedDemo.TitleEn))
326 demo.TitleEn = seedDemo.TitleEn;
330 if (
string.IsNullOrWhiteSpace(demo.DescriptionEn) && !
string.IsNullOrWhiteSpace(seedDemo.DescriptionEn))
332 demo.DescriptionEn = seedDemo.DescriptionEn;
413 Id =
"bulb", SortOrder = 5, NavLabel =
"Light Bulb",
414 Title =
"전구 — 버튼 & 체크박스",
415 TitleEn =
"Light Bulb — Button & Checkbox",
416 Description =
"버튼(커맨드)과 체크박스(양방향 바인딩)가 같은 상태를 조작합니다. 로직은 Event, 상태는 Model, 노출은 ViewModel — Dreamine의 3분할 그대로입니다.",
417 DescriptionEn =
"A button (command) and a checkbox (two-way binding) drive the same state. Logic in Event, state in Model, exposure in ViewModel.",
418 WpfShot =
"/img/demos/lightbulb-wpf.svg", WinFormsShot =
"/img/demos/lightbulb-winforms.svg", MauiShot =
"/img/demos/lightbulb-maui.svg",
419 BlazorCode =
@"@* @using Dreamine.UI.Blazor *@
420<DreamineLightBulb IsOn=""@Bulb.IsOn"" Diameter=""120"" />
421<DreamineButton Variant=""DreamineButtonVariant.Primary"" OnClick=""Bulb.Toggle"">Toggle</DreamineButton>
422<DreamineCheckBox @bind-Checked=""Bulb.IsOn"">Power</DreamineCheckBox>
423<span>@Bulb.StatusText · toggled @Bulb.ToggleCount times</span>",
424 VmCode =
@"public sealed class LightBulbModel { public bool IsOn; public int ToggleCount; }
426public sealed class LightBulbEvent
428 private readonly LightBulbModel _m;
429 public LightBulbEvent(LightBulbModel m) => _m = m;
430 public void Toggle() { _m.IsOn = !_m.IsOn; _m.ToggleCount++; }
433public sealed class LightBulbViewModel : ViewModelBase
435 public bool IsOn { get; set; }
436 public int ToggleCount { get; }
437 public string StatusText => IsOn ? ""ON"" : ""OFF"";
439 WpfCode =
@"<!-- WPF (XAML) -->
441 <ctrl:DreamineLightBulb IsOn=""{Binding IsOn}"" Diameter=""96"" />
442 <CheckBox Content=""Power"" IsChecked=""{Binding IsOn}"" />
443 <Button Content=""Toggle"" Command=""{Binding ToggleCommand}"" />
444 <TextBlock Text=""{Binding ToggleCount, StringFormat=Toggled {0} times}"" />
446 WinFormsCode =
@"var bulb = new DreamineLightBulb { Diameter = 96 };
448toggleButton.Click += (_, _) => vm.ToggleCommand.Execute(null);
449checkPower.DataBindings.Add(""Checked"", vm, nameof(vm.IsOn),
450 false, DataSourceUpdateMode.OnPropertyChanged);
452vm.PropertyChanged += (_, _) => bulb.IsOn = vm.IsOn;
453bulb.IsOn = vm.IsOn;",
454 MauiCode =
@"<dc:DreamineLightBulb IsOn=""{Binding IsOn}"" Diameter=""112"" />
455<CheckBox IsChecked=""{Binding IsOn}"" />
456<Button Text=""Toggle"" Command=""{Binding ToggleCommand}"" />
457<Label Text=""{Binding ToggleCount, StringFormat='Toggled {0} times'}"" />"
461 Id =
"button", SortOrder = 10, NavLabel =
"Button",
462 Title =
"Button — DreamineButton",
463 Description =
"커맨드에 바인딩된 버튼. 클릭할 때마다 Event가 카운트를 올리고 로그를 남깁니다.",
464 DescriptionEn =
"A command-bound button. Each click increments the count and appends a log via the Event.",
465 WpfShot =
"/img/demos/button-wpf.svg", WinFormsShot =
"/img/demos/button-winforms.svg", MauiShot =
"/img/demos/button-maui.svg",
466 BlazorCode =
@"@* @using Dreamine.UI.Blazor *@
467<DreamineButton Variant=""DreamineButtonVariant.Primary"" OnClick=""Vm.ClickMe"">Primary</DreamineButton>
468<DreamineButton OnClick=""Vm.ClickMe"">Secondary</DreamineButton>
469<DreamineButton Variant=""DreamineButtonVariant.Danger"" OnClick=""Vm.ClickMe"">Danger</DreamineButton>
470<span>Clicks @Vm.ClickCount</span>
471@foreach (var line in Vm.ActivityLog) { <li>@line</li> }",
472 VmCode =
@"// Shared ViewModel (Dreamine Source Generator)
473[DreamineProperty] private int _clickCount;
474public ObservableCollection<string> ActivityLog { get; } = new();
477private void ClickMe()
480 ActivityLog.Insert(0, $""[{DateTime.Now:HH:mm:ss}] Clicked ({ClickCount})"");
482 WpfCode =
@"<!-- xmlns:ctrl=""...Dreamine.UI.Wpf.Controls"" -->
483<ctrl:DreamineButton Content=""Primary"" Background=""#1E90FF"" Foreground=""White""
484 Width=""100"" Height=""34""
485 Command=""{Binding ClickMeCommand}"" />
486<ctrl:DreamineButton Content=""Blue Glow"" ShineColor=""#1E90FF""
487 Command=""{Binding ClickMeCommand}"" />
488<TextBlock Text=""{Binding ClickCount, StringFormat='Click count: {0}'}"" />",
489 WinFormsCode =
@"var btnPrimary = new DreamineButton {
490 Text = ""Primary"", Width = 100, Height = 34,
491 BackColor = Color.FromArgb(0x1E, 0x90, 0xFF),
492 ForeColor = Color.White
494btnPrimary.Click += (_, _) => vm.ClickMeCommand.Execute(null);
496var lblCount = new Label();
497vm.PropertyChanged += (_, _) =>
498 lblCount.Text = $""Click count: {vm.ClickCount}"";",
499 MauiCode =
@"<!-- .NET MAUI -->
500<Button Text=""Primary"" BackgroundColor=""#1E90FF"" TextColor=""White""
501 Command=""{Binding ClickMeCommand}"" />
502<Button Text=""Secondary"" Command=""{Binding ClickMeCommand}"" />
503<Label Text=""{Binding ClickCount, StringFormat='Click count: {0}'}"" />"
507 Id =
"checkbox", SortOrder = 20, NavLabel =
"CheckBox",
508 Title =
"CheckBox — DreamineCheckBox",
509 Description =
"IsChecked 양방향 바인딩. 마지막 항목은 IsEnabled=False로 비활성 상태입니다.",
510 DescriptionEn =
"IsChecked two-way binding. The last item is disabled (IsEnabled=False).",
511 WpfShot =
"/img/demos/checkbox-wpf.svg", WinFormsShot =
"/img/demos/checkbox-winforms.svg", MauiShot =
"/img/demos/checkbox-maui.svg",
512 BlazorCode =
@"@* @using Dreamine.UI.Blazor *@
513<DreamineCheckBox @bind-Checked=""Vm.Check1"">Check 1</DreamineCheckBox>
514<DreamineCheckBox @bind-Checked=""Vm.Check2"">Check 2</DreamineCheckBox>
515<DreamineCheckBox @bind-Checked=""Vm.Check3"" Disabled=""true"">Check 3</DreamineCheckBox>
516<p>Check1 = @Vm.Check1 · Check2 = @Vm.Check2</p>",
517 VmCode =
@"[DreamineProperty] private bool _check1 = true;
518[DreamineProperty] private bool _check2;
519[DreamineProperty] private bool _check3;",
520 WpfCode =
@"<ctrl:DreamineCheckBox Content=""Check 1 (checked by default)"" IsChecked=""{Binding Check1}"" />
521<ctrl:DreamineCheckBox Content=""Check 2"" IsChecked=""{Binding Check2}"" />
522<ctrl:DreamineCheckBox Content=""Check 3 (disabled)"" IsChecked=""{Binding Check3}"" IsEnabled=""False"" />",
523 WinFormsCode =
@"var chk1 = new DreamineCheckBox { Text = ""Check 1"" };
524chk1.DataBindings.Add(""Checked"", vm, nameof(vm.Check1),
525 false, DataSourceUpdateMode.OnPropertyChanged);
527var chk2 = new DreamineCheckBox { Text = ""Check 2"" };
528chk2.DataBindings.Add(""Checked"", vm, nameof(vm.Check2),
529 false, DataSourceUpdateMode.OnPropertyChanged);
531var chk3 = new DreamineCheckBox { Text = ""Check 3 (disabled)"", Enabled = false };
532chk3.DataBindings.Add(""Checked"", vm, nameof(vm.Check3));",
533 MauiCode =
@"<ctrl:DreamineCheckBox Text=""Check 1"" IsChecked=""{Binding Check1}"" />
534<ctrl:DreamineCheckBox Text=""Check 2"" IsChecked=""{Binding Check2}"" />
535<ctrl:DreamineCheckBox Text=""Check 3 (disabled)"" IsChecked=""{Binding Check3}""
536 IsEnabled=""False"" />"
540 Id =
"radio", SortOrder = 30, NavLabel =
"RadioButton",
541 Title =
"RadioButton — DreamineRadioButton",
542 Description =
"같은 GroupName으로 묶인 라디오. 선택 시 RelayCommand<string>로 값을 전달합니다.",
543 DescriptionEn =
"Radios sharing a GroupName; selection passes the value via RelayCommand<string>.",
544 WpfShot =
"/img/demos/radio-wpf.svg", WinFormsShot =
"/img/demos/radio-winforms.svg", MauiShot =
"/img/demos/radio-maui.svg",
545 BlazorCode =
@"@* @using Dreamine.UI.Blazor *@
546@foreach (var opt in new[] { ""Option A"", ""Option B"", ""Option C"" })
548 <DreamineRadioButton Name=""grp"" Value=""@opt""
549 SelectedValue=""@Vm.SelectedRadio""
550 SelectedValueChanged=""Vm.SelectRadio"">@opt</DreamineRadioButton>
552<p>Selected: @Vm.SelectedRadio</p>",
553 VmCode =
@"[DreamineProperty] private string _selectedRadio = ""Option A"";
556private void SelectRadio(string option) => SelectedRadio = option;",
557 WpfCode =
@"<ctrl:DreamineRadioButton Content=""Option A"" GroupName=""demo"" IsChecked=""True""
558 ctrl:DreamineRadioButton.Command=""{Binding SelectRadioCommand}""
559 ctrl:DreamineRadioButton.CommandParameter=""Option A""
560 ctrl:DreamineRadioButton.CommandTriggerName=""Click"" />
561<ctrl:DreamineRadioButton Content=""Option B"" GroupName=""demo""
562 ctrl:DreamineRadioButton.Command=""{Binding SelectRadioCommand}""
563 ctrl:DreamineRadioButton.CommandParameter=""Option B"" ... />
564<TextBlock Text=""{Binding SelectedRadio, StringFormat='Selected: {0}'}"" />",
565 WinFormsCode =
@"var radA = new DreamineRadioButton { Text = ""Option A"", Checked = true };
566var radB = new DreamineRadioButton { Text = ""Option B"" };
567var radC = new DreamineRadioButton { Text = ""Option C"" };
569radA.CheckedChanged += (_, _) => { if (radA.Checked) vm.SelectedRadio = ""Option A""; };
570radB.CheckedChanged += (_, _) => { if (radB.Checked) vm.SelectedRadio = ""Option B""; };
571radC.CheckedChanged += (_, _) => { if (radC.Checked) vm.SelectedRadio = ""Option C""; };",
572 MauiCode =
@"<RadioButton Content=""Option A"" Value=""Option A""
573 GroupName=""demo"" IsChecked=""True"" />
574<RadioButton Content=""Option B"" Value=""Option B""
575 GroupName=""demo"" />
576<RadioButton Content=""Option C"" Value=""Option C""
577 GroupName=""demo"" />
578<Label Text=""{Binding SelectedRadio, StringFormat='Selected: {0}'}"" />"
582 Id =
"checkled", SortOrder = 40, NavLabel =
"CheckLed",
583 Title =
"CheckLed — DreamineCheckLed",
584 Description =
"Dreamine 시그니처 LED 인디케이터. IsOn / IsPulse 를 바인딩합니다 (장비 상태 표시용).",
585 DescriptionEn =
"Dreamine's signature LED indicator. Bind IsOn / IsPulse (great for equipment status).",
586 WpfShot =
"/img/demos/checkled-wpf.svg", WinFormsShot =
"/img/demos/checkled-winforms.svg", MauiShot =
"/img/demos/checkled-maui.svg",
587 BlazorCode =
@"@* @using Dreamine.UI.Blazor *@
588<DreamineCheckLed IsOn=""Vm.LedIsOn"" IsPulse=""Vm.LedIsPulse"" Diameter=""34"" />
589<DreamineButton OnClick=""Vm.ToggleLed"">ON / OFF</DreamineButton>
590<DreamineButton OnClick=""Vm.TogglePulse"">Pulse</DreamineButton>
591<p>IsOn = @Vm.LedIsOn · IsPulse = @Vm.LedIsPulse</p>",
592 VmCode =
@"[DreamineProperty] private bool _ledIsOn = true;
593[DreamineProperty] private bool _ledIsPulse;
595[DreamineCommand] private void ToggleLed() => LedIsOn = !LedIsOn;
596[DreamineCommand] private void TogglePulse() => LedIsPulse = !LedIsPulse;",
597 WpfCode =
@"<ctrl:DreamineCheckLed IsOn=""{Binding LedIsOn}""
598 IsPulse=""{Binding LedIsPulse}""
599 Width=""36"" Height=""36"" />
600<ctrl:DreamineButton Content=""ON / OFF"" Command=""{Binding ToggleLedCommand}"" />
601<ctrl:DreamineButton Content=""Pulse"" Command=""{Binding TogglePulseCommand}"" />",
602 WinFormsCode =
@"var led = new DreamineCheckLed { Width = 36, Height = 36 };
603led.DataBindings.Add(""IsOn"", vm, nameof(vm.LedIsOn));
604led.DataBindings.Add(""IsPulse"", vm, nameof(vm.LedIsPulse));
606var btnToggle = new DreamineButton { Text = ""ON / OFF"" };
607btnToggle.Click += (_, _) => vm.ToggleLedCommand.Execute(null);
609var btnPulse = new DreamineButton { Text = ""Pulse"" };
610btnPulse.Click += (_, _) => vm.TogglePulseCommand.Execute(null);",
611 MauiCode =
@"<ctrl:DreamineCheckLed IsOn=""{Binding LedIsOn}""
612 IsPulse=""{Binding LedIsPulse}""
613 WidthRequest=""36"" HeightRequest=""36"" />
614<Button Text=""ON / OFF"" Command=""{Binding ToggleLedCommand}"" />
615<Button Text=""Pulse"" Command=""{Binding TogglePulseCommand}"" />"
619 Id =
"textbox", SortOrder = 50, NavLabel =
"TextBox",
620 Title =
"TextBox / PasswordBox — DreamineTextBox",
621 Description =
"Hint(placeholder) 지원 입력 컨트롤. Clear 커맨드로 값을 비웁니다.",
622 DescriptionEn =
"Input controls with Hint (placeholder). Clear via command.",
623 WpfShot =
"/img/demos/textbox-wpf.svg", WinFormsShot =
"/img/demos/textbox-winforms.svg", MauiShot =
"/img/demos/textbox-maui.svg",
624 BlazorCode =
@"@* @using Dreamine.UI.Blazor *@
625<DreamineTextBox @bind-Value=""Vm.TextInput"" Hint=""Type here...""
626 OnFocus=""() => _showKeyboard = true"" />
627<DreamineButton Size=""DreamineButtonSize.Small"" OnClick=""Vm.ClearText"">Clear</DreamineButton>
628<p>Value: @Vm.TextInput</p>
630@* 가상 키보드는 대상 텍스트박스 바로 아래에 표시 — 시선이 튀지 않게. *@
633 <DreamineVirtualKeyboard IsVisible=""_showKeyboard""
634 Value=""@Vm.TextInput""
635 ValueChanged=""v => Vm.TextInput = v""
636 OnEnter=""() => _showKeyboard = false"" />
639<DreamineTextBox @bind-Value=""Vm.Password"" IsPassword=""true"" Hint=""Password..."" />
640<DreamineButton Size=""DreamineButtonSize.Small"" OnClick=""Vm.ClearPassword"">Clear</DreamineButton>
641<p>Length: @Vm.Password.Length</p>
643@code { private bool _showKeyboard; }",
644 VmCode =
@"[DreamineProperty] private string _textInput = string.Empty;
645[DreamineProperty] private string _password = string.Empty;
647[DreamineCommand] private void ClearText() => TextInput = string.Empty;
648[DreamineCommand] private void ClearPassword() => Password = string.Empty;",
649 WpfCode =
@"<ctrl:DreamineTextBox
650 Text=""{Binding TextInput, UpdateSourceTrigger=PropertyChanged}""
651 Hint=""Type text here..."" Height=""40"" />
652<ctrl:DreamineButton Content=""Clear"" Command=""{Binding ClearTextCommand}"" />
654<ctrl:DreaminePasswordBox
655 Password=""{Binding Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}""
656 Hint=""Enter password..."" Height=""40"" />
657<TextBlock Text=""{Binding Password.Length, StringFormat='Length: {0} chars'}"" />",
658 WinFormsCode =
@"var txt = new DreamineTextBox { Hint = ""Type here..."" };
659txt.DataBindings.Add(""Text"", vm, nameof(vm.TextInput),
660 false, DataSourceUpdateMode.OnPropertyChanged);
662var pwd = new DreaminePasswordBox { Hint = ""Password..."" };
663pwd.DataBindings.Add(""Password"", vm, nameof(vm.Password),
664 false, DataSourceUpdateMode.OnPropertyChanged);
666var btnClear = new DreamineButton { Text = ""Clear"" };
667btnClear.Click += (_, _) => vm.ClearTextCommand.Execute(null);",
668 MauiCode =
@"<Entry Placeholder=""Type here...""
669 Text=""{Binding TextInput}"" />
670<Entry Placeholder=""Password..."" IsPassword=""True""
671 Text=""{Binding Password}"" />
672<Button Text=""Clear"" Command=""{Binding ClearTextCommand}"" />"
676 Id =
"combobox", SortOrder = 60, NavLabel =
"ComboBox",
677 Title =
"ComboBox — DreamineComboBox",
678 Description =
"ItemsSource / SelectedItem 바인딩.",
679 DescriptionEn =
"ItemsSource / SelectedItem binding.",
680 WpfShot =
"/img/demos/combobox-wpf.svg", WinFormsShot =
"/img/demos/combobox-winforms.svg", MauiShot =
"/img/demos/combobox-maui.svg",
681 BlazorCode =
@"@* @using Dreamine.UI.Blazor *@
682<DreamineComboBox Items=""Vm.FruitItems"" @bind-SelectedItem=""Vm.SelectedFruit"" />
683<span>Selected: @Vm.SelectedFruit</span>",
684 VmCode =
@"public string[] FruitItems { get; } =
685 { ""Apple"", ""Banana"", ""Cherry"", ""Grape"", ""Mango"", ""Melon"" };
687[DreamineProperty] private string _selectedFruit = ""Cherry"";",
688 WpfCode =
@"<ctrl:DreamineComboBox
689 ItemsSource=""{Binding FruitItems}""
690 SelectedItem=""{Binding SelectedFruit}""
691 Width=""200"" Height=""36"" />
692<TextBlock Text=""{Binding SelectedFruit, StringFormat='Selected item: {0}'}"" />",
693 WinFormsCode =
@"var cmb = new DreamineComboBox();
694cmb.DataSource = vm.FruitItems;
695cmb.DataBindings.Add(""SelectedItem"", vm, nameof(vm.SelectedFruit),
696 false, DataSourceUpdateMode.OnPropertyChanged);
698vm.PropertyChanged += (_, _) =>
699 lblSelected.Text = $""Selected: {vm.SelectedFruit}"";",
700 MauiCode =
@"<Picker Title=""Select fruit""
701 ItemsSource=""{Binding FruitItems}""
702 SelectedItem=""{Binding SelectedFruit}"" />
703<Label Text=""{Binding SelectedFruit, StringFormat='Selected: {0}'}"" />"
707 Id =
"expander", SortOrder = 70, NavLabel =
"Expander",
708 Title =
"Expander — DreamineExpander",
709 Description =
"IsExpanded를 VM에 바인딩해 코드에서도 펼침/접힘을 제어합니다.",
710 DescriptionEn =
"Bind IsExpanded to the VM to control expand/collapse from code too.",
711 WpfShot =
"/img/demos/expander-wpf.svg", WinFormsShot =
"/img/demos/expander-winforms.svg", MauiShot =
"/img/demos/expander-maui.svg",
712 BlazorCode =
@"@* @using Dreamine.UI.Blazor *@
713<DreamineExpander Header=""Expand / Collapse"" @bind-IsExpanded=""Vm.IsExpanded"">
714 Any content can go here. IsExpanded = @Vm.IsExpanded
716 VmCode =
@"[DreamineProperty] private bool _isExpanded = true;
718[DreamineCommand] private void ToggleExpand() => IsExpanded = !IsExpanded;",
719 WpfCode =
@"<ctrl:DreamineExpander Header=""Expand / Collapse""
720 IsExpanded=""{Binding IsExpanded}"">
721 <StackPanel Margin=""16,8"">
722 <TextBlock Text=""Any content can go here."" />
724</ctrl:DreamineExpander>",
725 WinFormsCode =
@"var exp = new DreamineExpander { HeaderText = ""Expand / Collapse"" };
726exp.DataBindings.Add(""IsExpanded"", vm, nameof(vm.IsExpanded),
727 false, DataSourceUpdateMode.OnPropertyChanged);
729exp.ContentPanel.Controls.Add(new Label {
730 Text = ""Any content can go here."",
731 Dock = DockStyle.Fill
733 MauiCode =
@"<ctrl:DreamineExpander Header=""Expand / Collapse""
734 IsExpanded=""{Binding IsExpanded}"">
735 <Label Text=""Any content can go here.""
737</ctrl:DreamineExpander>"
741 Id =
"numeric", SortOrder = 80, NavLabel =
"Numeric",
742 Title =
"Numeric — NumericRangeBehavior",
743 Description =
"0~100 범위로 자동 보정(Clamp)되는 숫자 입력. 슬라이더/숫자 어느 쪽으로 바꿔도 값이 동기화됩니다.",
744 DescriptionEn =
"Numeric input clamped to 0–100. Slider and number stay in sync.",
745 WpfShot =
"/img/demos/numeric-wpf.svg", WinFormsShot =
"/img/demos/numeric-winforms.svg", MauiShot =
"",
746 BlazorCode =
@"@* @using Dreamine.UI.Blazor *@
747<DreamineNumericSlider @bind-Value=""Vm.NumericInput"" Min=""0"" Max=""100"" />
748<span>Value: @Vm.NumericInput</span>",
749 VmCode =
@"private double _numericInput = 50;
750public double NumericInput
752 get => _numericInput;
753 set { _numericInput = Math.Clamp(value, 0, 100); OnPropertyChanged(); }
755 WpfCode =
@"<!-- behaviors:...Dreamine.UI.Wpf.Behaviors -->
756<TextBox Text=""{Binding NumericInput, UpdateSourceTrigger=PropertyChanged}""
757 behaviors:NumericRangeBehavior.IsEnabled=""True""
758 behaviors:NumericRangeBehavior.Min=""0""
759 behaviors:NumericRangeBehavior.Max=""100""
760 behaviors:NumericRangeBehavior.Mode=""Clamp"" Width=""120"" />
761<!-- Values outside the range are clamped to 0-100 automatically. -->",
762 WinFormsCode =
@"// WinForms uses the standard NumericUpDown with manual clamping.
763var nud = new NumericUpDown {
764 Minimum = 0, Maximum = 100, Value = 50,
767nud.DataBindings.Add(""Value"", vm, nameof(vm.NumericInput),
768 false, DataSourceUpdateMode.OnPropertyChanged);",
769 MauiCode =
@"<Slider Minimum=""0"" Maximum=""100""
770 Value=""{Binding NumericInput}"" />
771<Entry Keyboard=""Numeric""
772 Text=""{Binding NumericInput}"" />
773<Label Text=""{Binding NumericInput, StringFormat='Value: {0:F0}'}"" />"
777 Id =
"listbox", SortOrder = 90, NavLabel =
"ListBox",
778 Title =
"ListBox — DreamineListBox",
779 Description =
"선택(클릭) + 더블클릭 시 커맨드 실행(활성화).",
780 DescriptionEn =
"Select (click) + run a command on double-click (activation).",
781 WpfShot =
"/img/demos/listbox-wpf.svg", WinFormsShot =
"/img/demos/listbox-winforms.svg", MauiShot =
"/img/demos/listbox-maui.svg",
782 BlazorCode =
@"@* @using Dreamine.UI.Blazor *@
783<DreamineListBox Items=""Vm.FruitItems""
784 @bind-SelectedItem=""Vm.SelectedFruit""
785 OnActivate=""Vm.ActivateListItem"" />
786<p>Selected: @Vm.SelectedFruit · @Vm.ListActivated</p>",
787 VmCode =
@"public string[] FruitItems { get; } =
788 { ""Apple"", ""Banana"", ""Cherry"", ""Grape"", ""Mango"", ""Melon"" };
790[DreamineProperty] private string _selectedFruit = ""Cherry"";
791[DreamineProperty] private string _listActivated = ""(double-click an item)"";
794private void ActivateListItem(string item) => ListActivated = $""Activated: {item}"";",
795 WpfCode =
@"<ctrl:DreamineListBox
796 ItemsSource=""{Binding FruitItems}""
797 SelectedItem=""{Binding SelectedFruit}""
798 ctrl:DreamineListBox.Command=""{Binding ListBoxActivatedCommand}""
799 ctrl:DreamineListBox.CommandParameter=""{Binding SelectedFruit}""
800 ctrl:DreamineListBox.CommandTriggerName=""MouseDoubleClick"" />",
801 WinFormsCode =
@"var lst = new DreamineListBox();
802lst.DataSource = vm.FruitItems;
803lst.DataBindings.Add(""SelectedItem"", vm, nameof(vm.SelectedFruit),
804 false, DataSourceUpdateMode.OnPropertyChanged);
806lst.DoubleClick += (_, _) =>
807 vm.ListBoxActivatedCommand.Execute(vm.SelectedFruit);",
808 MauiCode =
@"<CollectionView ItemsSource=""{Binding FruitItems}""
809 SelectionMode=""Single""
810 SelectedItem=""{Binding SelectedFruit}"">
811 <CollectionView.ItemTemplate>
813 <Label Text=""{Binding .}"" Padding=""12,8"" />
815 </CollectionView.ItemTemplate>
820 Id =
"counter", SortOrder = 100, NavLabel =
"Counter",
821 Title =
"Counter — Increment / Reset",
822 Description =
"SampleCrossUi의 카운터 데모. Increment는 서비스를 거쳐 값을 올리고, Reset은 0으로 초기화합니다. 로그가 자동으로 쌓입니다.",
823 DescriptionEn =
"The SampleCrossUi counter demo. Increment goes through a service, Reset zeroes the count, and logs accumulate automatically.",
824 WpfShot =
"/img/demos/counter-wpf.svg", WinFormsShot =
"/img/demos/counter-winforms.svg", MauiShot =
"/img/demos/counter-maui.svg",
825 BlazorCode =
@"@* @using Dreamine.UI.Blazor *@
826<span class=""dw-counter-value"">@Vm.Count</span>
827<DreamineButton Variant=""DreamineButtonVariant.Primary"" OnClick=""Vm.Increment"">Increment</DreamineButton>
828<DreamineButton OnClick=""Vm.ResetCounter"">Reset</DreamineButton>
829@foreach (var line in Vm.CounterLogs) { <li>@line</li> }",
830 VmCode =
@"[DreamineProperty] private int _count;
831public ObservableCollection<string> CounterLogs { get; } = new();
834private void Increment()
837 CounterLogs.Insert(0, $""[{DateTime.Now:HH:mm:ss}] Incremented → {Count}"");
841private void ResetCounter()
844 CounterLogs.Insert(0, $""[{DateTime.Now:HH:mm:ss}] Counter reset."");
846 WpfCode =
@"<!-- CounterView.xaml — SampleCrossUi.Wpf -->
847<ctrl:DreamineLabel Content=""{Binding Count, StringFormat='Count: {0}'}""
848 FontSize=""52"" FontWeight=""Bold"" />
850<ctrl:DreamineButton Content=""Increment""
851 Command=""{Binding IncrementCommand}"" Width=""130"" Height=""40"" />
852<ctrl:DreamineButton Content=""Reset""
853 Command=""{Binding ResetCommand}"" Width=""100"" Height=""40"" />
855<ListBox ItemsSource=""{Binding Logs}""
856 behaviors:AutoScrollListBoxBehavior.IsEnabled=""True"" />",
857 WinFormsCode =
@"// CounterPage — SampleCrossUi.WinForms
858var lblCount = new Label { Font = new Font(""Segoe UI"", 52, FontStyle.Bold) };
859vm.PropertyChanged += (_, _) =>
860 lblCount.Text = $""Count: {vm.Count}"";
862var btnInc = new DreamineButton { Text = ""Increment"" };
863btnInc.Click += (_, _) => vm.IncrementCommand.Execute(null);
865var btnReset = new DreamineButton { Text = ""Reset"" };
866btnReset.Click += (_, _) => vm.ResetCommand.Execute(null);",
867 MauiCode =
@"<!-- CounterPage — SampleCrossUi.Maui -->
868<Label Text=""{Binding Count, StringFormat='Count: {0}'}""
869 FontSize=""52"" FontAttributes=""Bold""
870 HorizontalOptions=""Center"" />
871<Button Text=""Increment"" Command=""{Binding IncrementCommand}"" />
872<Button Text=""Reset"" Command=""{Binding ResetCommand}"" />"
876 Id =
"datagrid", SortOrder = 110, NavLabel =
"DataGrid",
877 Title =
"DataGrid — DreamineDataGrid",
878 Description =
"ItemsSource 바인딩 + ClickToDeselect Behavior. 읽기 전용 테이블에 장비 상태를 표시합니다.",
879 DescriptionEn =
"ItemsSource binding + ClickToDeselect Behavior. Displays equipment status in a read-only table.",
880 WpfShot =
"/img/demos/datagrid-wpf.svg", WinFormsShot =
"/img/demos/datagrid-winforms.svg", MauiShot =
"/img/demos/datagrid-maui.svg",
881 BlazorCode =
@"@* @using Dreamine.UI.Blazor *@
882<DreamineDataGrid TItem=""GridRow"" Items=""Vm.GridRows"" @bind-SelectedItem=""Vm.SelectedRow"">
883 <HeaderContent><th>No</th><th>Name</th><th>Status</th></HeaderContent>
885 <td>@context.No</td><td>@context.Name</td><td>@context.Status</td>
888 VmCode =
@"public ObservableCollection<GridRow> GridRows { get; } = new()
890 new() { No = 1, Name = ""Device A"", Status = ""Running"" },
891 new() { No = 2, Name = ""Device B"", Status = ""Stopped"" },
892 new() { No = 3, Name = ""Device C"", Status = ""Running"" },
893 new() { No = 4, Name = ""Device D"", Status = ""Error"" },
896[DreamineProperty] private GridRow? _selectedRow;",
897 WpfCode =
@"<ctrl:DreamineDataGrid
898 ItemsSource=""{Binding GridRows}""
899 AutoGenerateColumns=""False""
900 behaviors:DataGridBehaviors.EnableClickToDeselect=""True""
902 <ctrl:DreamineDataGrid.Columns>
903 <DataGridTextColumn Header=""No"" Binding=""{Binding No}"" Width=""50"" />
904 <DataGridTextColumn Header=""Name"" Binding=""{Binding Name}"" Width=""150"" />
905 <DataGridTextColumn Header=""Status"" Binding=""{Binding Status}"" Width=""120"" />
906 </ctrl:DreamineDataGrid.Columns>
907</ctrl:DreamineDataGrid>",
908 WinFormsCode =
@"var dgv = new DreamineDataGrid {
909 AutoGenerateColumns = false, ReadOnly = true
911dgv.Columns.Add(new DataGridViewTextBoxColumn {
912 HeaderText = ""No"", DataPropertyName = ""No"", Width = 50 });
913dgv.Columns.Add(new DataGridViewTextBoxColumn {
914 HeaderText = ""Name"", DataPropertyName = ""Name"", Width = 150 });
915dgv.Columns.Add(new DataGridViewTextBoxColumn {
916 HeaderText = ""Status"", DataPropertyName = ""Status"", Width = 120 });
918dgv.DataSource = new BindingSource { DataSource = vm.GridRows };",
919 MauiCode =
@"<!-- MAUI - table-like layout with CollectionView -->
920<CollectionView ItemsSource=""{Binding GridRows}""
921 SelectionMode=""Single""
922 SelectedItem=""{Binding SelectedRow}"">
923 <CollectionView.ItemTemplate>
925 <Grid ColumnDefinitions=""50,*,120"" Padding=""8,4"">
926 <Label Grid.Column=""0"" Text=""{Binding No}"" />
927 <Label Grid.Column=""1"" Text=""{Binding Name}"" />
928 <Label Grid.Column=""2"" Text=""{Binding Status}"" />
931 </CollectionView.ItemTemplate>
936 Id =
"timespinner", SortOrder = 120, NavLabel =
"TimeSpinner",
937 Title =
"TimeSpinner — DreamineTimeSpinner",
938 Description =
"시:분:초를 ▲▼ 버튼으로 조절합니다. WPF에서는 스크롤 휠과 키보드도 지원합니다.",
939 DescriptionEn =
"Adjust hours, minutes, seconds with ▲▼ buttons. WPF also supports scroll wheel and keyboard.",
940 WpfShot =
"/img/demos/timespinner-wpf.svg", WinFormsShot =
"", MauiShot =
"/img/demos/timespinner-maui.svg",
941 BlazorCode =
@"<div class=""dw-timespinner"">
943 <button @onclick=""@(() => Vm.Hours++)"">▲</button>
944 <input type=""number"" min=""0"" max=""23"" @bind=""Vm.Hours"" />
945 <button @onclick=""@(() => Vm.Hours--)"">▼</button>
948 <!-- Minutes and seconds use the same structure. -->
950<p>Time: @Vm.TimeDisplay</p>",
951 VmCode =
@"[DreamineProperty] private int _hours = 9;
952[DreamineProperty] private int _minutes = 30;
953[DreamineProperty] private int _seconds;
955public string TimeDisplay => $""{Hours:D2}:{Minutes:D2}:{Seconds:D2}"";",
956 WpfCode =
@"<ctrl:DreamineTimeSpinner
957 Time=""{Binding Time, Mode=TwoWay}""
958 Width=""190"" Height=""40""
959 Foreground=""White"" Background=""#FF162040"" />
960<TextBlock Text=""{Binding Time, StringFormat='Selected time: {0:hh\\:mm\\:ss}'}"" />",
961 WinFormsCode =
@"var picker = new DateTimePicker {
962 Format = DateTimePickerFormat.Custom,
963 CustomFormat = ""HH:mm:ss"",
966picker.DataBindings.Add(""Value"", vm, nameof(vm.Time),
967 false, DataSourceUpdateMode.OnPropertyChanged);",
968 MauiCode =
@"<!-- MAUI - TimePicker -->
969<TimePicker Time=""{Binding Time}""
970 Format=""HH:mm:ss"" />
971<Label Text=""{Binding Time, StringFormat='Selected time: {0:hh\\:mm\\:ss}'}"" />"
975 Id =
"image", SortOrder = 130, NavLabel =
"Image",
976 Title =
"Image — DreamineImage",
977 Description =
"CornerRadius로 둥근 모서리, ClickCommand로 클릭 이벤트를 바인딩합니다. FallbackSource로 로드 실패 시 대체 이미지도 지정 가능합니다.",
978 DescriptionEn =
"CornerRadius for rounded corners, ClickCommand for click events. FallbackSource provides a fallback on load failure.",
979 WpfShot =
"/img/demos/image-wpf.svg", WinFormsShot =
"", MauiShot =
"/img/demos/image-maui.svg",
980 BlazorCode =
@"<div class=""dw-image-demo"" @onclick=""Vm.ClickImage"">
981 <svg viewBox=""0 0 100 100"" width=""80"" height=""80"">
982 <circle cx=""50"" cy=""50"" r=""50"" fill=""#1E90FF""/>
983 <circle cx=""50"" cy=""50"" r=""22"" fill=""#fff""/>
986<span>Image clicks @Vm.ImageClickCount</span>",
987 VmCode =
@"[DreamineProperty] private int _imageClickCount;
990private void ClickImage() => ImageClickCount++;",
991 WpfCode =
@"<ctrl:DreamineImage
992 Source=""{StaticResource DemoDrawingImage}""
994 Width=""64"" Height=""64""
995 ClickCommand=""{Binding ClickMeCommand}"" />
996<!-- FallbackSource can provide a replacement image when loading fails. -->",
997 WinFormsCode =
@"var image = new PictureBox {
998 ImageLocation = ""demo_icon.png"",
999 SizeMode = PictureBoxSizeMode.Zoom,
1003image.Click += (_, _) => vm.ClickMeCommand.Execute(null);",
1004 MauiCode =
@"<Image Source=""demo_icon.png""
1005 WidthRequest=""64"" HeightRequest=""64"">
1006 <Image.GestureRecognizers>
1007 <TapGestureRecognizer Command=""{Binding ClickMeCommand}"" />
1008 </Image.GestureRecognizers>
1013 Id =
"popup", SortOrder = 140, NavLabel =
"Popup",
1014 Title =
"Popup — MessageBox & BlinkPopup",
1015 Description =
"메시지 박스와 점멸(Blink) 팝업. 설비 알람처럼 색이 번갈아 깜빡이는 모달을 띄우고, 자동 닫힘(카운트다운)도 지원합니다. 결과는 await로 받습니다.",
1016 DescriptionEn =
"Message box and blinking popup. Show an alarm-style modal whose colors alternate, with optional auto-close countdown. The result is awaited.",
1017 WpfShot =
"", WinFormsShot =
"", MauiShot =
"",
1018 BlazorCode =
@"@* @using Dreamine.UI.Blazor
1019 @inject DreamineDialogService Dialog
1020 페이지 어딘가에 <DreamineDialogHost /> 를 한 번 배치해야 합니다. *@
1021<DreamineButton OnClick=""ShowMessageBox"">DreamineMessageBox (5s)</DreamineButton>
1022<DreamineButton Variant=""DreamineButtonVariant.Danger"" OnClick=""ShowAlarm"">ALARM (blink)</DreamineButton>
1025 private async Task ShowMessageBox()
1027 var r = await Dialog.ShowMessageBoxAsync(
1028 ""Message box demo."", ""MessageBox"", autoClickDelaySeconds: 5);
1031 private async Task ShowAlarm()
1033 var r = await Dialog.ShowBlinkAsync(new BlinkPopupOptions
1035 Title = ""⚠ ALARM"", Message = ""Equipment fault detected."",
1036 UseBlink = true, BlinkIntervalMs = 400,
1037 Color1 = ""#B41E1E"", Color2 = ""#500A0A"",
1038 ForegroundColor = ""#FFD700"", OkText = ""OK"", CancelText = ""Cancel""
1042 VmCode =
@"// DreamineDialogService는 DI에 Scoped로 등록합니다.
1043// builder.Services.AddScoped<DreamineDialogService>();
1044// WPF의 IPopupService / WinForms·MAUI의 DreamineMessageBox·DreamineBlinkPopup과
1045// 같은 역할을 하며, Blazor에서는 <DreamineDialogHost /> 오버레이가 실제로 그립니다.
1046DreamineDialogResult result = await Dialog.ShowMessageBoxAsync(message, title);",
1047 WpfCode =
@"// WPF — Dreamine.UI.Abstractions.Popup.IPopupService
1048var result = _popup.ShowMessageBox(
1049 ""Message box demo."", ""MessageBox"",
1050 autoClickDelaySeconds: 5);
1052_popup.ShowBlink(new BlinkPopupOptions
1054 Title = ""⚠ ALARM"", Message = ""Equipment fault detected."",
1055 UseBlink = true, BlinkIntervalMs = 400,
1056 Color1 = ""#B41E1E"", Color2 = ""#500A0A"",
1057 ForegroundColor = ""#FFD700""
1059 WinFormsCode =
@"// WinForms — DreamineMessageBox / DreamineBlinkPopup
1060var result = DreamineMessageBox.Show(
1061 ""Message box demo."", ""MessageBox"",
1062 autoClickDelaySeconds: 5);
1064DreamineBlinkPopup.Show(new BlinkPopupOptions
1066 Title = ""⚠ ALARM"", Message = ""Equipment fault detected."",
1067 UseBlink = true, BlinkIntervalMs = 400,
1068 Color1 = Color.FromArgb(0xB4, 0x1E, 0x1E),
1069 Color2 = Color.FromArgb(0x50, 0x0A, 0x0A)
1071 MauiCode =
@"<!-- MAUI — same DreamineMessageBox / DreamineBlinkPopup API -->
1072var result = await DreamineMessageBox.ShowAsync(
1073 ""Message box demo."", ""MessageBox"",
1074 autoClickDelaySeconds: 5);
1076await DreamineBlinkPopup.ShowAsync(new BlinkPopupOptions
1078 Title = ""⚠ ALARM"", Message = ""Equipment fault detected."",
1079 UseBlink = true, BlinkIntervalMs = 400,
1080 Color1 = ""#B41E1E"", Color2 = ""#500A0A""