DotvvmChildEventCallback
Changes
src/Web/DotvvmStartup.cs 2(+2 -0)
src/Web/ViewModels/DefaultViewModel.cs 97(+17 -80)
src/Web/Views/Default.dothtml 41(+1 -40)
src/Web/Web.csproj 4(+4 -0)
Details
diff --git a/src/Web/Controls/WizardNavigation/WizardNavigation.cs b/src/Web/Controls/WizardNavigation/WizardNavigation.cs
new file mode 100644
index 0000000..0c0770e
--- /dev/null
+++ b/src/Web/Controls/WizardNavigation/WizardNavigation.cs
@@ -0,0 +1,74 @@
+namespace Web.Controls.WizardNavigation
+{
+ using DotVVM.Framework.Binding;
+ using DotVVM.Framework.Binding.Expressions;
+ using DotVVM.Framework.Controls;
+ using DotVVM.Framework.Hosting;
+ using System.Threading.Tasks;
+
+ public class WizardNavigation : DotvvmMarkupControl
+ {
+ public Command NextButtonCommand
+ {
+ get { return (Command)GetValue(NextButtonCommandProperty); }
+ set
+ {
+ SetValue(NextButtonCommandProperty, value);
+ }
+ }
+ public static readonly DotvvmProperty NextButtonCommandProperty
+ = DotvvmProperty.Register<Command, WizardNavigation>(c => c.NextButtonCommand, null);
+
+ public Command PreviousButtonCommand
+ {
+ get { return (Command)GetValue(PreviousButtonCommandProperty); }
+ set
+ {
+ SetValue(PreviousButtonCommandProperty, value);
+ }
+ }
+ public static readonly DotvvmProperty PreviousButtonCommandProperty
+ = DotvvmProperty.Register<Command, WizardNavigation>(c => c.PreviousButtonCommand, null);
+
+ protected override void OnInit(IDotvvmRequestContext context)
+ {
+ var dataContext = DataContext as WizardNavigationViewModel;
+ if (dataContext == null)
+ throw new DotvvmControlException("Data Context is null.");
+
+ if(dataContext.Total <= 0)
+ throw new DotvvmControlException("Initialization was not called.");
+
+ base.OnInit(context);
+ }
+
+ public async Task NextCommand()
+ {
+ var binding = GetCommandBinding(NextButtonCommandProperty);
+
+ if (binding == null)
+ return;
+
+ var dataContext = DataContext as WizardNavigationViewModel;
+ if (dataContext == null)
+ throw new DotvvmControlException("Data Context is null.");
+
+ await dataContext.OnNextClickAsync(NextButtonCommand);
+ }
+
+ public async Task PreviousCommand()
+ {
+ var binding = GetCommandBinding(PreviousButtonCommandProperty);
+
+ if (binding == null)
+ return;
+
+ var dataContext = DataContext as WizardNavigationViewModel;
+ if (dataContext == null)
+ throw new DotvvmControlException("Data Context is null.");
+
+ await dataContext.OnPreviousClickAsync(PreviousButtonCommand);
+ }
+ }
+}
+
diff --git a/src/Web/Controls/WizardNavigation/WizardNavigation.dotcontrol b/src/Web/Controls/WizardNavigation/WizardNavigation.dotcontrol
new file mode 100644
index 0000000..fdf7f2f
--- /dev/null
+++ b/src/Web/Controls/WizardNavigation/WizardNavigation.dotcontrol
@@ -0,0 +1,29 @@
+@viewModel Web.Controls.WizardNavigation.WizardNavigationViewModel, Web
+@baseType Web.Controls.WizardNavigation.WizardNavigation, Web
+
+<div class="container mt-3" IncludeInPage="{value: WizardNavigationVisible}">
+ <div class="row">
+ <div class="col-md-2">
+ <bs:Button ButtonTagName="button" Click="{controlCommand: PreviousCommand()}" Enabled="{value: PreviousButtonEnabled}" IncludeInPage="{value: PreviousButtonVisible}" Type="Light">
+ Previous
+ </bs:Button>
+ </div>
+ <div class="col-md-8">
+ Step {{value: Step}} out of {{value: Total}}.
+ <bs:ProgressBar Color="Info" VisualStyle="AnimatedStriped" Value="{value: Percentage}" />
+ {{value: Percentage.ToString("#.##")}}% Complete
+ </div>
+ <div class="col-md-2">
+ <bs:Button ButtonTagName="button" Click="{controlCommand: NextCommand()}" Enabled="{value: NextButtonEnabled}" IncludeInPage="{value: NextButtonVisible}" IsSubmitButton="true" Type="Primary">
+ Next
+ </bs:Button>
+ </div>
+ </div>
+</div>
+<div class="container">
+ <div class="row">
+ <div class="offset-md-5 col-md-4">
+ <h4 class="lead">{{value: Title}}</h4>
+ </div>
+ </div>
+</div>
diff --git a/src/Web/Controls/WizardNavigation/WizardNavigationConfigurationExtensions.cs b/src/Web/Controls/WizardNavigation/WizardNavigationConfigurationExtensions.cs
new file mode 100644
index 0000000..be65d26
--- /dev/null
+++ b/src/Web/Controls/WizardNavigation/WizardNavigationConfigurationExtensions.cs
@@ -0,0 +1,18 @@
+namespace Web.Controls.WizardNavigation
+{
+ using DotVVM.Framework.Configuration;
+ using Helper;
+
+ public static class WizardNavigationConfigurationExtensions
+ {
+ public static void AddWizardNavigationConfiguration(this DotvvmConfiguration config)
+ {
+ config.Markup.Controls.Add(new DotvvmControlConfiguration()
+ {
+ Assembly = typeof(WizardNavigation).Assembly.GetName().Name,
+ Namespace = typeof(WizardNavigation).Namespace,
+ TagPrefix = Constants.TagPrefix
+ });
+ }
+ }
+}
diff --git a/src/Web/Controls/WizardNavigation/WizardNavigationViewModel.cs b/src/Web/Controls/WizardNavigation/WizardNavigationViewModel.cs
new file mode 100644
index 0000000..b66a0b6
--- /dev/null
+++ b/src/Web/Controls/WizardNavigation/WizardNavigationViewModel.cs
@@ -0,0 +1,80 @@
+namespace Web.Controls.WizardNavigation
+{
+ using DotVVM.Framework.Binding.Expressions;
+ using DotVVM.Framework.ViewModel;
+ using System;
+ using System.Collections.Generic;
+ using System.Threading.Tasks;
+
+ public class WizardNavigationViewModel : DotvvmViewModelBase
+ {
+ public int MinimumStep { get; set; }
+ public bool NextButtonEnabled { get; set; }
+ public bool NextButtonVisible { get; set; }
+ public double Percentage => ((float)Step / Total) * 100.0;
+ public int PreviousStep { get; set; }
+ public bool PreviousButtonEnabled { get; set; }
+ public bool PreviousButtonVisible { get; set; }
+ public int Step { get; set; }
+ public string Title => Titles[Step - 1];
+ public string[] Titles { get; set; }
+ public int Total => Titles.Length;
+ public bool WizardNavigationVisible { get; set; }
+
+ public WizardNavigationViewModel(List<string> titles) : this(titles, 1)
+ {
+ }
+
+ public WizardNavigationViewModel(List<string> titles, int minimumStep)
+ {
+ if (titles == null)
+ throw new ArgumentNullException($"{nameof(titles)} is required.");
+
+ if (titles.Count == 0)
+ throw new ArgumentOutOfRangeException($"{nameof(titles)} is empty.");
+
+ MinimumStep = minimumStep;
+ NextButtonVisible = true;
+ PreviousButtonVisible = true;
+ Step = 1;
+ Titles = titles.ToArray();
+
+ if (Total > 1)
+ NextButtonEnabled = true;
+ }
+
+ public void Hide() => WizardNavigationVisible = false;
+
+ public async Task OnNextClickAsync(Command command)
+ {
+ if (Step >= Total)
+ return;
+
+ PreviousStep = Step;
+ Step++;
+ SetButtonStatus();
+ await command.Invoke();
+ }
+
+ public async Task OnPreviousClickAsync(Command command)
+ {
+ if (Step <= MinimumStep)
+ return;
+
+ PreviousStep = Step;
+ Step--;
+ SetButtonStatus();
+ await command.Invoke();
+ }
+
+ public void Show() => WizardNavigationVisible = true;
+
+ #region Private Methods
+ private void SetButtonStatus()
+ {
+ NextButtonEnabled = Step < Total;
+ PreviousButtonEnabled = Step > MinimumStep;
+ }
+ #endregion
+ }
+}
src/Web/DotvvmStartup.cs 2(+2 -0)
diff --git a/src/Web/DotvvmStartup.cs b/src/Web/DotvvmStartup.cs
index 9728645..49063b3 100644
--- a/src/Web/DotvvmStartup.cs
+++ b/src/Web/DotvvmStartup.cs
@@ -1,6 +1,7 @@
namespace Web
{
using Controls.ConfirmationModal;
+ using Controls.WizardNavigation;
using DotVVM.Framework.Configuration;
using DotVVM.Framework.Controls.Bootstrap4;
using DotVVM.Framework.ResourceManagement;
@@ -35,6 +36,7 @@ namespace Web
private void ConfigureControls(DotvvmConfiguration config, string applicationPath)
{
config.AddConfirmationModalConfiguration();
+ config.AddWizardNavigationConfiguration();
config.Markup.AutoDiscoverControls(new DefaultControlRegistrationStrategy(config, Constants.TagPrefix, "Controls"));
}
src/Web/ViewModels/DefaultViewModel.cs 97(+17 -80)
diff --git a/src/Web/ViewModels/DefaultViewModel.cs b/src/Web/ViewModels/DefaultViewModel.cs
index e4409d7..47f015b 100644
--- a/src/Web/ViewModels/DefaultViewModel.cs
+++ b/src/Web/ViewModels/DefaultViewModel.cs
@@ -1,7 +1,8 @@
namespace Web.ViewModels
{
- using System;
+ using Controls.WizardNavigation;
using System.Collections.Generic;
+ using System.Threading.Tasks;
using static DefaultViewModelEvents;
public static class DefaultViewModelEvents
@@ -32,14 +33,20 @@ namespace Web.ViewModels
if (!First.IsInitialized)
First.Initialize(ToggleNextButtonEnabled);
- Wizard.NextButtonEnabled = false; // default off until user checks to continue
- Wizard.ShowNavigation();
+ Wizard = new WizardNavigationViewModel(new List<string>
+ {
+ "Tale of Two Cities",
+ "Moby Dick"
+ }, 1)
+ {
+ NextButtonEnabled = false // default off until user checks to continue
+ };
+
+ Wizard.Show();
}
- public void OnNextClick()
+ public async Task OnNextClick()
{
- Wizard.OnNextClick();
-
if (Wizard.Step == 2)
{
if (Second == null) // TODO resolve with IOC
@@ -48,11 +55,13 @@ namespace Web.ViewModels
if (!Second.IsInitialized)
Second.Initialize();
}
+
+ await Task.CompletedTask;
}
- public void OnPreviousClick()
+ public async Task OnPreviousClick()
{
- Wizard.OnPreviousClick();
+ await Task.CompletedTask;
}
#region Private Methods
@@ -110,76 +119,4 @@ namespace Web.ViewModels
}
}
#endregion
-
- #region Wizard Navigation
- public class WizardNavigationViewModel
- {
- public int MinimumStep { get; set; }
- public bool NextButtonEnabled { get; set; }
- public bool NextButtonVisible { get; set; }
- public double Percentage => ((float)Step / Total) * 100.0;
- public int PreviousStep { get; set; }
- public bool PreviousButtonEnabled { get; set; }
- public bool PreviousButtonVisible { get; set; }
- public int Step { get; set; }
- public string Title => Titles[Step - 1];
- public string[] Titles { get; set; }
- public int Total => Titles.Length;
- public bool NavigationVisible { get; set; }
-
- public WizardNavigationViewModel(List<string> titles) : this(titles, 1)
- {
- }
-
- public WizardNavigationViewModel(List<string> titles, int minimumStep)
- {
- if (titles == null)
- throw new ArgumentNullException($"{nameof(titles)} is required.");
-
- if (titles.Count == 0)
- throw new ArgumentOutOfRangeException($"{nameof(titles)} is empty.");
-
- MinimumStep = minimumStep;
- NextButtonVisible = true;
- PreviousButtonVisible = true;
- Step = 1;
- Titles = titles.ToArray();
-
- if (Total > 1)
- NextButtonEnabled = true;
- }
-
- public void HideNavigation() => NavigationVisible = false;
-
- public void OnNextClick()
- {
- if (Step >= Total)
- return;
-
- PreviousStep = Step;
- Step++;
- SetButtonStatus();
- }
-
- public void OnPreviousClick()
- {
- if (Step <= MinimumStep)
- return;
-
- PreviousStep = Step;
- Step--;
- SetButtonStatus();
- }
-
- public void ShowNavigation() => NavigationVisible = true;
-
- #region Private Methods
- private void SetButtonStatus()
- {
- NextButtonEnabled = Step < Total;
- PreviousButtonEnabled = Step > MinimumStep;
- }
- #endregion
- }
- #endregion
}
src/Web/Views/Default.dothtml 41(+1 -40)
diff --git a/src/Web/Views/Default.dothtml b/src/Web/Views/Default.dothtml
index bf86c98..401b8ae 100644
--- a/src/Web/Views/Default.dothtml
+++ b/src/Web/Views/Default.dothtml
@@ -2,46 +2,7 @@
@masterPage Views/Site.dotmaster
<dot:Content ContentPlaceHolderID="Body">
- <div DataContext="{value: Wizard}">
- <div class="container mt-3" IncludeInPage="{value: NavigationVisible}">
- <div class="row">
- <div class="col-md-2">
- <bs:Button ButtonTagName="button" Click="{command: _root.OnPreviousClick()}" Enabled="{value: PreviousButtonEnabled}" IncludeInPage="{value: PreviousButtonVisible}" Type="Light">
- Previous Button
- </bs:Button>
- </div>
- <div class="col-md-8">
- <div class="row">
- <div class="col-md-3">
- Step {{value: Step}} out of {{value: Total}}.
- </div>
- </div>
- <div class="row">
- <div class="col-md-12">
- <bs:ProgressBar Color="Primary" VisualStyle="AnimatedStriped" Value="{value: Percentage}" />
- </div>
- </div>
- <div class="row">
- <div class="col-md-3">
- {{value: Percentage.ToString("#.##")}}% Complete
- </div>
- </div>
- </div>
- <div class="col-md-2">
- <bs:Button ButtonTagName="button" Click="{command: _root.OnNextClick()}" Enabled="{value: NextButtonEnabled}" IncludeInPage="{value: NextButtonVisible}" IsSubmitButton="true" Type="Primary">
- Next Button
- </bs:Button>
- </div>
- </div>
- </div>
- <div class="container mt-3">
- <div class="row">
- <div class="offset-md-2 col-md-8">
- <h5 class="text-muted">{{value: Title}}</h5>
- </div>
- </div>
- </div>
- </div>
+ <cl:WizardNavigation DataContext="{value: Wizard}" NextButtonCommand="{command: _parent.OnNextClick()}" PreviousButtonCommand="{command: _parent.OnPreviousClick()}" />
<div class="container mt-4">
<div class="row">
<div class="col-md-12">
src/Web/Web.csproj 4(+4 -0)
diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj
index 45b43f9..8deca42 100644
--- a/src/Web/Web.csproj
+++ b/src/Web/Web.csproj
@@ -193,6 +193,9 @@
<ItemGroup>
<Compile Include="Controls\ConfirmationModal\ConfirmationModal.cs" />
<Compile Include="Controls\ConfirmationModal\ConfirmationModalConfigurationExtensions.cs" />
+ <Compile Include="Controls\WizardNavigation\WizardNavigation.cs" />
+ <Compile Include="Controls\WizardNavigation\WizardNavigationConfigurationExtensions.cs" />
+ <Compile Include="Controls\WizardNavigation\WizardNavigationViewModel.cs" />
<Compile Include="Helper\Constants.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Startup.cs" />
@@ -232,6 +235,7 @@
<Content Include="wwwroot\js\toastr\toastr.min.css" />
<Content Include="wwwroot\js\toastr\toastr.min.js" />
<Content Include="Controls\ConfirmationModal\ConfirmationModal.dotcontrol" />
+ <Content Include="Controls\WizardNavigation\WizardNavigation.dotcontrol" />
<None Include="packages.config" />
<Content Include="Views\Site.dotmaster" />
<Content Include="Views\Default.dothtml" />