Browse Source

Refactor OptionsDialog to use MVVM style

pull/3274/head
tom-englert 1 year ago committed by tom-englert
parent
commit
ca687e2394
  1. 4
      ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs
  2. 4
      ILSpy/Options/DecompilerSettingsPanel.xaml.cs
  3. 4
      ILSpy/Options/DisplaySettingsPanel.xaml.cs
  4. 4
      ILSpy/Options/MiscSettingsPanel.xaml.cs
  5. 48
      ILSpy/Options/OptionsDialog.xaml
  6. 73
      ILSpy/Options/OptionsDialog.xaml.cs
  7. 71
      ILSpy/Options/OptionsDialogViewModel.cs
  8. 5
      TestPlugin/CustomOptionPage.xaml.cs

4
ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs

@ -26,7 +26,7 @@ using ICSharpCode.ILSpyX.Settings;
namespace ICSharpCode.ILSpy.ReadyToRun namespace ICSharpCode.ILSpy.ReadyToRun
{ {
[ExportOptionPage(Title = nameof(global::ILSpy.ReadyToRun.Properties.Resources.ReadyToRun), Order = 40)] [ExportOptionPage(Order = 40)]
[PartCreationPolicy(CreationPolicy.NonShared)] [PartCreationPolicy(CreationPolicy.NonShared)]
partial class ReadyToRunOptionPage : UserControl, IOptionPage partial class ReadyToRunOptionPage : UserControl, IOptionPage
{ {
@ -35,6 +35,8 @@ namespace ICSharpCode.ILSpy.ReadyToRun
InitializeComponent(); InitializeComponent();
} }
public string Title => global::ILSpy.ReadyToRun.Properties.Resources.ReadyToRun;
public void Load(ILSpySettings settings) public void Load(ILSpySettings settings)
{ {
Options s = new Options(); Options s = new Options();

4
ILSpy/Options/DecompilerSettingsPanel.xaml.cs

@ -26,7 +26,7 @@ namespace ICSharpCode.ILSpy.Options
/// <summary> /// <summary>
/// Interaction logic for DecompilerSettingsPanel.xaml /// Interaction logic for DecompilerSettingsPanel.xaml
/// </summary> /// </summary>
[ExportOptionPage(Title = nameof(Properties.Resources.Decompiler), Order = 10)] [ExportOptionPage(Order = 10)]
[PartCreationPolicy(CreationPolicy.NonShared)] [PartCreationPolicy(CreationPolicy.NonShared)]
internal partial class DecompilerSettingsPanel : IOptionPage internal partial class DecompilerSettingsPanel : IOptionPage
{ {
@ -35,6 +35,8 @@ namespace ICSharpCode.ILSpy.Options
InitializeComponent(); InitializeComponent();
} }
public string Title => Properties.Resources.Decompiler;
public static Decompiler.DecompilerSettings LoadDecompilerSettings(ILSpySettings settings) public static Decompiler.DecompilerSettings LoadDecompilerSettings(ILSpySettings settings)
{ {
return ISettingsProvider.LoadDecompilerSettings(settings); return ISettingsProvider.LoadDecompilerSettings(settings);

4
ILSpy/Options/DisplaySettingsPanel.xaml.cs

@ -34,7 +34,7 @@ namespace ICSharpCode.ILSpy.Options
/// <summary> /// <summary>
/// Interaction logic for DisplaySettingsPanel.xaml /// Interaction logic for DisplaySettingsPanel.xaml
/// </summary> /// </summary>
[ExportOptionPage(Title = nameof(Properties.Resources.Display), Order = 20)] [ExportOptionPage(Order = 20)]
[PartCreationPolicy(CreationPolicy.NonShared)] [PartCreationPolicy(CreationPolicy.NonShared)]
public partial class DisplaySettingsPanel : UserControl, IOptionPage public partial class DisplaySettingsPanel : UserControl, IOptionPage
{ {
@ -67,6 +67,8 @@ namespace ICSharpCode.ILSpy.Options
); );
} }
public string Title => Properties.Resources.Display;
public void Load(ILSpySettings settings) public void Load(ILSpySettings settings)
{ {
this.DataContext = LoadDisplaySettings(settings); this.DataContext = LoadDisplaySettings(settings);

4
ILSpy/Options/MiscSettingsPanel.xaml.cs

@ -27,7 +27,7 @@ namespace ICSharpCode.ILSpy.Options
/// <summary> /// <summary>
/// Interaction logic for MiscSettingsPanel.xaml /// Interaction logic for MiscSettingsPanel.xaml
/// </summary> /// </summary>
[ExportOptionPage(Title = nameof(Properties.Resources.Misc), Order = 30)] [ExportOptionPage(Order = 30)]
[PartCreationPolicy(CreationPolicy.NonShared)] [PartCreationPolicy(CreationPolicy.NonShared)]
public partial class MiscSettingsPanel : UserControl, IOptionPage public partial class MiscSettingsPanel : UserControl, IOptionPage
{ {
@ -36,6 +36,8 @@ namespace ICSharpCode.ILSpy.Options
InitializeComponent(); InitializeComponent();
} }
public string Title => Properties.Resources.Misc;
public void Load(ILSpySettings settings) public void Load(ILSpySettings settings)
{ {
this.DataContext = new MiscSettingsViewModel(SettingsService.Instance.MiscSettings); this.DataContext = new MiscSettingsViewModel(SettingsService.Instance.MiscSettings);

48
ILSpy/Options/OptionsDialog.xaml

@ -1,35 +1,43 @@
<Window x:Class="ICSharpCode.ILSpy.Options.OptionsDialog" <Window x:Class="ICSharpCode.ILSpy.Options.OptionsDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties" xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
xmlns:options="clr-namespace:ICSharpCode.ILSpy.Options" xmlns:options="clr-namespace:ICSharpCode.ILSpy.Options"
Style="{DynamicResource DialogWindow}" 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" WindowStartupLocation="CenterOwner"
ResizeMode="CanResizeWithGrip" ResizeMode="CanResizeWithGrip"
Title="{x:Static properties:Resources.Options}" Height="500" Width="600"> Title="{x:Static properties:Resources.Options}" Height="500" Width="600">
<Grid> <DockPanel>
<Grid.RowDefinitions> <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right" Margin="12,8">
<RowDefinition <Button Margin="2,0"
Height="1*" /> Command="{Binding ResetDefaultsCommand}"
<RowDefinition Content="{x:Static properties:Resources.ResetToDefaults}" />
Height="Auto" /> <Button IsDefault="True" Margin="2,0"
</Grid.RowDefinitions> toms:Button.DialogResult="true"
<TabControl Name="tabControl" SelectedValuePath="Content"> 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> <TabControl.ItemTemplate>
<DataTemplate DataType="options:TabItemViewModel"> <DataTemplate DataType="options:OptionsItemViewModel">
<TextBlock Text="{Binding Header}" /> <TextBlock Text="{Binding Title}" />
</DataTemplate> </DataTemplate>
</TabControl.ItemTemplate> </TabControl.ItemTemplate>
<TabControl.ContentTemplate> <TabControl.ContentTemplate>
<DataTemplate DataType="options:TabItemViewModel"> <DataTemplate DataType="options:OptionsItemViewModel">
<ContentPresenter Content="{Binding Content}" /> <ContentPresenter Content="{Binding Content}" />
</DataTemplate> </DataTemplate>
</TabControl.ContentTemplate> </TabControl.ContentTemplate>
</TabControl> </TabControl>
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="12,8"> </DockPanel>
<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>
</Window> </Window>

73
ILSpy/Options/OptionsDialog.xaml.cs

@ -18,103 +18,42 @@
using System; using System;
using System.ComponentModel.Composition; using System.ComponentModel.Composition;
using System.Linq;
using System.Windows;
using System.Xml.Linq; using System.Xml.Linq;
using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpyX.Settings; using ICSharpCode.ILSpyX.Settings;
using TomsToolbox.Composition;
namespace ICSharpCode.ILSpy.Options 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> /// <summary>
/// Interaction logic for OptionsDialog.xaml /// Interaction logic for OptionsDialog.xaml
/// </summary> /// </summary>
public partial class OptionsDialog : Window public sealed partial class OptionsDialog
{ {
readonly IExport<UIElement, IOptionsMetadata>[] optionPages;
public OptionsDialog() public OptionsDialog()
{ {
DataContext = new OptionsDialogViewModel();
InitializeComponent(); 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 public interface IOptionsMetadata
{ {
string Title { get; }
int Order { get; } int Order { get; }
} }
public interface IOptionPage public interface IOptionPage
{ {
string Title { get; }
void Load(ILSpySettings settings); void Load(ILSpySettings settings);
void Save(XElement root); void Save(XElement root);
void LoadDefaults(); void LoadDefaults();
} }
[MetadataAttribute] [MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Class)]
public class ExportOptionPageAttribute : ExportAttribute 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; } public int Order { get; set; }
} }
@ -125,7 +64,7 @@ namespace ICSharpCode.ILSpy.Options
public override void Execute(object parameter) public override void Execute(object parameter)
{ {
OptionsDialog dlg = new() { OptionsDialog dlg = new() {
Owner = MainWindow.Instance Owner = MainWindow.Instance,
}; };
if (dlg.ShowDialog() == true) if (dlg.ShowDialog() == true)
{ {

71
ILSpy/Options/OptionsDialogViewModel.cs

@ -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
TestPlugin/CustomOptionPage.xaml.cs

@ -6,13 +6,12 @@ using System.ComponentModel.Composition;
using System.Windows.Controls; using System.Windows.Controls;
using System.Xml.Linq; using System.Xml.Linq;
using ICSharpCode.ILSpy;
using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpy.Options;
using ICSharpCode.ILSpyX.Settings; using ICSharpCode.ILSpyX.Settings;
namespace TestPlugin namespace TestPlugin
{ {
[ExportOptionPage(Title = "TestPlugin", Order = 0)] [ExportOptionPage(Order = 0)]
[PartCreationPolicy(CreationPolicy.NonShared)] [PartCreationPolicy(CreationPolicy.NonShared)]
partial class CustomOptionPage : UserControl, IOptionPage partial class CustomOptionPage : UserControl, IOptionPage
{ {
@ -23,6 +22,8 @@ namespace TestPlugin
InitializeComponent(); InitializeComponent();
} }
public string Title => "TestPlugin";
public void Load(ILSpySettings settings) public void Load(ILSpySettings settings)
{ {
// For loading options, use ILSpySetting's indexer. // For loading options, use ILSpySetting's indexer.

Loading…
Cancel
Save