Browse Source

Refactor AnalyzerTreeView into View/ViewModel

pull/3257/head
tom-englert 9 months ago
parent
commit
e241444113
  1. 27
      ILSpy/Analyzers/AnalyzeCommand.cs
  2. 42
      ILSpy/Analyzers/AnalyzerRootNode.cs
  3. 158
      ILSpy/Analyzers/AnalyzerTreeView.cs
  4. 23
      ILSpy/Analyzers/AnalyzerTreeView.xaml
  5. 36
      ILSpy/Analyzers/AnalyzerTreeView.xaml.cs
  6. 130
      ILSpy/Analyzers/AnalyzerTreeViewModel.cs
  7. 18
      ILSpy/Docking/DockWorkspace.cs
  8. 2
      ILSpy/MainWindow.xaml
  9. 3
      ILSpy/MainWindow.xaml.cs
  10. 25
      ILSpy/ViewModels/PaneModel.cs
  11. 4
      ILSpy/ViewModels/TabPageModel.cs

27
ILSpy/Analyzers/AnalyzeCommand.cs

@ -27,9 +27,9 @@ namespace ICSharpCode.ILSpy.Analyzers
{ {
[ExportContextMenuEntry(Header = nameof(Resources.Analyze), Icon = "Images/Search", Category = nameof(Resources.Analyze), InputGestureText = "Ctrl+R", Order = 100)] [ExportContextMenuEntry(Header = nameof(Resources.Analyze), Icon = "Images/Search", Category = nameof(Resources.Analyze), InputGestureText = "Ctrl+R", Order = 100)]
[PartCreationPolicy(CreationPolicy.Shared)] [PartCreationPolicy(CreationPolicy.Shared)]
internal sealed class AnalyzeCommand : SimpleCommand, IContextMenuEntry internal sealed class AnalyzeContextMenuCommand : IContextMenuEntry
{ {
private static readonly AnalyzerTreeView AnalyzerTreeView = App.ExportProvider.GetExportedValue<AnalyzerTreeView>(); private static readonly AnalyzerTreeViewModel AnalyzerTreeView = App.ExportProvider.GetExportedValue<AnalyzerTreeViewModel>();
public bool IsVisible(TextViewContext context) public bool IsVisible(TextViewContext context)
{ {
@ -70,29 +70,22 @@ namespace ICSharpCode.ILSpy.Analyzers
AnalyzerTreeView.Analyze(entity); AnalyzerTreeView.Analyze(entity);
} }
} }
}
internal sealed class AnalyzeCommand : SimpleCommand
{
private static readonly AnalyzerTreeViewModel AnalyzerTreeView = App.ExportProvider.GetExportedValue<AnalyzerTreeViewModel>();
public override bool CanExecute(object parameter) public override bool CanExecute(object parameter)
{ {
return AnalyzerTreeView.IsKeyboardFocusWithin return MainWindow.Instance.SelectedNodes.All(n => n is IMemberTreeNode);
? AnalyzerTreeView.SelectedItems.OfType<object>().All(n => n is IMemberTreeNode)
: MainWindow.Instance.SelectedNodes.All(n => n is IMemberTreeNode);
} }
public override void Execute(object parameter) public override void Execute(object parameter)
{ {
if (AnalyzerTreeView.IsKeyboardFocusWithin) foreach (var node in MainWindow.Instance.SelectedNodes.OfType<IMemberTreeNode>())
{ {
foreach (var node in AnalyzerTreeView.SelectedItems.OfType<IMemberTreeNode>().ToArray()) AnalyzerTreeView.Analyze(node.Member);
{
AnalyzerTreeView.Analyze(node.Member);
}
}
else
{
foreach (var node in MainWindow.Instance.SelectedNodes.OfType<IMemberTreeNode>())
{
AnalyzerTreeView.Analyze(node.Member);
}
} }
} }
} }

42
ILSpy/Analyzers/AnalyzerRootNode.cs

@ -0,0 +1,42 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using ICSharpCode.ILSpy.Util;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.TreeView;
namespace ICSharpCode.ILSpy.Analyzers;
public sealed class AnalyzerRootNode : AnalyzerTreeNode
{
public AnalyzerRootNode()
{
MessageBus<CurrentAssemblyListChangedEventArgs>.Subscribers += (sender, e) => CurrentAssemblyList_Changed(sender, e);
}
void CurrentAssemblyList_Changed(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Reset)
{
this.Children.Clear();
}
else
{
var removedAssemblies = e.OldItems?.Cast<LoadedAssembly>().ToArray() ?? [];
var addedAssemblies = e.NewItems?.Cast<LoadedAssembly>().ToArray() ?? [];
HandleAssemblyListChanged(removedAssemblies, addedAssemblies);
}
}
public override bool HandleAssemblyListChanged(ICollection<LoadedAssembly> removedAssemblies, ICollection<LoadedAssembly> addedAssemblies)
{
this.Children.RemoveAll(
delegate (SharpTreeNode n) {
AnalyzerTreeNode an = n as AnalyzerTreeNode;
return an == null || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies);
});
return true;
}
}

158
ILSpy/Analyzers/AnalyzerTreeView.cs

@ -1,158 +0,0 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.Analyzers.TreeNodes;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.ViewModels;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpy.Controls.TreeView;
using ICSharpCode.ILSpy.Util;
using ICSharpCode.ILSpyX.TreeView;
using TomsToolbox.Wpf.Composition.Mef;
namespace ICSharpCode.ILSpy.Analyzers
{
/// <summary>
/// Analyzer tree view.
/// </summary>
[DataTemplate(typeof(AnalyzerPaneModel))]
[PartCreationPolicy(CreationPolicy.Shared)]
[Export]
public class AnalyzerTreeView : SharpTreeView
{
public AnalyzerTreeView()
{
this.ShowRoot = false;
this.BorderThickness = new();
this.Root = new AnalyzerRootNode();
ContextMenuProvider.Add(this);
MessageBus<CurrentAssemblyListChangedEventArgs>.Subscribers += (sender, e) => CurrentAssemblyList_Changed(sender, e);
}
void CurrentAssemblyList_Changed(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Reset)
{
this.Root.Children.Clear();
}
else
{
List<LoadedAssembly> removedAssemblies = new List<LoadedAssembly>();
if (e.OldItems != null)
removedAssemblies.AddRange(e.OldItems.Cast<LoadedAssembly>());
List<LoadedAssembly> addedAssemblies = new List<LoadedAssembly>();
if (e.NewItems != null)
addedAssemblies.AddRange(e.NewItems.Cast<LoadedAssembly>());
((AnalyzerRootNode)this.Root).HandleAssemblyListChanged(removedAssemblies, addedAssemblies);
}
}
public void Show()
{
DockWorkspace.Instance.ShowToolPane(AnalyzerPaneModel.PaneContentId);
}
public void Show(AnalyzerTreeNode node)
{
Show();
node.IsExpanded = true;
this.Root.Children.Add(node);
this.SelectedItem = node;
this.FocusNode(node);
}
public void ShowOrFocus(AnalyzerTreeNode node)
{
if (node is AnalyzerEntityTreeNode)
{
var an = node as AnalyzerEntityTreeNode;
var found = this.Root.Children.OfType<AnalyzerEntityTreeNode>().FirstOrDefault(n => n.Member == an.Member);
if (found != null)
{
Show();
found.IsExpanded = true;
this.SelectedItem = found;
this.FocusNode(found);
return;
}
}
Show(node);
}
public void Analyze(IEntity entity)
{
if (entity == null)
{
throw new ArgumentNullException(nameof(entity));
}
if (entity.MetadataToken.IsNil)
{
MessageBox.Show(Properties.Resources.CannotAnalyzeMissingRef, "ILSpy");
return;
}
switch (entity)
{
case ITypeDefinition td:
ShowOrFocus(new AnalyzedTypeTreeNode(td));
break;
case IField fd:
if (!fd.IsConst)
ShowOrFocus(new AnalyzedFieldTreeNode(fd));
break;
case IMethod md:
ShowOrFocus(new AnalyzedMethodTreeNode(md));
break;
case IProperty pd:
ShowOrFocus(new AnalyzedPropertyTreeNode(pd));
break;
case IEvent ed:
ShowOrFocus(new AnalyzedEventTreeNode(ed));
break;
default:
throw new ArgumentOutOfRangeException(nameof(entity), $"Entity {entity.GetType().FullName} is not supported.");
}
}
sealed class AnalyzerRootNode : AnalyzerTreeNode
{
public override bool HandleAssemblyListChanged(ICollection<LoadedAssembly> removedAssemblies, ICollection<LoadedAssembly> addedAssemblies)
{
this.Children.RemoveAll(
delegate (SharpTreeNode n) {
AnalyzerTreeNode an = n as AnalyzerTreeNode;
return an == null || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies);
});
return true;
}
}
}
}

23
ILSpy/Analyzers/AnalyzerTreeView.xaml

@ -0,0 +1,23 @@
<treeView:SharpTreeView
x:Class="ICSharpCode.ILSpy.Analyzers.AnalyzerTreeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:toms="urn:TomsToolbox"
xmlns:treeView="clr-namespace:ICSharpCode.ILSpy.Controls.TreeView"
xmlns:analyzers="clr-namespace:ICSharpCode.ILSpy.Analyzers"
mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance analyzers:AnalyzerTreeViewModel}"
ShowRoot="False"
BorderThickness="0"
Root="{Binding Root}"
toms:MultiSelectorExtensions.SelectionBinding="{Binding SelectedItems}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
SelectionChanged="AnalyzerTreeView_OnSelectionChanged">
<UIElement.InputBindings>
<KeyBinding Key="R" Modifiers="Control" Command="{Binding AnalyzeCommand}" />
</UIElement.InputBindings>
</treeView:SharpTreeView>

36
ILSpy/ViewModels/AnalyzerPaneModel.cs → ILSpy/Analyzers/AnalyzerTreeView.xaml.cs

@ -1,4 +1,4 @@
// Copyright (c) 2019 AlphaSierraPapa for the SharpDevelop Team // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this // Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software // software and associated documentation files (the "Software"), to deal in the Software
@ -17,22 +17,34 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System.ComponentModel.Composition; using System.ComponentModel.Composition;
using System.Windows.Input; using System.Windows.Controls;
namespace ICSharpCode.ILSpy.ViewModels using ICSharpCode.ILSpyX.TreeView;
using TomsToolbox.Wpf.Composition.Mef;
namespace ICSharpCode.ILSpy.Analyzers
{ {
[ExportToolPane] /// <summary>
[PartCreationPolicy(CreationPolicy.Shared)] /// Interaction logic for AnalyzerTreeView.xaml
public class AnalyzerPaneModel : ToolPaneModel /// </summary>
[DataTemplate(typeof(AnalyzerTreeViewModel))]
[PartCreationPolicy(CreationPolicy.NonShared)]
[Export]
public partial class AnalyzerTreeView
{ {
public const string PaneContentId = "analyzerPane"; public AnalyzerTreeView()
{
InitializeComponent();
ContextMenuProvider.Add(this);
}
public AnalyzerPaneModel() private void AnalyzerTreeView_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{ {
ContentId = PaneContentId; if (SelectedItem is SharpTreeNode sharpTreeNode)
Title = Properties.Resources.Analyze; {
ShortcutKey = new KeyGesture(Key.R, ModifierKeys.Control); FocusNode(sharpTreeNode);
AssociatedCommand = ILSpyCommands.Analyze; }
} }
} }
} }

130
ILSpy/Analyzers/AnalyzerTreeViewModel.cs

@ -0,0 +1,130 @@
// Copyright (c) 2019 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.Analyzers.TreeNodes;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.ViewModels;
using TomsToolbox.Wpf;
namespace ICSharpCode.ILSpy.Analyzers
{
[ExportToolPane]
[PartCreationPolicy(CreationPolicy.Shared)]
[Export]
public class AnalyzerTreeViewModel : ToolPaneModel
{
private AnalyzerTreeNode selectedItem;
public const string PaneContentId = "analyzerPane";
public AnalyzerTreeViewModel()
{
ContentId = PaneContentId;
Title = Properties.Resources.Analyze;
ShortcutKey = new(Key.R, ModifierKeys.Control);
AssociatedCommand = ILSpyCommands.Analyze;
}
public AnalyzerRootNode Root { get; } = new();
public AnalyzerTreeNode SelectedItem {
get => selectedItem;
set => SetProperty(ref selectedItem, value);
}
public ICommand AnalyzeCommand => new DelegateCommand(AnalyzeSelected);
public ObservableCollection<AnalyzerTreeNode> SelectedItems { get; } = [];
private void AnalyzeSelected()
{
foreach (var node in SelectedItems.OfType<IMemberTreeNode>())
{
Analyze(node.Member);
}
}
void AddOrSelect(AnalyzerTreeNode node)
{
Show();
AnalyzerTreeNode target = default;
if (node is AnalyzerEntityTreeNode { Member: { } member })
{
target = this.Root.Children.OfType<AnalyzerEntityTreeNode>().FirstOrDefault(item => item.Member == member);
}
if (target == null)
{
this.Root.Children.Add(node);
target = node;
}
target.IsExpanded = true;
this.SelectedItem = target;
}
public void Analyze(IEntity entity)
{
if (entity == null)
{
throw new ArgumentNullException(nameof(entity));
}
if (entity.MetadataToken.IsNil)
{
MessageBox.Show(Properties.Resources.CannotAnalyzeMissingRef, "ILSpy");
return;
}
switch (entity)
{
case ITypeDefinition td:
AddOrSelect(new AnalyzedTypeTreeNode(td));
break;
case IField fd:
if (!fd.IsConst)
AddOrSelect(new AnalyzedFieldTreeNode(fd));
break;
case IMethod md:
AddOrSelect(new AnalyzedMethodTreeNode(md));
break;
case IProperty pd:
AddOrSelect(new AnalyzedPropertyTreeNode(pd));
break;
case IEvent ed:
AddOrSelect(new AnalyzedEventTreeNode(ed));
break;
default:
throw new ArgumentOutOfRangeException(nameof(entity), $@"Entity {entity.GetType().FullName} is not supported.");
}
}
}
}

18
ILSpy/Docking/DockWorkspace.cs

