diff --git a/ZuneModCore/Mod.cs b/ZuneModCore/Mod.cs new file mode 100644 index 0000000..2153d6f --- /dev/null +++ b/ZuneModCore/Mod.cs @@ -0,0 +1,43 @@ +using OwlCore.AbstractUI.Models; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using ZuneModCore.Mods; + +namespace ZuneModCore +{ + public abstract class Mod + { + /// + /// A list of all available mods + /// + public static readonly IReadOnlyList AvailableMods = new List + { + new FeaturesOverrideMod(), + new VideoSyncMod(), + new WebservicesMod(), + }.AsReadOnly(); + + public static string ZuneInstallDir { get; set; } = @"C:\Program Files\Zune\"; + + public abstract string Id { get; } + + public abstract string Title { get; } + + public abstract string Description { get; } + + public virtual Task Init() => Task.CompletedTask; + + public abstract Task Apply(); + + public abstract Task Reset(); + + public abstract AbstractUIElementGroup? OptionsUI { get; } + + public string StorageDirectory => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "ZuneModCore", Id); + + public abstract IReadOnlyList? DependentMods { get; } + } +} diff --git a/ZuneModCore/Mods/FeaturesOverrideMod.cs b/ZuneModCore/Mods/FeaturesOverrideMod.cs new file mode 100644 index 0000000..53925ed --- /dev/null +++ b/ZuneModCore/Mods/FeaturesOverrideMod.cs @@ -0,0 +1,92 @@ +using OwlCore.AbstractUI.Models; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace ZuneModCore.Mods +{ + public class FeaturesOverrideMod : Mod + { + private const string ZUNE_FEATURESOVERRIDE_REGKEY = RegEdit.ZUNE_REG_PATH + "FeaturesOverride"; + + public override string Id => nameof(FeaturesOverrideMod); + + public override string Title => "Features Override"; + + public override string Description => "Re-enables access to some features disabled by Microsoft, such as the Social and Marketplace tabs.\r\n" + + "Does not restore functionality of those features, but shows them in the software."; + + public override AbstractUIElementGroup OptionsUI => new(nameof(FeaturesOverrideMod)) + { + 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", "MBRPreview"), + //new AbstractBooleanUIElement("MBRPurchase", "MBRPurchase"), + //new AbstractBooleanUIElement("MBRRental", "MBRRental"), + new AbstractBooleanUIElement("Music", "Music"), + new AbstractBooleanUIElement("MusicVideos", "MusicVideos"), + 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; + + public override Task Init() + { + foreach (AbstractUIElement uiElem in OptionsUI.Items) + if (uiElem is AbstractBooleanUIElement boolElem) + boolElem.ChangeState(GetFeatureOverride(boolElem.Id)); + + return Task.CompletedTask; + } + + public override Task Apply() + { + return Task.FromResult(true); + foreach (AbstractUIElement uiElem in OptionsUI.Items) + if (uiElem is AbstractBooleanUIElement boolElem && boolElem.State) + SetFeatureOverride(boolElem.Id, true); + + return Task.FromResult(true); + } + + public override Task Reset() + { + foreach (AbstractUIElement uiElem in OptionsUI.Items) + if (uiElem is AbstractBooleanUIElement boolElem) + ResetFeatureOverride(boolElem.Id); + + return Task.FromResult(true); + } + + public static void SetFeatureOverride(string feature, bool value) => + RegEdit.CurrentUserSetBoolValue(ZUNE_FEATURESOVERRIDE_REGKEY, feature, value); + + public static bool GetFeatureOverride(string feature) => + RegEdit.CurrentUserGetBoolValue(ZUNE_FEATURESOVERRIDE_REGKEY, feature); + + public static void ResetFeatureOverride(string feature) => + RegEdit.CurrentUserDeleteValue(RegEdit.ZUNE_REG_PATH + "FeaturesOverride", feature); + } +} diff --git a/ZuneModCore/Mods/VideoSyncMod.cs b/ZuneModCore/Mods/VideoSyncMod.cs new file mode 100644 index 0000000..dbfb268 --- /dev/null +++ b/ZuneModCore/Mods/VideoSyncMod.cs @@ -0,0 +1,56 @@ +using OwlCore.AbstractUI.Models; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace ZuneModCore.Mods +{ + public class VideoSyncMod : Mod + { + public VideoSyncMod() + { + testButton.Clicked += TestButton_Clicked; + } + + private void TestButton_Clicked(object? sender, EventArgs e) + { + System.Diagnostics.Debug.WriteLine("Howdy, Zune! How are ya?"); + } + + public override string Title => "Fix Video Sync"; + + public override string Description => + "Resolves \"Error C00D11CD\" when attempting to sync video to a Zune device using Windows 10 1607 (Anniversary Update) or newer"; + + public override string Id => nameof(VideoSyncMod); + + public override AbstractUIElementGroup OptionsUI => new(nameof(VideoSyncMod)) + { + Title = Title, + Items = + { + new AbstractBooleanUIElement("test", "Is this is test?"), + testButton + } + }; + + private readonly AbstractButton testButton = new(nameof(testButton), "Howdy Zune!", type: AbstractButtonType.Generic); + + public override Task Apply() + { + var writer = File.CreateText(Path.Combine(StorageDirectory, "1.log")); + writer.WriteLine("Hello world"); + writer.Flush(); + writer.Close(); + return Task.FromResult(true); + } + + public override Task Reset() + { + return Task.FromResult(true); + } + + public override IReadOnlyList? DependentMods => null; + } +} diff --git a/ZuneModCore/Mods/WebservicesMod.cs b/ZuneModCore/Mods/WebservicesMod.cs new file mode 100644 index 0000000..063f48d --- /dev/null +++ b/ZuneModCore/Mods/WebservicesMod.cs @@ -0,0 +1,80 @@ +using OwlCore.AbstractUI.Models; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using static ZuneModCore.Mods.FeaturesOverrideMod; + +#if DEBUG +using System.Diagnostics; +#endif + +namespace ZuneModCore.Mods +{ + public class WebservicesMod : Mod + { + private readonly byte[] ZUNE_4_8_VERSION_BYTES = + { + 0x34, 0x2E, 0x38 + }; + private const int ZUNESERVICES_ENDPOINTS_BLOCK_OFFSET = 0x14D60; + private const int ZUNESERVICES_ENDPOINTS_BLOCK_LENGTH = 0x884; + + public override string Id => nameof(WebservicesMod); + + public override string Title => "Community Webservices"; + + public override string Description => "Partially restores online features such as the Marketplace by patching the Zune desktop software " + + "to use the community's recreation of Microsoft's Zune servers at zunes.tk (instead of zune.net)."; + + public override AbstractUIElementGroup? OptionsUI => null; + + public override IReadOnlyList? DependentMods => null; + + public override async Task Apply() + { + // Open ZuneServices.dll + FileInfo zsDllInfo = new(Path.Combine(ZuneInstallDir, "ZuneService.dll")); + if (!zsDllInfo.Exists) + return false; + using FileStream zsDll = zsDllInfo.Open(FileMode.Open); + using BinaryWriter zsDllWriter = new(zsDll); + using BinaryReader zsDllReader = new(zsDll); + + // Verify that the DLL is from v4.8 (other versions not tested) + zsDllReader.BaseStream.Position = 0x12C824; + var versionBytes = zsDllReader.ReadBytes(ZUNE_4_8_VERSION_BYTES.Length * 2); + if (versionBytes[0] != '4' || versionBytes[2] != '.' || versionBytes[4] != '8') + return false; + + // Patch ZuneServices.dll to use zunes.tk instead of zune.net + zsDllReader.BaseStream.Position = ZUNESERVICES_ENDPOINTS_BLOCK_OFFSET; + string endpointBlock = System.Text.Encoding.Unicode.GetString(zsDllReader.ReadBytes(ZUNESERVICES_ENDPOINTS_BLOCK_LENGTH)); + endpointBlock = endpointBlock.Replace("zune.net", "zunes.tk"); + byte[] endpointBytes = System.Text.Encoding.Unicode.GetBytes(endpointBlock); + if (endpointBytes.Length != ZUNESERVICES_ENDPOINTS_BLOCK_LENGTH) + return false; + zsDllWriter.Seek(ZUNESERVICES_ENDPOINTS_BLOCK_OFFSET, SeekOrigin.Begin); + zsDllWriter.Write(endpointBytes); + + + // Enable all feature overrides affected by new servers + SetFeatureOverride("Apps", true); + SetFeatureOverride("Channels", true); + SetFeatureOverride("Games", true); + SetFeatureOverride("Marketplace", true); + SetFeatureOverride("Music", true); + SetFeatureOverride("MusicVideos", true); + SetFeatureOverride("Podcasts", true); + SetFeatureOverride("Social", true); + SetFeatureOverride("Videos", true); + + return true; + } + + public override Task Reset() + { + throw new NotImplementedException(); + } + } +} diff --git a/ZuneModCore/RegEdit.cs b/ZuneModCore/RegEdit.cs new file mode 100644 index 0000000..cf2cb6a --- /dev/null +++ b/ZuneModCore/RegEdit.cs @@ -0,0 +1,64 @@ +using Microsoft.Win32; + +namespace ZuneModCore +{ + +#pragma warning disable CA1416 // Validate platform compatibility + + public class RegEdit + { + public const string ZUNE_REG_PATH = @"SOFTWARE\Microsoft\Zune\"; + + public static void CurrentUserSetBoolValue(string key, string name, bool value) + { + RegistryKey? regKey = Registry.CurrentUser.OpenSubKey(key, true); + if (regKey == null) + regKey = Registry.CurrentUser.CreateSubKey(key, true); + + regKey.SetValue(name, value, RegistryValueKind.DWord); + regKey.Close(); + regKey.Dispose(); + } + + public static bool CurrentUserGetBoolValue(string key, string name) + { + using RegistryKey? regKey = Registry.CurrentUser.OpenSubKey(key, true); + if (regKey == null) + return false; + + int? value = regKey.GetValue(name, false) as int?; + return value.HasValue && value != 0; + } + + public static void CurrentUserDeleteKey(string key) + { + string[] targetKeySplit = key.Split('\\', System.StringSplitOptions.RemoveEmptyEntries); + using RegistryKey? targetKey = Registry.CurrentUser.OpenSubKey(key, true); + if (targetKey == null) + // Key is already deleted + return; + + // Delete all subkeys + RegistryKey? hdr = targetKey.OpenSubKey(targetKeySplit[^1], true); + if (hdr != null) + foreach (string subKey in hdr.GetSubKeyNames()) + hdr.DeleteSubKey(subKey); + hdr?.Close(); + + // Delete target key + targetKey.DeleteSubKeyTree(targetKeySplit[^1]); + } + + public static void CurrentUserDeleteValue(string key, string name) + { + using RegistryKey? regKey = Registry.CurrentUser.OpenSubKey(key, true); + if (regKey == null) + return; + + regKey.DeleteValue(name); + } + } + +#pragma warning restore CA1416 // Validate platform compatibility + +} diff --git a/ZuneModCore/ZuneModCore.csproj b/ZuneModCore/ZuneModCore.csproj new file mode 100644 index 0000000..49667bc --- /dev/null +++ b/ZuneModCore/ZuneModCore.csproj @@ -0,0 +1,14 @@ + + + + net5.0 + 9 + enable + + + + + + + + diff --git a/ZuneModdingHelper.sln b/ZuneModdingHelper.sln new file mode 100644 index 0000000..fb9cdda --- /dev/null +++ b/ZuneModdingHelper.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31205.134 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZuneModdingHelper", "ZuneModdingHelper\ZuneModdingHelper.csproj", "{A4AE840D-BC01-4E60-B106-289218D9479F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZuneModCore", "ZuneModCore\ZuneModCore.csproj", "{4B1587B5-0DE2-4E94-87FC-239D5E7FC4E7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A4AE840D-BC01-4E60-B106-289218D9479F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A4AE840D-BC01-4E60-B106-289218D9479F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4AE840D-BC01-4E60-B106-289218D9479F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4AE840D-BC01-4E60-B106-289218D9479F}.Release|Any CPU.Build.0 = Release|Any CPU + {4B1587B5-0DE2-4E94-87FC-239D5E7FC4E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4B1587B5-0DE2-4E94-87FC-239D5E7FC4E7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B1587B5-0DE2-4E94-87FC-239D5E7FC4E7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4B1587B5-0DE2-4E94-87FC-239D5E7FC4E7}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {CB6377CF-90C6-4056-BA04-3AF40B6CEE1B} + EndGlobalSection +EndGlobal diff --git a/ZuneModdingHelper/AbstractUI/Controls/AbstractBooleanUIElementPresenter.cs b/ZuneModdingHelper/AbstractUI/Controls/AbstractBooleanUIElementPresenter.cs new file mode 100644 index 0000000..cd24bc5 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Controls/AbstractBooleanUIElementPresenter.cs @@ -0,0 +1,17 @@ +using OwlCore.AbstractUI.Models; +using System.Windows; +using System.Windows.Controls; + +namespace ZuneModdingHelper.AbstractUI.Controls +{ + /// + /// A control that displays an . + /// + public sealed partial class AbstractBooleanUIElementPresenter : Control + { + static AbstractBooleanUIElementPresenter() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(AbstractBooleanUIElementPresenter), new FrameworkPropertyMetadata(typeof(AbstractBooleanUIElementPresenter))); + } + } +} \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Controls/AbstractButtonPresenter.cs b/ZuneModdingHelper/AbstractUI/Controls/AbstractButtonPresenter.cs new file mode 100644 index 0000000..f85f23a --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Controls/AbstractButtonPresenter.cs @@ -0,0 +1,20 @@ +using OwlCore.AbstractUI.Models; +using System.Windows; +using System.Windows.Controls; + +namespace ZuneModdingHelper.AbstractUI.Controls +{ + /// + /// A control that displays an . + /// + public sealed partial class AbstractButtonPresenter : Control + { + /// + /// Creates a new instance of . + /// + public AbstractButtonPresenter() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(AbstractButtonPresenter), new FrameworkPropertyMetadata(typeof(AbstractButtonPresenter))); + } + } +} \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Controls/AbstractDataListPresenter.cs b/ZuneModdingHelper/AbstractUI/Controls/AbstractDataListPresenter.cs new file mode 100644 index 0000000..e45ed4d --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Controls/AbstractDataListPresenter.cs @@ -0,0 +1,17 @@ +using System.Windows; +using System.Windows.Controls; +using OwlCore.AbstractUI.Models; + +namespace ZuneModdingHelper.AbstractUI.Controls +{ + /// + /// A control that displays an . + /// + public sealed partial class AbstractDataListPresenter : Control + { + static AbstractDataListPresenter() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(AbstractDataListPresenter), new FrameworkPropertyMetadata(typeof(AbstractDataListPresenter))); + } + } +} \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Controls/AbstractMultiChoicePresenter.cs b/ZuneModdingHelper/AbstractUI/Controls/AbstractMultiChoicePresenter.cs new file mode 100644 index 0000000..cb3b4d8 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Controls/AbstractMultiChoicePresenter.cs @@ -0,0 +1,17 @@ +using System.Windows; +using System.Windows.Controls; +using OwlCore.AbstractUI.Models; + +namespace ZuneModdingHelper.AbstractUI.Controls +{ + /// + /// A control that displays an . + /// + public sealed partial class AbstractMultiChoicePresenter : Control + { + static AbstractMultiChoicePresenter() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(AbstractMultiChoicePresenter), new FrameworkPropertyMetadata(typeof(AbstractMultiChoicePresenter))); + } + } +} \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Controls/AbstractMutableDataListPresenter.cs b/ZuneModdingHelper/AbstractUI/Controls/AbstractMutableDataListPresenter.cs new file mode 100644 index 0000000..d8a308c --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Controls/AbstractMutableDataListPresenter.cs @@ -0,0 +1,17 @@ +using System.Windows; +using System.Windows.Controls; +using OwlCore.AbstractUI.Models; + +namespace ZuneModdingHelper.AbstractUI.Controls +{ + /// + /// A control that displays an . + /// + public sealed partial class AbstractMutableDataListPresenter : Control + { + static AbstractMutableDataListPresenter() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(AbstractMutableDataListPresenter), new FrameworkPropertyMetadata(typeof(AbstractMutableDataListPresenter))); + } + } +} \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Controls/AbstractProgessUIElementPresenter.cs b/ZuneModdingHelper/AbstractUI/Controls/AbstractProgessUIElementPresenter.cs new file mode 100644 index 0000000..09d97f3 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Controls/AbstractProgessUIElementPresenter.cs @@ -0,0 +1,17 @@ +using System.Windows; +using System.Windows.Controls; +using OwlCore.AbstractUI.Models; + +namespace ZuneModdingHelper.AbstractUI.Controls +{ + /// + /// A control that displays an . + /// + public sealed partial class AbstractProgessUIElementPresenter : Control + { + static AbstractProgessUIElementPresenter() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(AbstractProgessUIElementPresenter), new FrameworkPropertyMetadata(typeof(AbstractProgessUIElementPresenter))); + } + } +} diff --git a/ZuneModdingHelper/AbstractUI/Controls/AbstractRichTextBlockPresenter.cs b/ZuneModdingHelper/AbstractUI/Controls/AbstractRichTextBlockPresenter.cs new file mode 100644 index 0000000..13f7d18 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Controls/AbstractRichTextBlockPresenter.cs @@ -0,0 +1,17 @@ +using System.Windows; +using System.Windows.Controls; +using OwlCore.AbstractUI.Models; + +namespace ZuneModdingHelper.AbstractUI.Controls +{ + /// + /// A control that displays an . + /// + public sealed partial class AbstractRichTextBlockPresenter : Control + { + static AbstractRichTextBlockPresenter() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(AbstractRichTextBlockPresenter), new FrameworkPropertyMetadata(typeof(AbstractRichTextBlockPresenter))); + } + } +} \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Controls/AbstractTextBoxPresenter.cs b/ZuneModdingHelper/AbstractUI/Controls/AbstractTextBoxPresenter.cs new file mode 100644 index 0000000..adf0d62 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Controls/AbstractTextBoxPresenter.cs @@ -0,0 +1,17 @@ +using System.Windows; +using System.Windows.Controls; +using OwlCore.AbstractUI.Models; + +namespace ZuneModdingHelper.AbstractUI.Controls +{ + /// + /// A control that displays an . + /// + public sealed partial class AbstractTextBoxPresenter : Control + { + static AbstractTextBoxPresenter() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(AbstractTextBoxPresenter), new FrameworkPropertyMetadata(typeof(AbstractTextBoxPresenter))); + } + } +} \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Controls/AbstractUIGroupItemTemplateSelector.cs b/ZuneModdingHelper/AbstractUI/Controls/AbstractUIGroupItemTemplateSelector.cs new file mode 100644 index 0000000..f24f985 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Controls/AbstractUIGroupItemTemplateSelector.cs @@ -0,0 +1,109 @@ +using System.Windows; +using System.Windows.Controls; +using OwlCore.AbstractUI.ViewModels; +using Microsoft.Toolkit.Diagnostics; +using OwlCore.AbstractUI.Models; + +namespace ZuneModdingHelper.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() + { + object textBoxTemplate = new Themes.AbstractTextBoxStyle()["DefaultAbstractTextBoxTemplate"]; + if (textBoxTemplate == null) + ThrowHelper.ThrowArgumentNullException(nameof(textBoxTemplate)); + + object dataListTemplate = new Themes.AbstractDataListStyle()["DefaultAbstractDataListTemplate"]; + if (dataListTemplate == null) + ThrowHelper.ThrowArgumentNullException(nameof(dataListTemplate)); + + object mutableDataListTemplate = new Themes.AbstractMutableDataListStyle()["DefaultAbstractMutableDataListTemplate"]; + if (mutableDataListTemplate == null) + ThrowHelper.ThrowArgumentNullException(nameof(mutableDataListTemplate)); + + object buttonTemplate = new Themes.AbstractButtonStyle()["DefaultAbstractButtonTemplate"]; + if (buttonTemplate == null) + ThrowHelper.ThrowArgumentNullException(nameof(buttonTemplate)); + + object multiChoiceTemplate = new Themes.AbstractMultiChoiceUIElementStyle()["DefaultAbstractMultipleChoiceTemplate"]; + if (multiChoiceTemplate == null) + ThrowHelper.ThrowArgumentNullException(nameof(multiChoiceTemplate)); + + object booleanTemplate = new Themes.AbstractBooleanUIElementStyle()["DefaultAbstractBooleanUIElementTemplate"]; + if (booleanTemplate == null) + ThrowHelper.ThrowArgumentNullException(nameof(booleanTemplate)); + + object progressTemplate = new Themes.AbstractProgressUIElementStyle()["DefaultAbstractProgressUIElementTemplate"]; + if (progressTemplate == null) + ThrowHelper.ThrowArgumentNullException(nameof(progressTemplate)); + + TextBoxTemplate = (DataTemplate)textBoxTemplate; + DataListTemplate = (DataTemplate)dataListTemplate; + ButtonTemplate = (DataTemplate)buttonTemplate; + MutableDataListTemplate = (DataTemplate)mutableDataListTemplate; + MultiChoiceTemplate = (DataTemplate)multiChoiceTemplate; + BooleanTemplate = (DataTemplate)booleanTemplate; + ProgressTemplate = (DataTemplate)progressTemplate; + } + + /// + /// 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 MutableDataListTemplate { 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) + { + System.Diagnostics.Debug.WriteLine($"{item.GetType()} template selecting for {nameof(AbstractUIGroupItemTemplateSelector)}"); + + return item switch + { + AbstractTextBoxViewModel _ => TextBoxTemplate, + AbstractDataListViewModel _ => DataListTemplate, + AbstractButtonViewModel _ => ButtonTemplate, + AbstractMutableDataListViewModel _ => MutableDataListTemplate, + AbstractMultiChoiceUIElementViewModel _ => MultiChoiceTemplate, + AbstractBooleanViewModel _ => BooleanTemplate, + AbstractProgressUIElement _ => ProgressTemplate, + _ => base.SelectTemplate(item, container) + }; + } + } +} \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Controls/AbstractUIGroupPresenter.xaml.cs b/ZuneModdingHelper/AbstractUI/Controls/AbstractUIGroupPresenter.xaml.cs new file mode 100644 index 0000000..d81b34a --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Controls/AbstractUIGroupPresenter.xaml.cs @@ -0,0 +1,117 @@ +using System.Windows; +using System.Windows.Controls; +using OwlCore.AbstractUI.ViewModels; +using OwlCore.AbstractUI.Models; + +namespace ZuneModdingHelper.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() + { + AttachEvents(); + } + + static AbstractUIGroupPresenter() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(AbstractUIGroupPresenter), new FrameworkPropertyMetadata(typeof(AbstractUIGroupPresenter))); + } + + 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; + + if (GetTemplateChild("GroupItemsControl") is ItemsControl ic) + { + ic.ItemsSource = ViewModel.Items; + } + } + } +} diff --git a/ZuneModdingHelper/AbstractUI/Controls/CustomControl1.cs b/ZuneModdingHelper/AbstractUI/Controls/CustomControl1.cs new file mode 100644 index 0000000..519f2da --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Controls/CustomControl1.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace ZuneModdingHelper.AbstractUI.Controls +{ + /// + /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file. + /// + /// Step 1a) Using this custom control in a XAML file that exists in the current project. + /// Add this XmlNamespace attribute to the root element of the markup file where it is + /// to be used: + /// + /// xmlns:MyNamespace="clr-namespace:ZuneModdingHelper.AbstractUI.Controls" + /// + /// + /// Step 1b) Using this custom control in a XAML file that exists in a different project. + /// Add this XmlNamespace attribute to the root element of the markup file where it is + /// to be used: + /// + /// xmlns:MyNamespace="clr-namespace:ZuneModdingHelper.AbstractUI.Controls;assembly=ZuneModdingHelper.AbstractUI.Controls" + /// + /// You will also need to add a project reference from the project where the XAML file lives + /// to this project and Rebuild to avoid compilation errors: + /// + /// Right click on the target project in the Solution Explorer and + /// "Add Reference"->"Projects"->[Browse to and select this project] + /// + /// + /// Step 2) + /// Go ahead and use your control in the XAML file. + /// + /// + /// + /// + public class CustomControl1 : Control + { + static CustomControl1() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1))); + } + } +} diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractBooleanUIElementStyle.xaml b/ZuneModdingHelper/AbstractUI/Themes/AbstractBooleanUIElementStyle.xaml new file mode 100644 index 0000000..aa93dfc --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractBooleanUIElementStyle.xaml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractBooleanUIElementStyle.xaml.cs b/ZuneModdingHelper/AbstractUI/Themes/AbstractBooleanUIElementStyle.xaml.cs new file mode 100644 index 0000000..186e470 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractBooleanUIElementStyle.xaml.cs @@ -0,0 +1,12 @@ +using System.Windows; + +namespace ZuneModdingHelper.AbstractUI.Themes +{ + public sealed partial class AbstractBooleanUIElementStyle : ResourceDictionary + { + public AbstractBooleanUIElementStyle() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractButtonStyle.xaml b/ZuneModdingHelper/AbstractUI/Themes/AbstractButtonStyle.xaml new file mode 100644 index 0000000..8c53466 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractButtonStyle.xaml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractButtonStyle.xaml.cs b/ZuneModdingHelper/AbstractUI/Themes/AbstractButtonStyle.xaml.cs new file mode 100644 index 0000000..7131405 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractButtonStyle.xaml.cs @@ -0,0 +1,12 @@ +using System.Windows; + +namespace ZuneModdingHelper.AbstractUI.Themes +{ + public sealed partial class AbstractButtonStyle : ResourceDictionary + { + public AbstractButtonStyle() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractButtonTemplateSelector.cs b/ZuneModdingHelper/AbstractUI/Themes/AbstractButtonTemplateSelector.cs new file mode 100644 index 0000000..b18194f --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractButtonTemplateSelector.cs @@ -0,0 +1,68 @@ +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 ZuneModdingHelper.AbstractUI.Controls; +using System.Windows.Data; +using System.Globalization; + +namespace ZuneModdingHelper.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, string language) + { + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractDataListStyle.xaml.cs b/ZuneModdingHelper/AbstractUI/Themes/AbstractDataListStyle.xaml.cs new file mode 100644 index 0000000..b9ec1b2 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractDataListStyle.xaml.cs @@ -0,0 +1,12 @@ +using System.Windows; + +namespace ZuneModdingHelper.AbstractUI.Themes +{ + public sealed partial class AbstractDataListStyle : ResourceDictionary + { + public AbstractDataListStyle() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractDataListTypeTemplateSelector.cs b/ZuneModdingHelper/AbstractUI/Themes/AbstractDataListTypeTemplateSelector.cs new file mode 100644 index 0000000..4fb66d0 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractDataListTypeTemplateSelector.cs @@ -0,0 +1,53 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using OwlCore.AbstractUI.ViewModels; +using Microsoft.Toolkit.Diagnostics; +using OwlCore.AbstractUI.Models; + +namespace ZuneModdingHelper.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 AbstractMutableDataListViewModel mutableViewModel) + { + return mutableViewModel.PreferredDisplayMode switch + { + AbstractDataListPreferredDisplayMode.Grid => GridTemplate ?? ThrowHelper.ThrowArgumentNullException(), + AbstractDataListPreferredDisplayMode.List => ListTemplate ?? ThrowHelper.ThrowArgumentNullException(), + _ => throw new NotImplementedException(), + }; + } + + if (item is AbstractDataListViewModel viewModel) + { + return viewModel.PreferredDisplayMode switch + { + AbstractDataListPreferredDisplayMode.Grid => GridTemplate ?? ThrowHelper.ThrowArgumentNullException(), + AbstractDataListPreferredDisplayMode.List => ListTemplate ?? ThrowHelper.ThrowArgumentNullException(), + _ => throw new NotImplementedException(), + }; + } + + return base.SelectTemplate(item, container); + } + } +} \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractMultiChoiceTypeTemplateSelector.cs b/ZuneModdingHelper/AbstractUI/Themes/AbstractMultiChoiceTypeTemplateSelector.cs new file mode 100644 index 0000000..2623949 --- /dev/null +++ b/ZuneModdingHelper/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 ZuneModdingHelper.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 base.SelectTemplate(item, container); + } + } +} \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractMultiChoiceUIElementStyle.xaml b/ZuneModdingHelper/AbstractUI/Themes/AbstractMultiChoiceUIElementStyle.xaml new file mode 100644 index 0000000..7945454 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractMultiChoiceUIElementStyle.xaml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractMultiChoiceUIElementStyle.xaml.cs b/ZuneModdingHelper/AbstractUI/Themes/AbstractMultiChoiceUIElementStyle.xaml.cs new file mode 100644 index 0000000..4e5cc35 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractMultiChoiceUIElementStyle.xaml.cs @@ -0,0 +1,12 @@ +using System.Windows; + +namespace ZuneModdingHelper.AbstractUI.Themes +{ + public sealed partial class AbstractMultiChoiceUIElementStyle : ResourceDictionary + { + public AbstractMultiChoiceUIElementStyle() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractMutableDataListStyle.xaml b/ZuneModdingHelper/AbstractUI/Themes/AbstractMutableDataListStyle.xaml new file mode 100644 index 0000000..2cb0822 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractMutableDataListStyle.xaml @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractMutableDataListStyle.xaml.cs b/ZuneModdingHelper/AbstractUI/Themes/AbstractMutableDataListStyle.xaml.cs new file mode 100644 index 0000000..7e0678f --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractMutableDataListStyle.xaml.cs @@ -0,0 +1,12 @@ +using System.Windows; + +namespace ZuneModdingHelper.AbstractUI.Themes +{ + public sealed partial class AbstractMutableDataListStyle : ResourceDictionary + { + public AbstractMutableDataListStyle() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractProgressUIElementStyle.xaml b/ZuneModdingHelper/AbstractUI/Themes/AbstractProgressUIElementStyle.xaml new file mode 100644 index 0000000..b13daa1 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractProgressUIElementStyle.xaml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractProgressUIElementStyle.xaml.cs b/ZuneModdingHelper/AbstractUI/Themes/AbstractProgressUIElementStyle.xaml.cs new file mode 100644 index 0000000..ee96451 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractProgressUIElementStyle.xaml.cs @@ -0,0 +1,12 @@ +using System.Windows; + +namespace ZuneModdingHelper.AbstractUI.Themes +{ + public sealed partial class AbstractProgressUIElementStyle : ResourceDictionary + { + public AbstractProgressUIElementStyle() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractRichTextBlockStyle.xaml b/ZuneModdingHelper/AbstractUI/Themes/AbstractRichTextBlockStyle.xaml new file mode 100644 index 0000000..3def781 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractRichTextBlockStyle.xaml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractRichTextBlockStyle.xaml.cs b/ZuneModdingHelper/AbstractUI/Themes/AbstractRichTextBlockStyle.xaml.cs new file mode 100644 index 0000000..07df333 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractRichTextBlockStyle.xaml.cs @@ -0,0 +1,12 @@ +using System.Windows; + +namespace ZuneModdingHelper.AbstractUI.Themes +{ + public sealed partial class AbstractRichTextBlockStyle : ResourceDictionary + { + public AbstractRichTextBlockStyle() + { + InitializeComponent(); + } + } +} diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractTextBoxStyle.xaml b/ZuneModdingHelper/AbstractUI/Themes/AbstractTextBoxStyle.xaml new file mode 100644 index 0000000..80d3ba0 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractTextBoxStyle.xaml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractTextBoxStyle.xaml.cs b/ZuneModdingHelper/AbstractUI/Themes/AbstractTextBoxStyle.xaml.cs new file mode 100644 index 0000000..ddf4b15 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractTextBoxStyle.xaml.cs @@ -0,0 +1,12 @@ +using System.Windows; + +namespace ZuneModdingHelper.AbstractUI.Themes +{ + public sealed partial class AbstractTextBoxStyle : ResourceDictionary + { + public AbstractTextBoxStyle() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractUIGroupPresenterStyle.xaml b/ZuneModdingHelper/AbstractUI/Themes/AbstractUIGroupPresenterStyle.xaml new file mode 100644 index 0000000..76c7021 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractUIGroupPresenterStyle.xaml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractUIGroupPresenterStyle.xaml.cs b/ZuneModdingHelper/AbstractUI/Themes/AbstractUIGroupPresenterStyle.xaml.cs new file mode 100644 index 0000000..b3945b8 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractUIGroupPresenterStyle.xaml.cs @@ -0,0 +1,12 @@ +using System.Windows; + +namespace ZuneModdingHelper.AbstractUI.Themes +{ + public sealed partial class AbstractUIGroupPresenterStyle : ResourceDictionary + { + public AbstractUIGroupPresenterStyle() + { + InitializeComponent(); + } + } +} diff --git a/ZuneModdingHelper/AbstractUI/Themes/AbstractUIResources.xaml b/ZuneModdingHelper/AbstractUI/Themes/AbstractUIResources.xaml new file mode 100644 index 0000000..9c2dcc0 --- /dev/null +++ b/ZuneModdingHelper/AbstractUI/Themes/AbstractUIResources.xaml @@ -0,0 +1,12 @@ + + + 24 + + 16 + Light + 0.5 + + \ No newline at end of file diff --git a/ZuneModdingHelper/AbstractUIGroupDialog.xaml b/ZuneModdingHelper/AbstractUIGroupDialog.xaml new file mode 100644 index 0000000..c40bee8 --- /dev/null +++ b/ZuneModdingHelper/AbstractUIGroupDialog.xaml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + +