Skip to content

Commit

Permalink
Refactor OptionsDialog to use MVVM style
Browse files Browse the repository at this point in the history
  • Loading branch information
tom-englert committed Sep 7, 2024
1 parent 6544a01 commit df7c5cf
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 93 deletions.
4 changes: 3 additions & 1 deletion ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

namespace ICSharpCode.ILSpy.ReadyToRun
{
[ExportOptionPage(Title = nameof(global::ILSpy.ReadyToRun.Properties.Resources.ReadyToRun), Order = 40)]
[ExportOptionPage(Order = 40)]
[PartCreationPolicy(CreationPolicy.NonShared)]
partial class ReadyToRunOptionPage : UserControl, IOptionPage
{
Expand All @@ -35,6 +35,8 @@ public ReadyToRunOptionPage()
InitializeComponent();
}

public string Title => global::ILSpy.ReadyToRun.Properties.Resources.ReadyToRun;

public void Load(ILSpySettings settings)
{
Options s = new Options();
Expand Down
4 changes: 3 additions & 1 deletion ILSpy/Options/DecompilerSettingsPanel.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace ICSharpCode.ILSpy.Options
/// <summary>
/// Interaction logic for DecompilerSettingsPanel.xaml
/// </summary>
[ExportOptionPage(Title = nameof(Properties.Resources.Decompiler), Order = 10)]
[ExportOptionPage(Order = 10)]
[PartCreationPolicy(CreationPolicy.NonShared)]
internal partial class DecompilerSettingsPanel : IOptionPage
{
Expand All @@ -35,6 +35,8 @@ public DecompilerSettingsPanel()
InitializeComponent();
}

public string Title => Properties.Resources.Decompiler;

public static Decompiler.DecompilerSettings LoadDecompilerSettings(ILSpySettings settings)
{
return ISettingsProvider.LoadDecompilerSettings(settings);
Expand Down
4 changes: 3 additions & 1 deletion ILSpy/Options/DisplaySettingsPanel.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ namespace ICSharpCode.ILSpy.Options
/// <summary>
/// Interaction logic for DisplaySettingsPanel.xaml
/// </summary>
[ExportOptionPage(Title = nameof(Properties.Resources.Display), Order = 20)]
[ExportOptionPage(Order = 20)]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class DisplaySettingsPanel : UserControl, IOptionPage
{
Expand Down Expand Up @@ -67,6 +67,8 @@ public DisplaySettingsPanel()
);
}

public string Title => Properties.Resources.Display;

public void Load(ILSpySettings settings)
{
this.DataContext = LoadDisplaySettings(settings);
Expand Down
4 changes: 3 additions & 1 deletion ILSpy/Options/MiscSettingsPanel.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace ICSharpCode.ILSpy.Options
/// <summary>
/// Interaction logic for MiscSettingsPanel.xaml
/// </summary>
[ExportOptionPage(Title = nameof(Properties.Resources.Misc), Order = 30)]
[ExportOptionPage(Order = 30)]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class MiscSettingsPanel : UserControl, IOptionPage
{
Expand All @@ -36,6 +36,8 @@ public MiscSettingsPanel()
InitializeComponent();
}

public string Title => Properties.Resources.Misc;

public void Load(ILSpySettings settings)
{
this.DataContext = new MiscSettingsViewModel(SettingsService.Instance.MiscSettings);
Expand Down
48 changes: 28 additions & 20 deletions ILSpy/Options/OptionsDialog.xaml
Original file line number Diff line number Diff line change
@@ -1,35 +1,43 @@
<Window x:Class="ICSharpCode.ILSpy.Options.OptionsDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
xmlns:options="clr-namespace:ICSharpCode.ILSpy.Options"
Style="{DynamicResource DialogWindow}"
xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
xmlns:options="clr-namespace:ICSharpCode.ILSpy.Options"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:toms="urn:TomsToolbox"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance options:OptionsDialogViewModel}"
Style="{StaticResource DialogWindow}"
WindowStartupLocation="CenterOwner"
ResizeMode="CanResizeWithGrip"
Title="{x:Static properties:Resources.Options}" Height="500" Width="600">
<Grid>
<Grid.RowDefinitions>
<RowDefinition
Height="1*" />
<RowDefinition
Height="Auto" />
</Grid.RowDefinitions>
<TabControl Name="tabControl" SelectedValuePath="Content">
<DockPanel>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right" Margin="12,8">
<Button Margin="2,0"
Command="{Binding ResetDefaultsCommand}"
Content="{x:Static properties:Resources.ResetToDefaults}" />
<Button IsDefault="True" Margin="2,0"
toms:Button.DialogResult="true"
Command="{Binding CommitCommand}"
Content="{x:Static properties:Resources.OK}" />
<Button IsCancel="True" Margin="2,0"
Content="{x:Static properties:Resources.Cancel}" />
</StackPanel>
<TabControl ItemsSource="{Binding OptionPages}"
SelectedIndex="0"
SelectedValuePath="Content"
SelectedValue="{Binding SelectedPage}">
<TabControl.ItemTemplate>
<DataTemplate DataType="options:TabItemViewModel">
<TextBlock Text="{Binding Header}" />
<DataTemplate DataType="options:OptionsItemViewModel">
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="options:TabItemViewModel">
<DataTemplate DataType="options:OptionsItemViewModel">
<ContentPresenter Content="{Binding Content}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="12,8">
<Button Margin="2,0" Name="defaultsButton" Click="DefaultsButton_Click" Content="{x:Static properties:Resources.ResetToDefaults}" />
<Button IsDefault="True" Margin="2,0" Name="okButton" Click="OKButton_Click" Content="{x:Static properties:Resources.OK}" />
<Button IsCancel="True" Margin="2,0" Content="{x:Static properties:Resources.Cancel}" />
</StackPanel>
</Grid>
</DockPanel>
</Window>
73 changes: 6 additions & 67 deletions ILSpy/Options/OptionsDialog.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,103 +18,42 @@

using System;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows;
using System.Xml.Linq;

using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpyX.Settings;

using TomsToolbox.Composition;

namespace ICSharpCode.ILSpy.Options
{
public class TabItemViewModel
{
public TabItemViewModel(string header, UIElement content)
{
Header = header;
Content = content;
}

public string Header { get; }

public UIElement Content { get; }
}

/// <summary>
/// Interaction logic for OptionsDialog.xaml
/// </summary>
public partial class OptionsDialog : Window
public sealed partial class OptionsDialog
{
readonly IExport<UIElement, IOptionsMetadata>[] optionPages;

public OptionsDialog()
{
DataContext = new OptionsDialogViewModel();
InitializeComponent();
var ep = App.ExportProvider;
this.optionPages = ep.GetExports<UIElement, IOptionsMetadata>("OptionPages").ToArray();
ILSpySettings settings = SettingsService.Instance.SpySettings;
foreach (var optionPage in optionPages.OrderBy(p => p.Metadata.Order))
{
var tabItem = new TabItemViewModel(Util.ResourceHelper.GetString(optionPage.Metadata.Title), optionPage.Value);

tabControl.Items.Add(tabItem);

IOptionPage page = optionPage.Value as IOptionPage;
if (page != null)
page.Load(settings);
}
}

void OKButton_Click(object sender, RoutedEventArgs e)
{
SettingsService.Instance.SpySettings.Update(
root => {
foreach (var optionPage in optionPages)
{
IOptionPage page = optionPage.Value as IOptionPage;
if (page != null)
page.Save(root);
}
});
this.DialogResult = true;
Close();
}

private void DefaultsButton_Click(object sender, RoutedEventArgs e)
{
if (MessageBox.Show(Properties.Resources.ResetToDefaultsConfirmationMessage, "ILSpy", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
var page = tabControl.SelectedValue as IOptionPage;
if (page != null)
page.LoadDefaults();
}
}
}

public interface IOptionsMetadata
{
string Title { get; }
int Order { get; }
}

public interface IOptionPage
{
string Title { get; }
void Load(ILSpySettings settings);
void Save(XElement root);
void LoadDefaults();
}

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ExportOptionPageAttribute : ExportAttribute
[AttributeUsage(AttributeTargets.Class)]
public sealed class ExportOptionPageAttribute() : ExportAttribute("OptionPages", typeof(IOptionPage)), IOptionsMetadata
{
public ExportOptionPageAttribute() : base("OptionPages", typeof(UIElement))
{ }

public string Title { get; set; }

public int Order { get; set; }
}

Expand All @@ -125,7 +64,7 @@ sealed class ShowOptionsCommand : SimpleCommand
public override void Execute(object parameter)
{
OptionsDialog dlg = new() {
Owner = MainWindow.Instance
Owner = MainWindow.Instance,
};
if (dlg.ShowDialog() == true)
{
Expand Down
71 changes: 71 additions & 0 deletions ILSpy/Options/OptionsDialogViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System.Linq;
using System.Windows;
using System.Windows.Input;

using TomsToolbox.Essentials;
using TomsToolbox.Wpf;

#nullable enable

namespace ICSharpCode.ILSpy.Options
{
public class OptionsItemViewModel(IOptionPage content) : ObservableObject
{
public string Title { get; } = content.Title;

public IOptionPage Content { get; } = content;
}

public class OptionsDialogViewModel : ObservableObject
{
private IOptionPage? selectedPage;

private readonly IOptionPage[] optionPages = App.ExportProvider.GetExports<IOptionPage, IOptionsMetadata>("OptionPages")
.OrderBy(page => page.Metadata?.Order)
.Select(item => item.Value)
.ExceptNullItems()
.ToArray();

public OptionsDialogViewModel()
{
var settings = SettingsService.Instance.SpySettings;

foreach (var optionPage in optionPages)
{
optionPage.Load(settings);
}

OptionPages = optionPages.Select(page => new OptionsItemViewModel(page)).ToArray();
SelectedPage = optionPages.FirstOrDefault();
}

public OptionsItemViewModel[] OptionPages { get; }

public IOptionPage? SelectedPage {
get => selectedPage;
set => SetProperty(ref selectedPage, value);
}

public ICommand CommitCommand => new DelegateCommand(Commit);

public ICommand ResetDefaultsCommand => new DelegateCommand(ResetDefaults);

private void ResetDefaults()
{
if (MessageBox.Show(Properties.Resources.ResetToDefaultsConfirmationMessage, "ILSpy", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
SelectedPage?.LoadDefaults();
}
}

private void Commit()
{
SettingsService.Instance.SpySettings.Update(root => {
foreach (var optionPage in optionPages)
{
optionPage.Save(root);
}
});
}
}
}
5 changes: 3 additions & 2 deletions TestPlugin/CustomOptionPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@
using System.Windows.Controls;
using System.Xml.Linq;

using ICSharpCode.ILSpy;
using ICSharpCode.ILSpy.Options;
using ICSharpCode.ILSpyX.Settings;

namespace TestPlugin
{
[ExportOptionPage(Title = "TestPlugin", Order = 0)]
[ExportOptionPage(Order = 0)]
[PartCreationPolicy(CreationPolicy.NonShared)]
partial class CustomOptionPage : UserControl, IOptionPage
{
Expand All @@ -23,6 +22,8 @@ public CustomOptionPage()
InitializeComponent();
}

public string Title => "TestPlugin";

public void Load(ILSpySettings settings)
{
// For loading options, use ILSpySetting's indexer.
Expand Down

0 comments on commit df7c5cf

Please sign in to comment.