diff --git a/OwlCore.Wpf/AbstractUI/Controls/AbstractBooleanPresenter.cs b/OwlCore.Wpf/AbstractUI/Controls/AbstractBooleanPresenter.cs
new file mode 100644
index 0000000..3fb0798
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Controls/AbstractBooleanPresenter.cs
@@ -0,0 +1,12 @@
+using System.Windows.Controls;
+using OwlCore.AbstractUI.Models;
+
+namespace OwlCore.Wpf.AbstractUI.Controls
+{
+ ///
+ /// A control that displays an .
+ ///
+ public partial class AbstractBooleanPresenter : Control
+ {
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/AbstractUI/Controls/AbstractButtonPresenter.cs b/OwlCore.Wpf/AbstractUI/Controls/AbstractButtonPresenter.cs
new file mode 100644
index 0000000..d3e69cb
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Controls/AbstractButtonPresenter.cs
@@ -0,0 +1,19 @@
+using System.Windows.Controls;
+using OwlCore.AbstractUI.Models;
+
+namespace OwlCore.Wpf.AbstractUI.Controls
+{
+ ///
+ /// A control that displays an .
+ ///
+ public partial class AbstractButtonPresenter : Control
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ public AbstractButtonPresenter()
+ {
+ this.DefaultStyleKey = typeof(AbstractButtonPresenter);
+ }
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/AbstractUI/Controls/AbstractDataListPresenter.cs b/OwlCore.Wpf/AbstractUI/Controls/AbstractDataListPresenter.cs
new file mode 100644
index 0000000..22cc179
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Controls/AbstractDataListPresenter.cs
@@ -0,0 +1,12 @@
+using System.Windows.Controls;
+using OwlCore.AbstractUI.Models;
+
+namespace OwlCore.Wpf.AbstractUI.Controls
+{
+ ///
+ /// A control that displays an .
+ ///
+ public partial class AbstractDataListPresenter : Control
+ {
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/AbstractUI/Controls/AbstractMultiChoicePresenter.cs b/OwlCore.Wpf/AbstractUI/Controls/AbstractMultiChoicePresenter.cs
new file mode 100644
index 0000000..97722da
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Controls/AbstractMultiChoicePresenter.cs
@@ -0,0 +1,12 @@
+using System.Windows.Controls;
+using OwlCore.AbstractUI.Models;
+
+namespace OwlCore.Wpf.AbstractUI.Controls
+{
+ ///
+ /// A control that displays an .
+ ///
+ public partial class AbstractMultiChoicePresenter : Control
+ {
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/AbstractUI/Controls/AbstractProgessUIElementPresenter.cs b/OwlCore.Wpf/AbstractUI/Controls/AbstractProgessUIElementPresenter.cs
new file mode 100644
index 0000000..ab10a26
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Controls/AbstractProgessUIElementPresenter.cs
@@ -0,0 +1,12 @@
+using System.Windows.Controls;
+using OwlCore.AbstractUI.Models;
+
+namespace OwlCore.Wpf.AbstractUI.Controls
+{
+ ///
+ /// A control that displays an .
+ ///
+ public partial class AbstractProgessUIElementPresenter : Control
+ {
+ }
+}
diff --git a/OwlCore.Wpf/AbstractUI/Controls/AbstractRichTextBlockPresenter.cs b/OwlCore.Wpf/AbstractUI/Controls/AbstractRichTextBlockPresenter.cs
new file mode 100644
index 0000000..9065cab
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Controls/AbstractRichTextBlockPresenter.cs
@@ -0,0 +1,12 @@
+using System.Windows.Controls;
+using OwlCore.AbstractUI.Models;
+
+namespace OwlCore.Wpf.AbstractUI.Controls
+{
+ ///
+ /// A control that displays an .
+ ///
+ public partial class AbstractRichTextBlockPresenter : Control
+ {
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/AbstractUI/Controls/AbstractTextBoxPresenter.cs b/OwlCore.Wpf/AbstractUI/Controls/AbstractTextBoxPresenter.cs
new file mode 100644
index 0000000..0cafb7b
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Controls/AbstractTextBoxPresenter.cs
@@ -0,0 +1,12 @@
+using System.Windows.Controls;
+using OwlCore.AbstractUI.Models;
+
+namespace OwlCore.Wpf.AbstractUI.Controls
+{
+ ///
+ /// A control that displays an .
+ ///
+ public partial class AbstractTextBoxPresenter : Control
+ {
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/AbstractUI/Controls/AbstractUIGroupItemTemplateSelector.cs b/OwlCore.Wpf/AbstractUI/Controls/AbstractUIGroupItemTemplateSelector.cs
new file mode 100644
index 0000000..8a9b7c2
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Controls/AbstractUIGroupItemTemplateSelector.cs
@@ -0,0 +1,114 @@
+using System.Windows;
+using System.Windows.Controls;
+using OwlCore.AbstractUI.ViewModels;
+using Microsoft.Toolkit.Diagnostics;
+using OwlCore.AbstractUI.Models;
+using OwlCore.Wpf.Extensions;
+
+namespace OwlCore.Wpf.AbstractUI.Controls
+{
+ ///
+ /// The template selector used to display Abstract UI elements. Use this to define your own custom styles for each control. You may specify the existing, default styles for those you don't want to override.
+ ///
+ public class AbstractUIGroupItemTemplateSelector : DataTemplateSelector
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ public AbstractUIGroupItemTemplateSelector()
+ {
+ if (!new Themes.AbstractTextBoxStyle().TryGetValue("DefaultAbstractTextBoxTemplate", out var textBoxTemplate))
+ {
+ TextBoxTemplate = ThrowHelper.ThrowArgumentNullException(nameof(textBoxTemplate));
+ }
+
+ if (!new Themes.AbstractDataListStyle().TryGetValue("DefaultAbstractDataListTemplate", out var dataListTemplate))
+ {
+ DataListTemplate = ThrowHelper.ThrowArgumentNullException(nameof(dataListTemplate));
+ }
+
+ if (!new Themes.AbstractButtonStyle().TryGetValue("DefaultAbstractButtonTemplate", out var buttonTemplate))
+ {
+ ButtonTemplate = ThrowHelper.ThrowArgumentNullException(nameof(buttonTemplate));
+ }
+
+ if (!new Themes.AbstractMultiChoiceUIElementStyle().TryGetValue("DefaultAbstractMultipleChoiceTemplate", out var multiChoiceTemplate))
+ {
+ MultiChoiceTemplate = ThrowHelper.ThrowArgumentNullException(nameof(multiChoiceTemplate));
+ }
+
+ if (!new Themes.AbstractBooleanStyle().TryGetValue("DefaultAbstractBooleanTemplate", out var booleanTemplate))
+ {
+ BooleanTemplate = ThrowHelper.ThrowArgumentNullException(nameof(booleanTemplate));
+ }
+
+ if (!new Themes.AbstractProgressUIElementStyle().TryGetValue("DefaultAbstractProgressUIElementTemplate", out var progressTemplate))
+ {
+ ProgressTemplate = ThrowHelper.ThrowArgumentNullException(nameof(progressTemplate));
+ }
+
+ TextBoxTemplate = (DataTemplate)textBoxTemplate;
+ DataListTemplate = (DataTemplate)dataListTemplate;
+ ButtonTemplate = (DataTemplate)buttonTemplate;
+ MultiChoiceTemplate = (DataTemplate)multiChoiceTemplate;
+ BooleanTemplate = (DataTemplate)booleanTemplate;
+ ProgressTemplate = (DataTemplate)progressTemplate;
+ }
+
+ ///
+ /// The data template used to display an .
+ ///
+ public DataTemplate? ElementGroupTemplate { get; set; }
+
+ ///
+ /// The data template used to display an .
+ ///
+ public DataTemplate TextBoxTemplate { get; set; }
+
+ ///
+ /// The data template used to display an .
+ ///
+ public DataTemplate DataListTemplate { get; set; }
+
+ ///
+ /// The data template used to display an .
+ ///
+ public DataTemplate ButtonTemplate { get; set; }
+
+ ///
+ /// The data template used to display an .
+ ///
+ public DataTemplate BooleanTemplate { get; set; }
+
+ ///
+ /// The data template used to display an .
+ ///
+ public DataTemplate ProgressTemplate { get; set; }
+
+ ///
+ /// The data template used to display an .
+ ///
+ public DataTemplate MultiChoiceTemplate { get; set; }
+
+ ///
+ public override DataTemplate SelectTemplate(object item, DependencyObject container)
+ {
+ if (!new Themes.AbstractUIGroupPresenterStyle().TryGetValue("DefaultAbstractUIElementGroupTemplate", out var elementGroupTemplate))
+ ElementGroupTemplate = ThrowHelper.ThrowArgumentNullException(nameof(elementGroupTemplate));
+
+ ElementGroupTemplate = (DataTemplate)elementGroupTemplate;
+
+ return item switch
+ {
+ AbstractTextBoxViewModel => TextBoxTemplate,
+ AbstractDataListViewModel => DataListTemplate,
+ AbstractButtonViewModel => ButtonTemplate,
+ AbstractMultiChoiceUIElementViewModel => MultiChoiceTemplate,
+ AbstractBooleanViewModel => BooleanTemplate,
+ AbstractProgressUIElementViewModel => ProgressTemplate,
+ AbstractUIElementGroupViewModel => ElementGroupTemplate,
+ _ => null!
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/AbstractUI/Controls/AbstractUIGroupPresenter.cs b/OwlCore.Wpf/AbstractUI/Controls/AbstractUIGroupPresenter.cs
new file mode 100644
index 0000000..046e219
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Controls/AbstractUIGroupPresenter.cs
@@ -0,0 +1,109 @@
+using System.Windows;
+using System.Windows.Controls;
+using OwlCore.AbstractUI.ViewModels;
+using OwlCore.AbstractUI.Models;
+
+namespace OwlCore.Wpf.AbstractUI.Controls
+{
+ ///
+ /// Displays a group of abstract UI elements.
+ ///
+ public sealed partial class AbstractUIGroupPresenter : Control
+ {
+ private bool _dataContextBeingSet;
+
+ ///
+ /// Backing property for .
+ ///
+ public static readonly DependencyProperty ViewModelProperty =
+ DependencyProperty.Register(nameof(ViewModel), typeof(AbstractUIElementGroupViewModel), typeof(AbstractUIGroupPresenter), new PropertyMetadata(null, (d, e) => ((AbstractUIGroupPresenter)d).OnViewModelChanged()));
+
+ ///
+ /// Backing property for .
+ ///
+ public static readonly DependencyProperty TemplateSelectorProperty =
+ DependencyProperty.Register(nameof(TemplateSelector), typeof(DataTemplateSelector), typeof(AbstractUIGroupPresenter), new PropertyMetadata(null));
+
+ ///
+ /// The ViewModel for this UserControl.
+ ///
+ public AbstractUIElementGroupViewModel? ViewModel
+ {
+ get => (AbstractUIElementGroupViewModel)GetValue(ViewModelProperty);
+ set => SetValue(ViewModelProperty, value);
+ }
+
+ ///
+ /// The template selector used to display Abstract UI elements. Use this to define your own custom styles for each control. You may specify the existing, default styles for those you don't want to override.
+ ///
+ public DataTemplateSelector? TemplateSelector
+ {
+ get => (DataTemplateSelector)GetValue(TemplateSelectorProperty);
+ set => SetValue(TemplateSelectorProperty, value);
+ }
+
+ ///
+ /// Creates a new instance of .
+ ///
+ public AbstractUIGroupPresenter()
+ {
+ this.DefaultStyleKey = typeof(AbstractUIGroupPresenter);
+
+ AttachEvents();
+ }
+
+ private void AttachEvents()
+ {
+ Loaded += OnLoaded;
+
+ DataContextChanged += OnDataContextChanged;
+ }
+
+ private void DetachEvents()
+ {
+ DataContextChanged -= OnDataContextChanged;
+ }
+
+ private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
+ {
+ if (_dataContextBeingSet)
+ return;
+
+ _dataContextBeingSet = true;
+
+ if (DataContext is AbstractUIElementGroup elementGroup)
+ ViewModel = new AbstractUIElementGroupViewModel(elementGroup);
+
+ if (DataContext is AbstractUIElementGroupViewModel elementGroupViewModel)
+ ViewModel = elementGroupViewModel;
+
+ _dataContextBeingSet = false;
+ }
+
+ private void OnLoaded(object sender, RoutedEventArgs e)
+ {
+ Loaded -= OnLoaded;
+ Unloaded += OnUnloaded;
+ }
+
+ private void OnUnloaded(object sender, RoutedEventArgs e)
+ {
+ Unloaded -= OnUnloaded;
+
+ DetachEvents();
+ }
+
+ ///
+ /// Raised when the changes.
+ ///
+ public void OnViewModelChanged()
+ {
+ if (_dataContextBeingSet)
+ return;
+
+ _dataContextBeingSet = true;
+ DataContext = ViewModel;
+ _dataContextBeingSet = false;
+ }
+ }
+}
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractBooleanStyle.xaml b/OwlCore.Wpf/AbstractUI/Themes/AbstractBooleanStyle.xaml
new file mode 100644
index 0000000..9dc4a83
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractBooleanStyle.xaml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractBooleanStyle.xaml.cs b/OwlCore.Wpf/AbstractUI/Themes/AbstractBooleanStyle.xaml.cs
new file mode 100644
index 0000000..fc8b3b7
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractBooleanStyle.xaml.cs
@@ -0,0 +1,19 @@
+using System.Windows;
+using OwlCore.AbstractUI.Models;
+
+namespace OwlCore.Wpf.AbstractUI.Themes
+{
+ ///
+ /// Default template for the
+ ///
+ public sealed partial class AbstractBooleanStyle : ResourceDictionary
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AbstractBooleanStyle()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractButtonStyle.xaml b/OwlCore.Wpf/AbstractUI/Themes/AbstractButtonStyle.xaml
new file mode 100644
index 0000000..245f076
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractButtonStyle.xaml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractButtonStyle.xaml.cs b/OwlCore.Wpf/AbstractUI/Themes/AbstractButtonStyle.xaml.cs
new file mode 100644
index 0000000..0b63fcf
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractButtonStyle.xaml.cs
@@ -0,0 +1,19 @@
+using System.Windows;
+using OwlCore.AbstractUI.Models;
+
+namespace OwlCore.Wpf.AbstractUI.Themes
+{
+ ///
+ /// Default template for the
+ ///
+ public sealed partial class AbstractButtonStyle : ResourceDictionary
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AbstractButtonStyle()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractButtonTemplateSelector.cs b/OwlCore.Wpf/AbstractUI/Themes/AbstractButtonTemplateSelector.cs
new file mode 100644
index 0000000..4e3fa01
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractButtonTemplateSelector.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using OwlCore.AbstractUI.Models;
+using OwlCore.AbstractUI.ViewModels;
+using Microsoft.Toolkit.Diagnostics;
+using OwlCore.Wpf.AbstractUI.Controls;
+using System.Windows.Data;
+using System.Globalization;
+
+namespace OwlCore.Wpf.AbstractUI.Themes
+{
+ ///
+ /// Selects the template that is used for an based on the .
+ ///
+ public class AbstractButtonTemplateSelector : IValueConverter
+ {
+ ///
+ /// The data template used to a display an with a generic style.
+ ///
+ public Style? GenericStyle { get; set; }
+
+ ///
+ /// The data template used to a display an with a confirmation style.
+ ///
+ public Style? ConfirmStyle { get; set; }
+
+ ///
+ /// The data template used to a display an with a deletion style.
+ ///
+ public Style? DeleteStyle { get; set; }
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var type = AbstractButtonType.Generic;
+
+ if (value is AbstractButtonType vType)
+ type = vType;
+ if (value is AbstractButtonViewModel buttonViewModel)
+ type = buttonViewModel.Type;
+ else if (value is AbstractButton button)
+ type = button.Type;
+
+ return type switch
+ {
+ AbstractButtonType.Generic => GenericStyle ?? ThrowHelper.ThrowArgumentNullException
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractDataListStyle.xaml.cs b/OwlCore.Wpf/AbstractUI/Themes/AbstractDataListStyle.xaml.cs
new file mode 100644
index 0000000..5783c2e
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractDataListStyle.xaml.cs
@@ -0,0 +1,19 @@
+using System.Windows;
+using OwlCore.AbstractUI.Models;
+
+namespace OwlCore.Wpf.AbstractUI.Themes
+{
+ ///
+ /// Default template for the
+ ///
+ public sealed partial class AbstractDataListStyle : ResourceDictionary
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AbstractDataListStyle()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractDataListTypeTemplateSelector.cs b/OwlCore.Wpf/AbstractUI/Themes/AbstractDataListTypeTemplateSelector.cs
new file mode 100644
index 0000000..e4c983c
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractDataListTypeTemplateSelector.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using OwlCore.AbstractUI.ViewModels;
+using Microsoft.Toolkit.Diagnostics;
+using OwlCore.AbstractUI.Models;
+
+namespace OwlCore.Wpf.AbstractUI.Themes
+{
+ ///
+ /// Selects the template that is used for an based on the .
+ ///
+ public class AbstractDataListTypeTemplateSelector : DataTemplateSelector
+ {
+ ///
+ /// The data template used to display an .
+ ///
+ public DataTemplate? GridTemplate { get; set; }
+
+ ///
+ /// The data template used to display an .
+ ///
+ public DataTemplate? ListTemplate { get; set; }
+
+ ///
+ public override DataTemplate SelectTemplate(object item, DependencyObject container)
+ {
+ if (item is AbstractDataListViewModel viewModel)
+ {
+ return viewModel.PreferredDisplayMode switch
+ {
+ AbstractDataListPreferredDisplayMode.Grid => GridTemplate ?? ThrowHelper.ThrowArgumentNullException(),
+ AbstractDataListPreferredDisplayMode.List => ListTemplate ?? ThrowHelper.ThrowArgumentNullException(),
+ _ => throw new NotImplementedException(),
+ };
+ }
+
+ return null!;
+ }
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractMultiChoiceTypeTemplateSelector.cs b/OwlCore.Wpf/AbstractUI/Themes/AbstractMultiChoiceTypeTemplateSelector.cs
new file mode 100644
index 0000000..1a7b0bd
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractMultiChoiceTypeTemplateSelector.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using OwlCore.AbstractUI.ViewModels;
+using Microsoft.Toolkit.Diagnostics;
+using OwlCore.AbstractUI.Models;
+
+namespace OwlCore.Wpf.AbstractUI.Themes
+{
+ ///
+ /// Selects the template that is used for an based on the .
+ ///
+ public class AbstractMultiChoiceTypeTemplateSelector : DataTemplateSelector
+ {
+ ///
+ /// The data template used to display an .
+ ///
+ public DataTemplate? ComboBoxTemplate { get; set; }
+
+ ///
+ /// The data template used to display an .
+ ///
+ public DataTemplate? RadioButtonTemplate { get; set; }
+
+ ///
+ public override DataTemplate SelectTemplate(object item, DependencyObject container)
+ {
+ if (item is AbstractMultiChoiceUIElementViewModel viewModel)
+ {
+ return viewModel.PreferredDisplayMode switch
+ {
+ AbstractMultiChoicePreferredDisplayMode.Dropdown => ComboBoxTemplate ?? ThrowHelper.ThrowArgumentNullException(),
+ AbstractMultiChoicePreferredDisplayMode.RadioButtons => RadioButtonTemplate ?? ThrowHelper.ThrowArgumentNullException(),
+ _ => throw new NotImplementedException(),
+ };
+ }
+
+ return null!;
+ }
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractMultiChoiceUIElementStyle.xaml b/OwlCore.Wpf/AbstractUI/Themes/AbstractMultiChoiceUIElementStyle.xaml
new file mode 100644
index 0000000..61a9e15
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractMultiChoiceUIElementStyle.xaml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractMultiChoiceUIElementStyle.xaml.cs b/OwlCore.Wpf/AbstractUI/Themes/AbstractMultiChoiceUIElementStyle.xaml.cs
new file mode 100644
index 0000000..16f777c
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractMultiChoiceUIElementStyle.xaml.cs
@@ -0,0 +1,19 @@
+using System.Windows;
+using OwlCore.AbstractUI.Models;
+
+namespace OwlCore.Wpf.AbstractUI.Themes
+{
+ ///
+ /// Default template for the
+ ///
+ public sealed partial class AbstractMultiChoiceUIElementStyle : ResourceDictionary
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AbstractMultiChoiceUIElementStyle()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractProgressUIElementStyle.xaml b/OwlCore.Wpf/AbstractUI/Themes/AbstractProgressUIElementStyle.xaml
new file mode 100644
index 0000000..03d3d37
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractProgressUIElementStyle.xaml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractProgressUIElementStyle.xaml.cs b/OwlCore.Wpf/AbstractUI/Themes/AbstractProgressUIElementStyle.xaml.cs
new file mode 100644
index 0000000..da67ec4
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractProgressUIElementStyle.xaml.cs
@@ -0,0 +1,18 @@
+using System.Windows;
+
+namespace OwlCore.Wpf.AbstractUI.Themes
+{
+ ///
+ /// Default template for the
+ ///
+ public sealed partial class AbstractProgressUIElementStyle : ResourceDictionary
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AbstractProgressUIElementStyle()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractRichTextBlockStyle.xaml b/OwlCore.Wpf/AbstractUI/Themes/AbstractRichTextBlockStyle.xaml
new file mode 100644
index 0000000..d381471
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractRichTextBlockStyle.xaml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractRichTextBlockStyle.xaml.cs b/OwlCore.Wpf/AbstractUI/Themes/AbstractRichTextBlockStyle.xaml.cs
new file mode 100644
index 0000000..aaa81e0
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractRichTextBlockStyle.xaml.cs
@@ -0,0 +1,19 @@
+using System.Windows;
+using OwlCore.AbstractUI.Models;
+
+namespace OwlCore.Wpf.AbstractUI.Themes
+{
+ ///
+ /// Default template for the
+ ///
+ public sealed partial class AbstractRichTextBlockStyle : ResourceDictionary
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AbstractRichTextBlockStyle()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractTextBoxStyle.xaml b/OwlCore.Wpf/AbstractUI/Themes/AbstractTextBoxStyle.xaml
new file mode 100644
index 0000000..412b7c8
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractTextBoxStyle.xaml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractTextBoxStyle.xaml.cs b/OwlCore.Wpf/AbstractUI/Themes/AbstractTextBoxStyle.xaml.cs
new file mode 100644
index 0000000..d831e2e
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractTextBoxStyle.xaml.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+using OwlCore.AbstractUI.Models;
+
+namespace OwlCore.Wpf.AbstractUI.Themes
+{
+ ///
+ /// Default template for the
+ ///
+ public sealed partial class AbstractTextBoxStyle : ResourceDictionary
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AbstractTextBoxStyle()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractUIGroupPresenterStyle.xaml b/OwlCore.Wpf/AbstractUI/Themes/AbstractUIGroupPresenterStyle.xaml
new file mode 100644
index 0000000..7a84a6a
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractUIGroupPresenterStyle.xaml
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractUIGroupPresenterStyle.xaml.cs b/OwlCore.Wpf/AbstractUI/Themes/AbstractUIGroupPresenterStyle.xaml.cs
new file mode 100644
index 0000000..55d17e4
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractUIGroupPresenterStyle.xaml.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+using OwlCore.AbstractUI.Models;
+
+namespace OwlCore.Wpf.AbstractUI.Themes
+{
+ ///
+ /// Default template for the
+ ///
+ public sealed partial class AbstractUIGroupPresenterStyle : ResourceDictionary
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AbstractUIGroupPresenterStyle()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/OwlCore.Wpf/AbstractUI/Themes/AbstractUIResources.xaml b/OwlCore.Wpf/AbstractUI/Themes/AbstractUIResources.xaml
new file mode 100644
index 0000000..3657a58
--- /dev/null
+++ b/OwlCore.Wpf/AbstractUI/Themes/AbstractUIResources.xaml
@@ -0,0 +1,12 @@
+
+
+ 24
+
+ 16
+ Light
+ 0.5
+
+
\ No newline at end of file
diff --git a/OwlCore.Wpf/Converters/Bools/CollectionAnyToBoolConverter.cs b/OwlCore.Wpf/Converters/Bools/CollectionAnyToBoolConverter.cs
new file mode 100644
index 0000000..eb9e418
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Bools/CollectionAnyToBoolConverter.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections;
+using System.Diagnostics.Contracts;
+using System.Runtime.CompilerServices;
+using System.Windows.Data;
+using System.Globalization;
+
+namespace OwlCore.Wpf.Converters.Bools
+{
+ ///
+ /// A converter that converts a given to an bool based on the presence of any items in the .
+ ///
+ public sealed class CollectionAnyToBoolConverter : IValueConverter
+ {
+ ///
+ /// Gets whether or not a collection is not empty.
+ ///
+ /// The collection to check.
+ /// Whether or not the colleciton contains any elements
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool Convert(ICollection collection) => collection.Count > 0;
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is ICollection collection)
+ {
+ return Convert(collection);
+ }
+
+ return false;
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/OwlCore.Wpf/Converters/Bools/CollectionNotAnyToBoolConverter.cs b/OwlCore.Wpf/Converters/Bools/CollectionNotAnyToBoolConverter.cs
new file mode 100644
index 0000000..d473b1d
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Bools/CollectionNotAnyToBoolConverter.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections;
+using System.Diagnostics.Contracts;
+using System.Runtime.CompilerServices;
+using System.Windows.Data;
+using System.Globalization;
+
+namespace OwlCore.Wpf.Converters.Bools
+{
+ ///
+ /// A converter that converts a given to an bool based on the lack of presence of any items in the .
+ ///
+ public sealed class CollectionNotAnyToBoolConverter : IValueConverter
+ {
+ ///
+ /// Gets whether or not a collection is empty.
+ ///
+ /// The collection to check.
+ /// Whether or not the colleciton contains no elements
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool Convert(ICollection collection) => collection.Count <= 0;
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is ICollection collection)
+ {
+ return Convert(collection);
+ }
+
+ return false;
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/OwlCore.Wpf/Converters/Bools/InverseBoolConverter.cs b/OwlCore.Wpf/Converters/Bools/InverseBoolConverter.cs
new file mode 100644
index 0000000..578ec28
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Bools/InverseBoolConverter.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters.Bools
+{
+ ///
+ /// A converter that converts a given to its inverse.
+ ///
+ public sealed class InverseBoolConverter : IValueConverter
+ {
+ ///
+ /// Gets the inverse of a bool.
+ ///
+ /// The bool to inverse.
+ /// An inversed bool.
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool Convert(bool data) => !data;
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is bool bValue)
+ {
+ return Convert(bValue);
+ }
+
+ return false;
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/Converters/Bools/NotNullOrEmptyToBoolConverter.cs b/OwlCore.Wpf/Converters/Bools/NotNullOrEmptyToBoolConverter.cs
new file mode 100644
index 0000000..814150e
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Bools/NotNullOrEmptyToBoolConverter.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters.Bools.Visible
+{
+ ///
+ /// A converter that converts checks if a string is null or empty and returns a .
+ ///
+ public sealed class NotNullOrEmptyToBoolConverter : IValueConverter
+ {
+ ///
+ /// Checks if a string is null or empty, and returns a .
+ ///
+ /// The string to null or empty check.
+ /// if not null or empty, if null or empty.
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool Convert(string? str) => !string.IsNullOrEmpty(str);
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is string str)
+ {
+ return Convert(str);
+ }
+
+ return false;
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/Converters/Bools/NotNullToBoolConverter.cs b/OwlCore.Wpf/Converters/Bools/NotNullToBoolConverter.cs
new file mode 100644
index 0000000..2622252
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Bools/NotNullToBoolConverter.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters.Bools
+{
+ ///
+ /// A converter that converts checks null checks an object.
+ ///
+ public sealed class NotNullToBoolConverter : IValueConverter
+ {
+ ///
+ /// Checks if an object is null.
+ ///
+ /// The object to check.
+ /// True if not null, false if null
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool Convert(object? obj) => obj != null;
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return Convert(value);
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/Converters/Bools/Visible/BoolToVisibilityConverter.cs b/OwlCore.Wpf/Converters/Bools/Visible/BoolToVisibilityConverter.cs
new file mode 100644
index 0000000..d136054
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Bools/Visible/BoolToVisibilityConverter.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters.Bools.Visible
+{
+ ///
+ /// A converter that converts a given a .
+ ///
+ public sealed class BoolToVisibilityConverter : IValueConverter
+ {
+ ///
+ /// Returns visible if the boolean is true.
+ ///
+ /// boolean to check.
+ /// Collapsed if false, otherwise Visible.
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Visibility Convert(bool data) => data ? Visibility.Visible : Visibility.Collapsed;
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return Convert(value is bool bValue && bValue);
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/Converters/Bools/Visible/CollectionAnyToVisibilityConverter.cs b/OwlCore.Wpf/Converters/Bools/Visible/CollectionAnyToVisibilityConverter.cs
new file mode 100644
index 0000000..2cb2e50
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Bools/Visible/CollectionAnyToVisibilityConverter.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters.Bools.Visible
+{
+ ///
+ /// A simple converter that converts a given to an based on the presence of any items in the .
+ ///
+ public sealed class CollectionAnyToVisibilityConverter : IValueConverter
+ {
+ ///
+ /// Converts an an based on the presence of any items in the .
+ ///
+ /// The .
+ /// A .
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Visibility Convert(ICollection value) => BoolToVisibilityConverter.Convert(CollectionAnyToBoolConverter.Convert(value));
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is ICollection collection)
+ {
+ return Convert(collection);
+ }
+
+ return Visibility.Collapsed;
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/OwlCore.Wpf/Converters/Bools/Visible/CollectionNotAnyToVisibilityConverter.cs b/OwlCore.Wpf/Converters/Bools/Visible/CollectionNotAnyToVisibilityConverter.cs
new file mode 100644
index 0000000..5088bad
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Bools/Visible/CollectionNotAnyToVisibilityConverter.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters.Bools.Visible
+{
+ ///
+ /// A converter that converts a given to an based on the presence of any items in the .
+ ///
+ public sealed class CollectionNotAnyToVisibilityConverter : IValueConverter
+ {
+ ///
+ /// Converts an an based on the presence of any items in the .
+ ///
+ /// The .
+ /// A .
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Visibility Convert(ICollection value) => BoolToVisibilityConverter.Convert(CollectionNotAnyToBoolConverter.Convert(value));
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is ICollection collection)
+ {
+ return Convert(collection);
+ }
+
+ return Visibility.Collapsed;
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/OwlCore.Wpf/Converters/Bools/Visible/InverseBoolToVisibilityConverter.cs b/OwlCore.Wpf/Converters/Bools/Visible/InverseBoolToVisibilityConverter.cs
new file mode 100644
index 0000000..ce512a1
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Bools/Visible/InverseBoolToVisibilityConverter.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters.Bools.Visible
+{
+ ///
+ /// A converter that converts a the inverse of a given a .
+ ///
+ public sealed class InverseBoolToVisibilityConverter : IValueConverter
+ {
+ ///
+ /// Gets a based on the opposite of a bool.
+ ///
+ /// The bool to represented.
+ /// if , if
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Visibility Convert(bool data) => BoolToVisibilityConverter.Convert(!data);
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is bool bValue)
+ {
+ return Convert(bValue);
+ }
+
+ return Visibility.Visible;
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/Converters/Bools/Visible/NotNullOrEmptyToVisibilityConverter.cs b/OwlCore.Wpf/Converters/Bools/Visible/NotNullOrEmptyToVisibilityConverter.cs
new file mode 100644
index 0000000..efc6afd
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Bools/Visible/NotNullOrEmptyToVisibilityConverter.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters.Bools.Visible
+{
+ ///
+ /// A converter that converts checks if a string is null or empty and returns a .
+ ///
+ public sealed class NotNullOrEmptyToVisibilityConverter : IValueConverter
+ {
+ ///
+ /// Checks if a string is null or empty, and returns a .
+ ///
+ /// The string to null or empty check.
+ /// if not null or empty, if null or empty.
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Visibility Convert(string? str) => BoolToVisibilityConverter.Convert(NotNullOrEmptyToBoolConverter.Convert(str));
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is string str)
+ {
+ return Convert(str);
+ }
+
+ return false;
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/Converters/Bools/Visible/NotNullToVisibilityConverter.cs b/OwlCore.Wpf/Converters/Bools/Visible/NotNullToVisibilityConverter.cs
new file mode 100644
index 0000000..d0bc737
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Bools/Visible/NotNullToVisibilityConverter.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters.Bools.Visible
+{
+ ///
+ /// A converter that checks if an object is not null and returns a .
+ ///
+ public sealed class NotNullToVisibilityConverter : IValueConverter
+ {
+ ///
+ /// Checks if an object is null, and returns a .
+ ///
+ /// The object to null check.
+ /// if not null, if null.
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Visibility Convert(object? obj) => BoolToVisibilityConverter.Convert(obj != null);
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return Convert(value);
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/Converters/Bools/Visible/NullToVisibilityConverter.cs b/OwlCore.Wpf/Converters/Bools/Visible/NullToVisibilityConverter.cs
new file mode 100644
index 0000000..4b081e1
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Bools/Visible/NullToVisibilityConverter.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters.Bools.Visible
+{
+ ///
+ /// A converter that checks if an object is null and returns a .
+ ///
+ public sealed class NullToVisibilityConverter : IValueConverter
+ {
+ ///
+ /// Checks if an object is null, and returns a .
+ ///
+ /// The object to null check.
+ /// if not null, if null.
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Visibility Convert(object? obj) => BoolToVisibilityConverter.Convert(obj == null);
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return Convert(value);
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/Converters/DebugPassThroughConverter.cs b/OwlCore.Wpf/Converters/DebugPassThroughConverter.cs
new file mode 100644
index 0000000..4652e27
--- /dev/null
+++ b/OwlCore.Wpf/Converters/DebugPassThroughConverter.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters
+{
+ ///
+ /// A converter used for debugging the data passed by a binding.
+ ///
+ public sealed class DebugPassThroughConverter : IValueConverter
+ {
+ ///
+ public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ System.Diagnostics.Debug.Write($"Debug passthrough: Type is {value?.GetType().ToString() ?? "null"}");
+ return value;
+ }
+
+ ///
+ public object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return value;
+ }
+ }
+}
diff --git a/OwlCore.Wpf/Converters/Numerical/LongToDoubleConverter.cs b/OwlCore.Wpf/Converters/Numerical/LongToDoubleConverter.cs
new file mode 100644
index 0000000..c6bb407
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Numerical/LongToDoubleConverter.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters.Numerical
+{
+ ///
+ /// A converter that converts a given to a .
+ ///
+ public class LongToDoubleConverter : IValueConverter
+ {
+ ///
+ /// Converts a to a .
+ ///
+ /// The to convert
+ /// The converted value.
+ [Pure]
+ public static double Convert(long value) => value;
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return Convert((long)value);
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return (double)value;
+ }
+ }
+}
diff --git a/OwlCore.Wpf/Converters/SelectionChangedEventArgsToClickedItemConverter.cs b/OwlCore.Wpf/Converters/SelectionChangedEventArgsToClickedItemConverter.cs
new file mode 100644
index 0000000..87e91bb
--- /dev/null
+++ b/OwlCore.Wpf/Converters/SelectionChangedEventArgsToClickedItemConverter.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows.Controls;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters
+{
+ ///
+ /// A converter that returns the first element of
+ /// from a .
+ ///
+ public sealed class SelectionChangedEventArgsToClickedItemConverter : IValueConverter
+ {
+ ///
+ /// Gets the from a .
+ ///
+ /// The event args to check.
+ /// The clicked item, cast to .
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static object Convert(SelectionChangedEventArgs args) => args.AddedItems[0];
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is SelectionChangedEventArgs args)
+ return Convert(args);
+
+ return false;
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/Converters/Text/CaseConverter.cs b/OwlCore.Wpf/Converters/Text/CaseConverter.cs
new file mode 100644
index 0000000..bd91565
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Text/CaseConverter.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows.Controls;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters.Text
+{
+ ///
+ /// A converter that changes the casing of a string.
+ ///
+ public class CaseConverter : IValueConverter
+ {
+ ///
+ /// Gets or sets the character casing to convert an input string to.
+ ///
+ public CharacterCasing Case { get; set; }
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CaseConverter()
+ {
+ Case = CharacterCasing.Upper;
+ }
+
+ ///
+ /// Converts a string to a certain character casing.
+ ///
+ /// The string to convert.
+ /// The result's character casing.
+ /// String in the specified character casing.
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string Convert(string? value, CharacterCasing characterCasing)
+ {
+ if (value != null)
+ {
+ switch (characterCasing)
+ {
+ case CharacterCasing.Upper:
+ return value.ToUpper();
+
+ case CharacterCasing.Lower:
+ return value.ToLower();
+
+ case CharacterCasing.Normal:
+ default:
+ return value;
+ }
+ }
+
+ return string.Empty;
+ }
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var str = value as string;
+ return Convert(str, Case);
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/OwlCore.Wpf/Converters/Time/DateTimeToYearTextConverter.cs b/OwlCore.Wpf/Converters/Time/DateTimeToYearTextConverter.cs
new file mode 100644
index 0000000..0665f25
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Time/DateTimeToYearTextConverter.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters.Time
+{
+ ///
+ /// A converter that converts a given to a formatted year string.
+ ///
+ public class DateTimeToYearTextConverter : IValueConverter
+ {
+ ///
+ /// Converts a to a formatted year string.
+ ///
+ /// The to convert.
+ /// A formatted year string of the .
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string Convert(DateTime value) => value.ToString("yyyy");
+
+ ///
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string Convert(DateTime? value) => Convert(value.HasValue ? value.Value : DateTime.MinValue);
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return Convert(value as DateTime?);
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/OwlCore.Wpf/Converters/Time/Numerical/DoubleToTimeSpanConverter.cs b/OwlCore.Wpf/Converters/Time/Numerical/DoubleToTimeSpanConverter.cs
new file mode 100644
index 0000000..7552422
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Time/Numerical/DoubleToTimeSpanConverter.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters.Time.Numerical
+{
+ ///
+ /// A converter that converts a given to a .
+ ///
+ public sealed class DoubleToTimeSpanConverter : IValueConverter
+ {
+ ///
+ /// Converts a to a .
+ ///
+ /// The to convert.
+ /// A formatted string of the .
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static TimeSpan Convert(double value) => TimeSpan.FromMilliseconds(value);
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is double timeSpan)
+ {
+ return Convert(timeSpan);
+ }
+
+ return Convert(0);
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/OwlCore.Wpf/Converters/Time/Numerical/LongToTimeSpanConverter.cs b/OwlCore.Wpf/Converters/Time/Numerical/LongToTimeSpanConverter.cs
new file mode 100644
index 0000000..b4c1790
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Time/Numerical/LongToTimeSpanConverter.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters.Time.Numerical
+{
+ ///
+ /// A converter that converts a given to a .
+ ///
+ public sealed class LongToTimeSpanConverter : IValueConverter
+ {
+ ///
+ /// Converts a to a .
+ ///
+ /// The to convert.
+ /// A formatted string of the .
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static TimeSpan Convert(long value) => TimeSpan.FromMilliseconds(value);
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is long timeSpan)
+ {
+ return Convert(timeSpan);
+ }
+
+ return Convert(0);
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/OwlCore.Wpf/Converters/Time/Numerical/LongToTimeSpanTextConverter.cs b/OwlCore.Wpf/Converters/Time/Numerical/LongToTimeSpanTextConverter.cs
new file mode 100644
index 0000000..eeb8b60
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Time/Numerical/LongToTimeSpanTextConverter.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters.Time.Numerical
+{
+ ///
+ /// A converter that converts a given to a then to a natural time format string.
+ ///
+ public sealed class LongToTimeSpanTextConverter : IValueConverter
+ {
+ ///
+ /// Converts a to a formatted string.
+ ///
+ /// The to convert.
+ /// A formatted string of the as a .
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string Convert(long value) => TimeSpanToTextConverter.Convert(LongToTimeSpanConverter.Convert(value));
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ long lValue = 0;
+ switch (value)
+ {
+ case long l:
+ lValue = l;
+ break;
+ case double d:
+ lValue = (long)d;
+ break;
+ case int i:
+ lValue = i;
+ break;
+ case float f:
+ lValue = (long)f;
+ break;
+ }
+ return Convert(lValue);
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/OwlCore.Wpf/Converters/Time/TimeSpanToTextConverter.cs b/OwlCore.Wpf/Converters/Time/TimeSpanToTextConverter.cs
new file mode 100644
index 0000000..ab0ae13
--- /dev/null
+++ b/OwlCore.Wpf/Converters/Time/TimeSpanToTextConverter.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows.Data;
+
+namespace OwlCore.Wpf.Converters.Time
+{
+ ///
+ /// A converter that converts a given to a natural time format string.
+ ///
+ public sealed class TimeSpanToTextConverter : IValueConverter
+ {
+ ///
+ /// Converts a to a formatted string.
+ ///
+ /// The to convert.
+ /// A formatted string of the .
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string Convert(TimeSpan value)
+ {
+ // TODO: Make more rigorous cases
+ if (value.Hours > 0) return value.ToString(@"h\:mm\:ss");
+ else return value.ToString(@"m\:ss");
+ }
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is TimeSpan timeSpan)
+ {
+ return Convert(timeSpan);
+ }
+
+ return Convert(TimeSpan.Zero);
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/OwlCore.Wpf/Converters/UriToImageSourceConverter.cs b/OwlCore.Wpf/Converters/UriToImageSourceConverter.cs
new file mode 100644
index 0000000..45c527e
--- /dev/null
+++ b/OwlCore.Wpf/Converters/UriToImageSourceConverter.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+using System.Windows.Media.Imaging;
+
+namespace OwlCore.Wpf.Converters
+{
+ ///
+ /// A converter that converts a given to a .
+ ///
+ public sealed class UriToImageSourceConverter : IValueConverter
+ {
+ ///
+ /// Converts a or url string to a .
+ ///
+ /// The uri or url string.
+ /// A with the url as a source.
+ public static BitmapImage? Convert(object value)
+ {
+ Uri? uri = null;
+
+ if (value is Uri)
+ {
+ uri = value as Uri;
+ }
+ else if (value is string stringUri)
+ {
+ Uri.TryCreate(stringUri, UriKind.Absolute, out uri);
+ }
+
+ if (uri != null)
+ {
+ return new BitmapImage(uri);
+ }
+
+ return null;
+ }
+
+ ///
+ public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return Convert(value);
+ }
+
+ ///
+ public object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is BitmapImage bitmap)
+ {
+ return bitmap.UriSource;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/OwlCore.Wpf/Extensions/ResourceDictionaryExtensions.cs b/OwlCore.Wpf/Extensions/ResourceDictionaryExtensions.cs
new file mode 100644
index 0000000..cd7b636
--- /dev/null
+++ b/OwlCore.Wpf/Extensions/ResourceDictionaryExtensions.cs
@@ -0,0 +1,36 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Windows;
+
+namespace OwlCore.Wpf.Extensions
+{
+ public static class ResourceDictionaryExtensions
+ {
+ ///
+ /// Gets the value associated with the specified key.
+ ///
+ ///
+ /// The key of the value to get.
+ ///
+ ///
+ /// When this method returns, contains the value associated with the specified key,
+ /// if the key is found; otherwise, null. This parameter is passed uninitialized.
+ ///
+ ///
+ /// true if the contains an element with the specified key; otherwise, false.
+ ///
+ public static bool TryGetValue(this ResourceDictionary dict, object key, [MaybeNullWhen(false)] out object value)
+ {
+ try
+ {
+ value = dict[key];
+ return true;
+ }
+ catch (KeyNotFoundException)
+ {
+ value = null;
+ return false;
+ }
+ }
+ }
+}
diff --git a/OwlCore.Wpf/OwlCore.Wpf.csproj b/OwlCore.Wpf/OwlCore.Wpf.csproj
new file mode 100644
index 0000000..e75d644
--- /dev/null
+++ b/OwlCore.Wpf/OwlCore.Wpf.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net5.0-windows
+ true
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ZuneModCore/Mod.cs b/ZuneModCore/Mod.cs
index a1eb6c2..845279c 100644
--- a/ZuneModCore/Mod.cs
+++ b/ZuneModCore/Mod.cs
@@ -30,13 +30,25 @@ namespace ZuneModCore
public abstract string Author { get; }
+ public virtual AbstractUIElementGroup? GetDefaultOptionsUI() => null;
+
public virtual Task Init() => Task.CompletedTask;
public abstract Task Apply();
public abstract Task Reset();
- public abstract AbstractUIElementGroup? OptionsUI { get; }
+ private AbstractUIElementGroup? _OptionsUI;
+ public AbstractUIElementGroup? OptionsUI
+ {
+ get
+ {
+ if (_OptionsUI == null)
+ _OptionsUI = GetDefaultOptionsUI();
+ return _OptionsUI;
+ }
+ set => _OptionsUI = value;
+ }
public string StorageDirectory
{
diff --git a/ZuneModCore/Mods/FeaturesOverrideMod.cs b/ZuneModCore/Mods/FeaturesOverrideMod.cs
index af1a82b..bbedf53 100644
--- a/ZuneModCore/Mods/FeaturesOverrideMod.cs
+++ b/ZuneModCore/Mods/FeaturesOverrideMod.cs
@@ -19,39 +19,42 @@ namespace ZuneModCore.Mods
public override string Author => "Rafael Rivera";
- public override AbstractUIElementGroup OptionsUI => new(nameof(FeaturesOverrideMod))
+ public override AbstractUIElementGroup? GetDefaultOptionsUI()
{
- Title = "Select features:",
- Items =
+ return new(nameof(FeaturesOverrideMod))
{
- // We don't know what some of these overrides do exactly, so hide them from the user.
- // The ID is the name of the registry key, the label is the display name
+ Title = "Select features:",
+ Items =
+ {
+ // We don't know what some of these overrides do exactly, so hide them from the user.
+ // The ID is the name of the registry key, the label is the display name
- new AbstractBooleanUIElement("Apps", "Apps"),
- new AbstractBooleanUIElement("Art", "Art"),
- new AbstractBooleanUIElement("Channels", "Channels"),
- new AbstractBooleanUIElement("FirstLaunchIntroVideo", "First Launch Intro Video"),
- new AbstractBooleanUIElement("Games", "Games"),
- new AbstractBooleanUIElement("Marketplace", "Marketplace"),
- new AbstractBooleanUIElement("MBRPreview", "[Marketplace] Media Preview"),
- new AbstractBooleanUIElement("MBRPurchase", "[Marketplace] Media Purchase"),
- new AbstractBooleanUIElement("MBRRental", "[Marketplace] Media Rental"),
- new AbstractBooleanUIElement("Music", "Music"),
- new AbstractBooleanUIElement("MusicVideos", "Music Videos"),
- new AbstractBooleanUIElement("Nowplaying", "Now Playing"),
- new AbstractBooleanUIElement("NowplayingArt", "Now Playing Art"),
- new AbstractBooleanUIElement("Picks", "Picks"),
- new AbstractBooleanUIElement("Podcasts", "Podcasts"),
- new AbstractBooleanUIElement("QuickMixLocal", "Quick Mix (Local)"),
- new AbstractBooleanUIElement("QuickMixZMP", "Quick Mix (ZMP)"),
- new AbstractBooleanUIElement("Quickplay", "Quickplay"),
- new AbstractBooleanUIElement("Sign In Available", "Sign In"),
- new AbstractBooleanUIElement("Social", "Social"),
- new AbstractBooleanUIElement("SocialMarketplace", "Social Marketplace"),
- new AbstractBooleanUIElement("SubscriptionFreeTracks", "Subscription Free Tracks"),
- new AbstractBooleanUIElement("Videos", "Videos"),
- }
- };
+ new AbstractBooleanUIElement("Apps", "Apps"),
+ new AbstractBooleanUIElement("Art", "Art"),
+ new AbstractBooleanUIElement("Channels", "Channels"),
+ new AbstractBooleanUIElement("FirstLaunchIntroVideo", "First Launch Intro Video"),
+ new AbstractBooleanUIElement("Games", "Games"),
+ new AbstractBooleanUIElement("Marketplace", "Marketplace"),
+ new AbstractBooleanUIElement("MBRPreview", "[Marketplace] Media Preview"),
+ new AbstractBooleanUIElement("MBRPurchase", "[Marketplace] Media Purchase"),
+ new AbstractBooleanUIElement("MBRRental", "[Marketplace] Media Rental"),
+ new AbstractBooleanUIElement("Music", "Music"),
+ new AbstractBooleanUIElement("MusicVideos", "Music Videos"),
+ new AbstractBooleanUIElement("Nowplaying", "Now Playing"),
+ new AbstractBooleanUIElement("NowplayingArt", "Now Playing Art"),
+ new AbstractBooleanUIElement("Picks", "Picks"),
+ new AbstractBooleanUIElement("Podcasts", "Podcasts"),
+ new AbstractBooleanUIElement("QuickMixLocal", "Quick Mix (Local)"),
+ new AbstractBooleanUIElement("QuickMixZMP", "Quick Mix (ZMP)"),
+ new AbstractBooleanUIElement("Quickplay", "Quickplay"),
+ new AbstractBooleanUIElement("Sign In Available", "Sign In"),
+ new AbstractBooleanUIElement("Social", "Social"),
+ new AbstractBooleanUIElement("SocialMarketplace", "Social Marketplace"),
+ new AbstractBooleanUIElement("SubscriptionFreeTracks", "Subscription Free Tracks"),
+ new AbstractBooleanUIElement("Videos", "Videos"),
+ }
+ };
+ }
public override IReadOnlyList? DependentMods => null;
diff --git a/ZuneModCore/Mods/MbidLocatorMod.cs b/ZuneModCore/Mods/MbidLocatorMod.cs
index 96b12c1..15051ca 100644
--- a/ZuneModCore/Mods/MbidLocatorMod.cs
+++ b/ZuneModCore/Mods/MbidLocatorMod.cs
@@ -24,21 +24,24 @@ namespace ZuneModCore.Mods
public override string Author => "Joshua \"Yoshi\" Askharoun";
- public override AbstractUIElementGroup OptionsUI => new(nameof(MbidLocatorMod))
+ public override AbstractUIElementGroup? GetDefaultOptionsUI()
{
- Title = "Select music folder:",
- Items =
+ return new(nameof(MbidLocatorMod))
{
- new AbstractTextBox("folderBox"),
- new AbstractBooleanUIElement("recursiveBox", "Search recursively")
- }
- };
+ Title = "Select music folder:",
+ Items =
+ {
+ new AbstractTextBox("folderBox"),
+ new AbstractBooleanUIElement("recursiveBox", "Search recursively")
+ }
+ };
+ }
public override IReadOnlyList? DependentMods => null;
public override async Task Apply()
{
- // TODO: Use user choices from AbstractUI
+ // Use user choices from AbstractUI
string folderPath = ((AbstractTextBox)OptionsUI.Items[0]).Value;
bool recursive = ((AbstractBooleanUIElement)OptionsUI.Items[1]).State;
string errorString = string.Empty;
diff --git a/ZuneModCore/Mods/VideoSyncMod.cs b/ZuneModCore/Mods/VideoSyncMod.cs
index 95714fd..fb0f504 100644
--- a/ZuneModCore/Mods/VideoSyncMod.cs
+++ b/ZuneModCore/Mods/VideoSyncMod.cs
@@ -23,8 +23,6 @@ namespace ZuneModCore.Mods
public override string Id => nameof(VideoSyncMod);
- public override AbstractUIElementGroup? OptionsUI => null;
-
#pragma warning disable CA1416 // Validate platform compatibility
public override async Task Apply()
{
diff --git a/ZuneModCore/Mods/WebservicesMod.cs b/ZuneModCore/Mods/WebservicesMod.cs
index 93d40a8..00ed9db 100644
--- a/ZuneModCore/Mods/WebservicesMod.cs
+++ b/ZuneModCore/Mods/WebservicesMod.cs
@@ -21,18 +21,21 @@ namespace ZuneModCore.Mods
public override string Author => "Joshua \"Yoshi\" Askharoun";
- public override AbstractUIElementGroup? OptionsUI => new(nameof(FeaturesOverrideMod))
+ public override AbstractUIElementGroup? GetDefaultOptionsUI()
{
- Title = string.Empty,
- Items =
+ return new(nameof(FeaturesOverrideMod))
{
- new AbstractTextBox("hostBox", "zunes.tk", "zune.net")
+ Title = string.Empty,
+ Items =
{
- Title = "Host",
- TooltipText = "The host where the replacement servers are located. Must be the same length as \"zune.net\"."
+ new AbstractTextBox("hostBox", "zunes.tk", "zune.net")
+ {
+ Title = "Host",
+ TooltipText = "The host where the replacement servers are located. Must be the same length as \"zune.net\"."
+ }
}
- }
- };
+ };
+ }
public override IReadOnlyList? DependentMods => null;
diff --git a/ZuneModdingHelper.sln b/ZuneModdingHelper.sln
index 4abc2b0..926a14d 100644
--- a/ZuneModdingHelper.sln
+++ b/ZuneModdingHelper.sln
@@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZuneModdingHelper", "ZuneMo
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZuneModCore", "ZuneModCore\ZuneModCore.csproj", "{4B1587B5-0DE2-4E94-87FC-239D5E7FC4E7}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OwlCore.Wpf", "OwlCore.Wpf\OwlCore.Wpf.csproj", "{BD87C032-C255-409E-8402-2DE16BFCE801}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -41,6 +43,18 @@ Global
{4B1587B5-0DE2-4E94-87FC-239D5E7FC4E7}.Release|x64.Build.0 = Release|x64
{4B1587B5-0DE2-4E94-87FC-239D5E7FC4E7}.Release|x86.ActiveCfg = Release|x86
{4B1587B5-0DE2-4E94-87FC-239D5E7FC4E7}.Release|x86.Build.0 = Release|x86
+ {BD87C032-C255-409E-8402-2DE16BFCE801}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BD87C032-C255-409E-8402-2DE16BFCE801}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BD87C032-C255-409E-8402-2DE16BFCE801}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {BD87C032-C255-409E-8402-2DE16BFCE801}.Debug|x64.Build.0 = Debug|Any CPU
+ {BD87C032-C255-409E-8402-2DE16BFCE801}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {BD87C032-C255-409E-8402-2DE16BFCE801}.Debug|x86.Build.0 = Debug|Any CPU
+ {BD87C032-C255-409E-8402-2DE16BFCE801}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BD87C032-C255-409E-8402-2DE16BFCE801}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BD87C032-C255-409E-8402-2DE16BFCE801}.Release|x64.ActiveCfg = Release|Any CPU
+ {BD87C032-C255-409E-8402-2DE16BFCE801}.Release|x64.Build.0 = Release|Any CPU
+ {BD87C032-C255-409E-8402-2DE16BFCE801}.Release|x86.ActiveCfg = Release|Any CPU
+ {BD87C032-C255-409E-8402-2DE16BFCE801}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/ZuneModdingHelper/AbstractUIGroupDialog.xaml b/ZuneModdingHelper/AbstractUIGroupDialog.xaml
index 8eac160..0fc3e2d 100644
--- a/ZuneModdingHelper/AbstractUIGroupDialog.xaml
+++ b/ZuneModdingHelper/AbstractUIGroupDialog.xaml
@@ -1,21 +1,62 @@
-
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
diff --git a/ZuneModdingHelper/AbstractUIGroupDialog.xaml.cs b/ZuneModdingHelper/AbstractUIGroupDialog.xaml.cs
index 502a9f0..80a0084 100644
--- a/ZuneModdingHelper/AbstractUIGroupDialog.xaml.cs
+++ b/ZuneModdingHelper/AbstractUIGroupDialog.xaml.cs
@@ -1,17 +1,43 @@
-using System.Windows;
+using MahApps.Metro.Controls;
+using OwlCore.AbstractUI.Models;
+using OwlCore.AbstractUI.ViewModels;
+using OwlCore.Wpf.AbstractUI.Controls;
+using System.Windows;
namespace ZuneModdingHelper
{
///
/// Interaction logic for AbstractUIGroupDialog.xaml
///
- public partial class AbstractUIGroupDialog : Window
+ public partial class AbstractUIGroupDialog : MetroWindow
{
- public AbstractUIGroupDialog()
+ public AbstractUIGroupDialog(AbstractUIElementGroupViewModel viewModel)
{
+ ViewModel = viewModel;
+ DataContext = ViewModel;
InitializeComponent();
}
+ public AbstractUIGroupDialog(AbstractUIElementGroup group) : this(new AbstractUIElementGroupViewModel(group))
+ {
- private void OptionsUINextButton_Click(object sender, RoutedEventArgs e) => Close();
+ }
+
+ private void OptionsUINextButton_Click(object sender, RoutedEventArgs e) => Finish(true);
+
+ private void OptionsUICancelButton_Click(object sender, RoutedEventArgs e) => Finish(false);
+
+ private void Finish(bool result)
+ {
+ DialogResult = result;
+ Close();
+ }
+
+ public AbstractUIElementGroupViewModel ViewModel
+ {
+ get => (AbstractUIElementGroupViewModel)GetValue(ViewModelProperty);
+ set => SetValue(ViewModelProperty, value);
+ }
+ public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(
+ nameof(ViewModel), typeof(AbstractUIElementGroupViewModel), typeof(AbstractUIGroupDialog));
}
}
diff --git a/ZuneModdingHelper/MainWindow.xaml.cs b/ZuneModdingHelper/MainWindow.xaml.cs
index 1cbe2e1..7296876 100644
--- a/ZuneModdingHelper/MainWindow.xaml.cs
+++ b/ZuneModdingHelper/MainWindow.xaml.cs
@@ -5,6 +5,7 @@ using MahApps.Metro.Controls.Dialogs;
using Microsoft.AppCenter.Analytics;
using Microsoft.WindowsAPICodePack.Dialogs;
using Newtonsoft.Json.Linq;
+using OwlCore.AbstractUI.Models;
using OwlCore.AbstractUI.ViewModels;
using System;
using System.Collections.Generic;
@@ -51,23 +52,32 @@ namespace ZuneModdingHelper
Mod mod = (Mod)((FrameworkElement)sender).DataContext;
Mod.ZuneInstallDir = ZuneInstallDirBox.Text;
- progDialog.Maximum = 2;
+ progDialog.Maximum = 3;
int numCompleted = 0;
+ // Stage 0: Initialize mod
progDialog.SetTitle($"Installing '{mod.Title}'");
- progDialog.SetMessage($"Preparing to install...");
+ progDialog.SetMessage("Preparing to install...");
await mod.Init();
progDialog.SetProgress(++numCompleted);
- // TODO: Implement AbstractUI display for options
- //if (mod.OptionsUI != null)
- //{
- // var optionsDialog = new AbstractUIGroupDialog();
- // optionsDialog.OptionsUIPresenter.ViewModel = new AbstractUIElementGroupViewModel(mod.OptionsUI);
- // optionsDialog.ShowDialog();
- //}
+ // Stage 1: Display AbstractUI for options
+ progDialog.SetMessage("Awaiting options...");
+ if (mod.OptionsUI != null)
+ {
+ var optionsDialog = new AbstractUIGroupDialog(mod.OptionsUI);
+ bool? optionsResult = optionsDialog.ShowDialog();
+ if (!(optionsResult.HasValue && optionsResult.Value))
+ {
+ await progDialog.CloseAsync();
+ return;
+ }
+ mod.OptionsUI = (AbstractUIElementGroup)optionsDialog.ViewModel.Model;
+ }
+ progDialog.SetProgress(++numCompleted);
- progDialog.SetMessage($"Applying mod...");
+ // Stage 2: Apply mod
+ progDialog.SetMessage("Applying mod...");
string applyResult = await mod.Apply();
if (applyResult != null)
{
diff --git a/ZuneModdingHelper/ZuneModdingHelper.csproj b/ZuneModdingHelper/ZuneModdingHelper.csproj
index 756d04e..37bd4c3 100644
--- a/ZuneModdingHelper/ZuneModdingHelper.csproj
+++ b/ZuneModdingHelper/ZuneModdingHelper.csproj
@@ -13,6 +13,7 @@
+