@ -35,6 +35,7 @@ using AvalonDock.Layout;
using AvalonDock.Layout.Serialization; using AvalonDock.Layout.Serialization;
using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.ILSpy.Analyzers;
using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpy.Util;
using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpy.ViewModels;
@ -45,17 +46,17 @@ namespace ICSharpCode.ILSpy.Docking
{ {
public class DockWorkspace : ObservableObject, ILayoutUpdateStrategy public class DockWorkspace : ObservableObject, ILayoutUpdateStrategy
{ {
private SessionSettings sessionSettings; private static SessionSettings SessionSettings => SettingsService.Instance.SessionSettings;
public static readonly DockWorkspace Instance = new(); public static readonly DockWorkspace Instance = new();
private DockWorkspace() private DockWorkspace()
{ {
this.TabPages.CollectionChanged += Documents_CollectionChanged; this.TabPages.CollectionChanged += Documents_CollectionChanged;
MessageBus<CurrentAssemblyListChangedEventArgs>.Subscribers += (sender, e) => MainWindow_Instance_CurrentAssemblyListChanged(sender, e); MessageBus<CurrentAssemblyListChangedEventArgs>.Subscribers += (sender, e) => CurrentAssemblyList_Changed(sender, e);
} }
private void MainWindow_Instance_CurrentAssemblyListChanged(object sender, NotifyCollectionChangedEventArgs e) private void CurrentAssemblyList_Changed(object sender, NotifyCollectionChangedEventArgs e)
{ {
if (e.OldItems == null) if (e.OldItems == null)
{ {
@ -158,7 +159,7 @@ namespace ICSharpCode.ILSpy.Docking
serializer.LayoutSerializationCallback += LayoutSerializationCallback; serializer.LayoutSerializationCallback += LayoutSerializationCallback;
try try
{ {
sessionSettings.DockLayout.Deserialize(serializer); SessionSettings.DockLayout.Deserialize(serializer);
} }
finally finally
{ {
@ -201,11 +202,6 @@ namespace ICSharpCode.ILSpy.Docking
ActiveTabPage.ShowTextView(textView => textView.ShowNodes(output, nodes, highlighting)); ActiveTabPage.ShowTextView(textView => textView.ShowNodes(output, nodes, highlighting));
} }
internal void LoadSettings(SessionSettings sessionSettings)
{
this.sessionSettings = sessionSettings;
}
internal void CloseAllTabs() internal void CloseAllTabs()
{ {
foreach (var doc in TabPages.ToArray()) foreach (var doc in TabPages.ToArray())
@ -222,7 +218,7 @@ namespace ICSharpCode.ILSpy.Docking
pane.IsVisible = false; pane.IsVisible = false;
} }
CloseAllTabs(); CloseAllTabs();
sessionSettings.DockLayout.Reset(); SessionSettings.DockLayout.Reset();
InitializeLayout(MainWindow.Instance.DockManager); InitializeLayout(MainWindow.Instance.DockManager);
MainWindow.Instance.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)MainWindow.Instance.RefreshDecompiledView); MainWindow.Instance.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)MainWindow.Instance.RefreshDecompiledView);
} }
@ -243,7 +239,7 @@ namespace ICSharpCode.ILSpy.Docking
previousContainer.Children.Add(anchorableToShow); previousContainer.Children.Add(anchorableToShow);
return true; return true;
case LegacyToolPaneLocation.Bottom: case LegacyToolPaneLocation.Bottom:
previousContainer = GetContainer<AnalyzerPaneModel>(); previousContainer = GetContainer<AnalyzerTreeViewModel>();
previousContainer.Children.Add(anchorableToShow); previousContainer.Children.Add(anchorableToShow);
return true; return true;
default: default:

2
ILSpy/MainWindow.xaml

@ -35,7 +35,6 @@
BorderThickness="0" Visibility="Visible"> BorderThickness="0" Visibility="Visible">
<tv:SharpTreeView.ItemContainerStyle> <tv:SharpTreeView.ItemContainerStyle>
<Style TargetType="tv:SharpTreeViewItem"> <Style TargetType="tv:SharpTreeViewItem">
<Setter Property="Template"> <Setter Property="Template">
<Setter.Value> <Setter.Value>
<ControlTemplate TargetType="{x:Type tv:SharpTreeViewItem}"> <ControlTemplate TargetType="{x:Type tv:SharpTreeViewItem}">
@ -65,6 +64,7 @@
</Style> </Style>
</tv:SharpTreeView.ItemContainerStyle> </tv:SharpTreeView.ItemContainerStyle>
</tv:SharpTreeView> </tv:SharpTreeView>
<DataTemplate DataType="{x:Type viewModels:AssemblyListPaneModel}"> <DataTemplate DataType="{x:Type viewModels:AssemblyListPaneModel}">
<ContentControl Content="{StaticResource AssemblyTreeView}" /> <ContentControl Content="{StaticResource AssemblyTreeView}" />
</DataTemplate> </DataTemplate>

3
ILSpy/MainWindow.xaml.cs

@ -135,7 +135,6 @@ namespace ICSharpCode.ILSpy
{ {
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(sessionSettings.CurrentCulture); System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(sessionSettings.CurrentCulture);
} }
DockWorkspace.Instance.LoadSettings(sessionSettings);
InitializeComponent(); InitializeComponent();
InitToolPanes(); InitToolPanes();
DockWorkspace.Instance.InitializeLayout(DockManager); DockWorkspace.Instance.InitializeLayout(DockManager);
@ -352,7 +351,7 @@ namespace ICSharpCode.ILSpy
private void InitToolPanes() private void InitToolPanes()
{ {
var toolPanes = App.ExportProvider.GetExportedValues<ToolPaneModel>("ToolPane"); var toolPanes = App.ExportProvider.GetExportedValues<ToolPaneModel>("ToolPane").OrderBy(item => item.Title);
DockWorkspace.Instance.ToolPanes.AddRange(toolPanes); DockWorkspace.Instance.ToolPanes.AddRange(toolPanes);
} }

25
ILSpy/ViewModels/PaneModel.cs

@ -20,9 +20,11 @@ using System;
using System.ComponentModel; using System.ComponentModel;
using System.Windows.Input; using System.Windows.Input;
using TomsToolbox.Wpf;
namespace ICSharpCode.ILSpy.ViewModels namespace ICSharpCode.ILSpy.ViewModels
{ {
public abstract class PaneModel : INotifyPropertyChanged public abstract class PaneModel : ObservableObject
{ {
class CloseCommandImpl : ICommand class CloseCommandImpl : ICommand
{ {
@ -60,13 +62,6 @@ namespace ICSharpCode.ILSpy.ViewModels
this.closeCommand = new CloseCommandImpl(this); this.closeCommand = new CloseCommandImpl(this);
} }
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private bool isSelected = false; private bool isSelected = false;
public bool IsSelected { public bool IsSelected {
get => isSelected; get => isSelected;
@ -74,7 +69,7 @@ namespace ICSharpCode.ILSpy.ViewModels
if (isSelected != value) if (isSelected != value)
{ {
isSelected = value; isSelected = value;
RaisePropertyChanged(nameof(IsSelected)); OnPropertyChanged(nameof(IsSelected));
} }
} }
} }
@ -86,7 +81,7 @@ namespace ICSharpCode.ILSpy.ViewModels
if (isActive != value) if (isActive != value)
{ {
isActive = value; isActive = value;
RaisePropertyChanged(nameof(IsActive)); OnPropertyChanged(nameof(IsActive));
} }
} }
} }
@ -98,7 +93,7 @@ namespace ICSharpCode.ILSpy.ViewModels
if (isVisible != value) if (isVisible != value)
{ {
isVisible = value; isVisible = value;
RaisePropertyChanged(nameof(IsVisible)); OnPropertyChanged(nameof(IsVisible));
} }
} }
} }
@ -110,7 +105,7 @@ namespace ICSharpCode.ILSpy.ViewModels
if (isCloseable != value) if (isCloseable != value)
{ {
isCloseable = value; isCloseable = value;
RaisePropertyChanged(nameof(IsCloseable)); OnPropertyChanged(nameof(IsCloseable));
} }
} }
} }
@ -122,7 +117,7 @@ namespace ICSharpCode.ILSpy.ViewModels
if (closeCommand != value) if (closeCommand != value)
{ {
closeCommand = value; closeCommand = value;
RaisePropertyChanged(nameof(CloseCommand)); OnPropertyChanged(nameof(CloseCommand));
} }
} }
} }
@ -134,7 +129,7 @@ namespace ICSharpCode.ILSpy.ViewModels
if (contentId != value) if (contentId != value)
{ {
contentId = value; contentId = value;
RaisePropertyChanged(nameof(ContentId)); OnPropertyChanged(nameof(ContentId));
} }
} }
} }
@ -146,7 +141,7 @@ namespace ICSharpCode.ILSpy.ViewModels
if (title != value) if (title != value)
{ {
title = value; title = value;
RaisePropertyChanged(nameof(Title)); OnPropertyChanged(nameof(Title));
} }
} }
} }

4
ILSpy/ViewModels/TabPageModel.cs

@ -40,7 +40,7 @@ namespace ICSharpCode.ILSpy.ViewModels
if (supportsLanguageSwitching != value) if (supportsLanguageSwitching != value)
{ {
supportsLanguageSwitching = value; supportsLanguageSwitching = value;
RaisePropertyChanged(nameof(SupportsLanguageSwitching)); OnPropertyChanged(nameof(SupportsLanguageSwitching));
} }
} }
} }
@ -53,7 +53,7 @@ namespace ICSharpCode.ILSpy.ViewModels
if (content != value) if (content != value)
{ {
content = value; content = value;
RaisePropertyChanged(nameof(Content)); OnPropertyChanged(nameof(Content));
} }
} }
} }

Loading…
Cancel
Save