mirror of https://github.com/icsharpcode/ILSpy.git
13 changed files with 936 additions and 25 deletions
@ -0,0 +1,65 @@
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Windows; |
||||
using System.Windows.Markup; |
||||
|
||||
namespace ICSharpCode.ILSpy |
||||
{ |
||||
/// <summary>
|
||||
/// ExtensionMethods that help with WPF.
|
||||
/// </summary>
|
||||
public static class ExtensionMethods |
||||
{ |
||||
/// <summary>
|
||||
/// Sets the value of a dependency property on <paramref name="targetObject"/> using a markup extension.
|
||||
/// </summary>
|
||||
/// <remarks>This method does not support markup extensions like x:Static that depend on
|
||||
/// having a XAML file as context.</remarks>
|
||||
public static void SetValueToExtension(this DependencyObject targetObject, DependencyProperty property, MarkupExtension markupExtension) |
||||
{ |
||||
// This method was copied from ICSharpCode.Core.Presentation
|
||||
|
||||
if (targetObject == null) |
||||
throw new ArgumentNullException("targetObject"); |
||||
if (property == null) |
||||
throw new ArgumentNullException("property"); |
||||
if (markupExtension == null) |
||||
throw new ArgumentNullException("markupExtension"); |
||||
|
||||
var serviceProvider = new SetValueToExtensionServiceProvider(targetObject, property); |
||||
targetObject.SetValue(property, markupExtension.ProvideValue(serviceProvider)); |
||||
} |
||||
|
||||
sealed class SetValueToExtensionServiceProvider : IServiceProvider, IProvideValueTarget |
||||
{ |
||||
// This class was copied from ICSharpCode.Core.Presentation
|
||||
|
||||
readonly DependencyObject targetObject; |
||||
readonly DependencyProperty targetProperty; |
||||
|
||||
public SetValueToExtensionServiceProvider(DependencyObject targetObject, DependencyProperty property) |
||||
{ |
||||
this.targetObject = targetObject; |
||||
this.targetProperty = property; |
||||
} |
||||
|
||||
public object GetService(Type serviceType) |
||||
{ |
||||
if (serviceType == typeof(IProvideValueTarget)) |
||||
return this; |
||||
else |
||||
return null; |
||||
} |
||||
|
||||
public object TargetObject { |
||||
get { return targetObject; } |
||||
} |
||||
|
||||
public object TargetProperty { |
||||
get { return targetProperty; } |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,216 @@
@@ -0,0 +1,216 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Runtime.InteropServices; |
||||
using System.Runtime.InteropServices.ComTypes; |
||||
using System.Text; |
||||
|
||||
namespace ICSharpCode.ILSpy |
||||
{ |
||||
// .NET Fusion COM interfaces
|
||||
[ComImport(), Guid("E707DCDE-D1CD-11D2-BAB9-00C04F8ECEAE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] |
||||
internal interface IAssemblyCache |
||||
{ |
||||
[PreserveSig()] |
||||
int UninstallAssembly(uint dwFlags, |
||||
[MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName, |
||||
IntPtr pvReserved, |
||||
out uint pulDisposition); |
||||
|
||||
[PreserveSig()] |
||||
int QueryAssemblyInfo(uint dwFlags, |
||||
[MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName, |
||||
IntPtr pAsmInfo); |
||||
|
||||
[PreserveSig()] |
||||
int CreateAssemblyCacheItem(uint dwFlags, |
||||
IntPtr pvReserved, |
||||
out IAssemblyCacheItem ppAsmItem, |
||||
[MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName); |
||||
|
||||
[PreserveSig()] |
||||
int CreateAssemblyScavenger(out object ppAsmScavenger); |
||||
|
||||
[PreserveSig()] |
||||
int InstallAssembly(uint dwFlags, |
||||
[MarshalAs(UnmanagedType.LPWStr)] string pszManifestFilePath, |
||||
IntPtr pvReserved); |
||||
} |
||||
|
||||
[ComImport(), Guid("9E3AAEB4-D1CD-11D2-BAB9-00C04F8ECEAE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] |
||||
internal interface IAssemblyCacheItem |
||||
{ |
||||
void CreateStream([MarshalAs(UnmanagedType.LPWStr)] string pszName, |
||||
uint dwFormat, |
||||
uint dwFlags, |
||||
uint dwMaxSize, |
||||
out IStream ppStream); |
||||
|
||||
void IsNameEqual(IAssemblyName pName); |
||||
|
||||
void Commit(uint dwFlags); |
||||
|
||||
void MarkAssemblyVisible(uint dwFlags); |
||||
} |
||||
|
||||
[ComImport(), Guid("CD193BC0-B4BC-11D2-9833-00C04FC31D2E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] |
||||
internal interface IAssemblyName |
||||
{ |
||||
[PreserveSig()] |
||||
int SetProperty(uint PropertyId, IntPtr pvProperty, uint cbProperty); |
||||
|
||||
[PreserveSig()] |
||||
int GetProperty(uint PropertyId, IntPtr pvProperty, ref uint pcbProperty); |
||||
|
||||
[PreserveSig()] |
||||
int Finalize(); |
||||
|
||||
[PreserveSig()] |
||||
int GetDisplayName([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder szDisplayName, |
||||
ref uint pccDisplayName, |
||||
uint dwDisplayFlags); |
||||
|
||||
[PreserveSig()] |
||||
int BindToObject(object refIID, |
||||
object pAsmBindSink, |
||||
IApplicationContext pApplicationContext, |
||||
[MarshalAs(UnmanagedType.LPWStr)] string szCodeBase, |
||||
long llFlags, |
||||
int pvReserved, |
||||
uint cbReserved, |
||||
out int ppv); |
||||
|
||||
[PreserveSig()] |
||||
int GetName(ref uint lpcwBuffer, |
||||
[Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwzName); |
||||
|
||||
[PreserveSig()] |
||||
int GetVersion(out uint pdwVersionHi, out uint pdwVersionLow); |
||||
|
||||
[PreserveSig()] |
||||
int IsEqual(IAssemblyName pName, |
||||
uint dwCmpFlags); |
||||
|
||||
[PreserveSig()] |
||||
int Clone(out IAssemblyName pName); |
||||
} |
||||
|
||||
[ComImport(), Guid("7C23FF90-33AF-11D3-95DA-00A024A85B51"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] |
||||
internal interface IApplicationContext |
||||
{ |
||||
void SetContextNameObject(IAssemblyName pName); |
||||
|
||||
void GetContextNameObject(out IAssemblyName ppName); |
||||
|
||||
void Set([MarshalAs(UnmanagedType.LPWStr)] string szName, |
||||
int pvValue, |
||||
uint cbValue, |
||||
uint dwFlags); |
||||
|
||||
void Get([MarshalAs(UnmanagedType.LPWStr)] string szName, |
||||
out int pvValue, |
||||
ref uint pcbValue, |
||||
uint dwFlags); |
||||
|
||||
void GetDynamicDirectory(out int wzDynamicDir, |
||||
ref uint pdwSize); |
||||
} |
||||
|
||||
[ComImport(), Guid("21B8916C-F28E-11D2-A473-00C04F8EF448"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] |
||||
internal interface IAssemblyEnum |
||||
{ |
||||
[PreserveSig()] |
||||
int GetNextAssembly(out IApplicationContext ppAppCtx, |
||||
out IAssemblyName ppName, |
||||
uint dwFlags); |
||||
|
||||
[PreserveSig()] |
||||
int Reset(); |
||||
|
||||
[PreserveSig()] |
||||
int Clone(out IAssemblyEnum ppEnum); |
||||
} |
||||
|
||||
|
||||
[ComImport(), Guid("1D23DF4D-A1E2-4B8B-93D6-6EA3DC285A54"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] |
||||
internal interface IHistoryReader |
||||
{ |
||||
[PreserveSig()] |
||||
int GetFilePath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzFilePath, |
||||
ref uint pdwSize); |
||||
|
||||
[PreserveSig()] |
||||
int GetApplicationName([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzAppName, |
||||
ref uint pdwSize); |
||||
|
||||
[PreserveSig()] |
||||
int GetEXEModulePath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzExePath, |
||||
ref uint pdwSize); |
||||
|
||||
void GetNumActivations(out uint pdwNumActivations); |
||||
|
||||
void GetActivationDate(uint dwIdx, // One-based!
|
||||
out long /* FILETIME */ pftDate); |
||||
|
||||
[PreserveSig()] |
||||
int GetRunTimeVersion(ref long /* FILETIME */ pftActivationDate, |
||||
[Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzRunTimeVersion, |
||||
ref uint pdwSize); |
||||
|
||||
void GetNumAssemblies(ref long /* FILETIME */ pftActivationDate, |
||||
out uint pdwNumAsms); |
||||
|
||||
void GetHistoryAssembly(ref long /* FILETIME */ pftActivationDate, |
||||
uint dwIdx, // One-based!
|
||||
[MarshalAs(UnmanagedType.IUnknown)] out object ppHistAsm); |
||||
|
||||
} |
||||
|
||||
internal static class Fusion |
||||
{ |
||||
[DllImport("fusion.dll", CharSet=CharSet.Auto)] |
||||
internal static extern int CreateAssemblyCache(out IAssemblyCache ppAsmCache, |
||||
uint dwReserved); |
||||
|
||||
// dwFlags: 1 = Enumerate native image (NGEN) assemblies
|
||||
// 2 = Enumerate GAC assemblies
|
||||
// 4 = Enumerate Downloaded assemblies
|
||||
//
|
||||
[DllImport("fusion.dll", CharSet=CharSet.Auto)] |
||||
internal static extern int CreateAssemblyEnum(out IAssemblyEnum ppEnum, |
||||
IApplicationContext pAppCtx, |
||||
IAssemblyName pName, |
||||
uint dwFlags, |
||||
int pvReserved); |
||||
|
||||
[DllImport("fusion.dll", CharSet=CharSet.Auto)] |
||||
internal static extern int CreateAssemblyNameObject(out IAssemblyName ppName, |
||||
string szAssemblyName, |
||||
uint dwFlags, |
||||
int pvReserved); |
||||
|
||||
// ?????
|
||||
[DllImport("fusion.dll")] |
||||
internal static extern int CreateApplicationContext(out IApplicationContext ppAppContext, |
||||
uint dw); |
||||
|
||||
[DllImport("fusion.dll")] |
||||
internal static extern int GetCachePath(uint flags, |
||||
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzDir, |
||||
ref uint pdwSize); |
||||
|
||||
public static string GetGacPath(bool isCLRv4 = false) |
||||
{ |
||||
const uint ASM_CACHE_ROOT = 0x08; // CLR V2.0
|
||||
const uint ASM_CACHE_ROOT_EX = 0x80; // CLR V4.0
|
||||
uint flags = isCLRv4 ? ASM_CACHE_ROOT_EX : ASM_CACHE_ROOT; |
||||
|
||||
const int size = 260; // MAX_PATH
|
||||
StringBuilder b = new StringBuilder(size); |
||||
uint tmp = size; |
||||
GetCachePath(flags, b, ref tmp); |
||||
return b.ToString(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<Window |
||||
x:Class="ICSharpCode.ILSpy.OpenFromGacDialog" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:ICSharpCode.ILSpy" |
||||
Title="Open From GAC" |
||||
Style="{DynamicResource DialogWindow}" |
||||
WindowStartupLocation="CenterOwner" |
||||
ResizeMode="CanResizeWithGrip" |
||||
MinWidth="200" |
||||
MinHeight="150" |
||||
Height="350" |
||||
Width="650" |
||||
FocusManager.FocusedElement="{Binding ElementName=filterTextBox}"> |
||||
<Grid |
||||
Margin="12,8"> |
||||
<Grid.RowDefinitions> |
||||
<RowDefinition |
||||
Height="Auto" /> |
||||
<RowDefinition |
||||
Height="1*" /> |
||||
<RowDefinition |
||||
Height="Auto" /> |
||||
</Grid.RowDefinitions> |
||||
<TextBox Name="filterTextBox" TextChanged="FilterTextBox_TextChanged" /> |
||||
<ListView Name="listView" Grid.Row="1" Margin="0, 8" local:SortableGridViewColumn.SortMode="Automatic" SelectionChanged="ListView_SelectionChanged"> |
||||
<ListView.View> |
||||
<GridView> |
||||
<local:SortableGridViewColumn x:Name="nameColumn" Width="300" Header="Reference Name" DisplayMemberBinding="{Binding ShortName}" /> |
||||
<local:SortableGridViewColumn Width="75" Header="Version" DisplayMemberBinding="{Binding Version}" /> |
||||
<local:SortableGridViewColumn Width="80" Header="Culture" DisplayMemberBinding="{Binding Culture}" /> |
||||
<local:SortableGridViewColumn Width="120" Header="Public Key Token" DisplayMemberBinding="{Binding PublicKeyToken}" /> |
||||
</GridView> |
||||
</ListView.View> |
||||
</ListView> |
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right"> |
||||
<Button IsDefault="True" Margin="2,0" IsEnabled="False" Name="okButton" Click="OKButton_Click">Open</Button> |
||||
<Button IsCancel="True" Margin="2,0">Cancel</Button> |
||||
</StackPanel> |
||||
</Grid> |
||||
</Window> |
@ -0,0 +1,180 @@
@@ -0,0 +1,180 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Collections.ObjectModel; |
||||
using System.ComponentModel; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using System.Threading; |
||||
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.Threading; |
||||
|
||||
namespace ICSharpCode.ILSpy |
||||
{ |
||||
/// <summary>
|
||||
/// Interaction logic for OpenFromGacDialog.xaml
|
||||
/// </summary>
|
||||
public partial class OpenFromGacDialog : Window |
||||
{ |
||||
ObservableCollection<GacEntry> gacEntries = new ObservableCollection<GacEntry>(); |
||||
ObservableCollection<GacEntry> filteredEntries = new ObservableCollection<GacEntry>(); |
||||
volatile bool cancelFetchThread; |
||||
|
||||
public OpenFromGacDialog() |
||||
{ |
||||
InitializeComponent(); |
||||
listView.ItemsSource = filteredEntries; |
||||
SortableGridViewColumn.SetCurrentSortColumn(listView, nameColumn); |
||||
SortableGridViewColumn.SetSortDirection(listView, ColumnSortDirection.Ascending); |
||||
|
||||
new Thread(new ThreadStart(FetchGacContents)).Start(); |
||||
} |
||||
|
||||
protected override void OnClosing(CancelEventArgs e) |
||||
{ |
||||
base.OnClosing(e); |
||||
cancelFetchThread = true; |
||||
} |
||||
|
||||
#region Fetch Gac Contents
|
||||
sealed class GacEntry : IEquatable<GacEntry> |
||||
{ |
||||
readonly string fullAssemblyName; |
||||
readonly string shortName, culture, publicKeyToken; |
||||
readonly Version version; |
||||
|
||||
public GacEntry(string fullAssemblyName) |
||||
{ |
||||
this.fullAssemblyName = fullAssemblyName; |
||||
string[] components = fullAssemblyName.Split(','); |
||||
shortName = components[0]; |
||||
for (int i = 1; i < components.Length; i++) { |
||||
string val = components[i].Trim(); |
||||
int pos = val.IndexOf('='); |
||||
if (pos > 0) { |
||||
switch (val.Substring(0, pos)) { |
||||
case "Version": |
||||
string versionText = val.Substring(pos + 1); |
||||
Version.TryParse(versionText, out version); |
||||
break; |
||||
case "Culture": |
||||
culture = val.Substring(pos + 1); |
||||
break; |
||||
case "PublicKeyToken": |
||||
publicKeyToken = val.Substring(pos + 1); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
public string FullName { |
||||
get { return fullAssemblyName; } |
||||
} |
||||
|
||||
public string ShortName { |
||||
get { return shortName; } |
||||
} |
||||
|
||||
public Version Version { |
||||
get { return version; } |
||||
} |
||||
|
||||
public string Culture { |
||||
get { return culture; } |
||||
} |
||||
|
||||
public string PublicKeyToken { |
||||
get { return publicKeyToken; } |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
return fullAssemblyName; |
||||
} |
||||
|
||||
public override int GetHashCode() |
||||
{ |
||||
return fullAssemblyName.GetHashCode(); |
||||
} |
||||
|
||||
public override bool Equals(object obj) |
||||
{ |
||||
return Equals(obj as GacEntry); |
||||
} |
||||
|
||||
public bool Equals(GacEntry o) |
||||
{ |
||||
return o != null && fullAssemblyName == o.fullAssemblyName; |
||||
} |
||||
} |
||||
|
||||
IEnumerable<GacEntry> GetGacAssemblyFullNames() |
||||
{ |
||||
IApplicationContext applicationContext = null; |
||||
IAssemblyEnum assemblyEnum = null; |
||||
IAssemblyName assemblyName = null; |
||||
|
||||
Fusion.CreateAssemblyEnum(out assemblyEnum, null, null, 2, 0); |
||||
while (!cancelFetchThread && assemblyEnum.GetNextAssembly(out applicationContext, out assemblyName, 0) == 0) { |
||||
uint nChars = 0; |
||||
assemblyName.GetDisplayName(null, ref nChars, 0); |
||||
|
||||
StringBuilder name = new StringBuilder((int)nChars); |
||||
assemblyName.GetDisplayName(name, ref nChars, 0); |
||||
|
||||
yield return new GacEntry(name.ToString()); |
||||
} |
||||
} |
||||
|
||||
void FetchGacContents() |
||||
{ |
||||
foreach (var entry in GetGacAssemblyFullNames().Distinct()) { |
||||
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action<GacEntry>(AddNewEntry), entry); |
||||
} |
||||
} |
||||
|
||||
void AddNewEntry(GacEntry entry) |
||||
{ |
||||
gacEntries.Add(entry); |
||||
string filter = filterTextBox.Text; |
||||
if (string.IsNullOrEmpty(filter) || entry.ShortName.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0) |
||||
filteredEntries.Add(entry); |
||||
} |
||||
#endregion
|
||||
|
||||
void FilterTextBox_TextChanged(object sender, TextChangedEventArgs e) |
||||
{ |
||||
string filter = filterTextBox.Text; |
||||
filteredEntries.Clear(); |
||||
foreach (GacEntry entry in gacEntries) { |
||||
if (string.IsNullOrEmpty(filter) || entry.ShortName.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0) |
||||
filteredEntries.Add(entry); |
||||
} |
||||
} |
||||
|
||||
void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e) |
||||
{ |
||||
okButton.IsEnabled = listView.SelectedItems.Count > 0; |
||||
} |
||||
|
||||
void OKButton_Click(object sender, RoutedEventArgs e) |
||||
{ |
||||
this.DialogResult = true; |
||||
Close(); |
||||
} |
||||
|
||||
public string[] SelectedFullNames { |
||||
get { |
||||
return listView.SelectedItems.OfType<GacEntry>().Select(e => e.FullName).ToArray(); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,210 @@
@@ -0,0 +1,210 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.ComponentModel; |
||||
using System.Windows; |
||||
using System.Windows.Controls; |
||||
using System.Windows.Data; |
||||
|
||||
namespace ICSharpCode.ILSpy |
||||
{ |
||||
/// <summary>
|
||||
/// Allows to automatically sort a grid view.
|
||||
/// </summary>
|
||||
public class SortableGridViewColumn : GridViewColumn |
||||
{ |
||||
// This class was copied from ICSharpCode.Core.Presentation.
|
||||
|
||||
static readonly ComponentResourceKey headerTemplateKey = new ComponentResourceKey(typeof(SortableGridViewColumn), "ColumnHeaderTemplate"); |
||||
|
||||
public SortableGridViewColumn() |
||||
{ |
||||
this.SetValueToExtension(HeaderTemplateProperty, new DynamicResourceExtension(headerTemplateKey)); |
||||
} |
||||
|
||||
string sortBy; |
||||
|
||||
public string SortBy { |
||||
get { return sortBy; } |
||||
set { |
||||
if (sortBy != value) { |
||||
sortBy = value; |
||||
OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("SortBy")); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#region SortDirection property
|
||||
public static readonly DependencyProperty SortDirectionProperty = |
||||
DependencyProperty.RegisterAttached("SortDirection", typeof(ColumnSortDirection), typeof(SortableGridViewColumn), |
||||
new FrameworkPropertyMetadata(ColumnSortDirection.None, OnSortDirectionChanged)); |
||||
|
||||
public ColumnSortDirection SortDirection { |
||||
get { return (ColumnSortDirection)GetValue(SortDirectionProperty); } |
||||
set { SetValue(SortDirectionProperty, value); } |
||||
} |
||||
|
||||
public static ColumnSortDirection GetSortDirection(ListView listView) |
||||
{ |
||||
return (ColumnSortDirection)listView.GetValue(SortDirectionProperty); |
||||
} |
||||
|
||||
public static void SetSortDirection(ListView listView, ColumnSortDirection value) |
||||
{ |
||||
listView.SetValue(SortDirectionProperty, value); |
||||
} |
||||
|
||||
static void OnSortDirectionChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) |
||||
{ |
||||
ListView grid = sender as ListView; |
||||
if (grid != null) { |
||||
SortableGridViewColumn col = GetCurrentSortColumn(grid); |
||||
if (col != null) |
||||
col.SortDirection = (ColumnSortDirection)args.NewValue; |
||||
Sort(grid); |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
#region CurrentSortColumn property
|
||||
public static readonly DependencyProperty CurrentSortColumnProperty = |
||||
DependencyProperty.RegisterAttached("CurrentSortColumn", typeof(SortableGridViewColumn), typeof(SortableGridViewColumn), |
||||
new FrameworkPropertyMetadata(OnCurrentSortColumnChanged)); |
||||
|
||||
public static SortableGridViewColumn GetCurrentSortColumn(ListView listView) |
||||
{ |
||||
return (SortableGridViewColumn)listView.GetValue(CurrentSortColumnProperty); |
||||
} |
||||
|
||||
public static void SetCurrentSortColumn(ListView listView, SortableGridViewColumn value) |
||||
{ |
||||
listView.SetValue(CurrentSortColumnProperty, value); |
||||
} |
||||
|
||||
static void OnCurrentSortColumnChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) |
||||
{ |
||||
ListView grid = sender as ListView; |
||||
if (grid != null) { |
||||
SortableGridViewColumn oldColumn = (SortableGridViewColumn)args.OldValue; |
||||
if (oldColumn != null) |
||||
oldColumn.SortDirection = ColumnSortDirection.None; |
||||
SortableGridViewColumn newColumn = (SortableGridViewColumn)args.NewValue; |
||||
if (newColumn != null) { |
||||
newColumn.SortDirection = GetSortDirection(grid); |
||||
} |
||||
Sort(grid); |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
#region SortMode property
|
||||
public static readonly DependencyProperty SortModeProperty = |
||||
DependencyProperty.RegisterAttached("SortMode", typeof(ListViewSortMode), typeof(SortableGridViewColumn), |
||||
new FrameworkPropertyMetadata(ListViewSortMode.None, OnSortModeChanged)); |
||||
|
||||
public static ListViewSortMode GetSortMode(ListView listView) |
||||
{ |
||||
return (ListViewSortMode)listView.GetValue(SortModeProperty); |
||||
} |
||||
|
||||
public static void SetSortMode(ListView listView, ListViewSortMode value) |
||||
{ |
||||
listView.SetValue(SortModeProperty, value); |
||||
} |
||||
|
||||
static void OnSortModeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) |
||||
{ |
||||
ListView grid = sender as ListView; |
||||
if (grid != null) { |
||||
if ((ListViewSortMode)args.NewValue != ListViewSortMode.None) |
||||
grid.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(GridViewColumnHeaderClickHandler)); |
||||
else |
||||
grid.RemoveHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(GridViewColumnHeaderClickHandler)); |
||||
} |
||||
} |
||||
|
||||
static void GridViewColumnHeaderClickHandler(object sender, RoutedEventArgs e) |
||||
{ |
||||
ListView grid = sender as ListView; |
||||
GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader; |
||||
if (grid != null && headerClicked != null && headerClicked.Role != GridViewColumnHeaderRole.Padding) { |
||||
if (headerClicked.Column == GetCurrentSortColumn(grid)) { |
||||
if (GetSortDirection(grid) == ColumnSortDirection.Ascending) |
||||
SetSortDirection(grid, ColumnSortDirection.Descending); |
||||
else |
||||
SetSortDirection(grid, ColumnSortDirection.Ascending); |
||||
} else { |
||||
SetSortDirection(grid, ColumnSortDirection.Ascending); |
||||
SetCurrentSortColumn(grid, headerClicked.Column as SortableGridViewColumn); |
||||
} |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
static void Sort(ListView grid) |
||||
{ |
||||
ColumnSortDirection currentDirection = GetSortDirection(grid); |
||||
SortableGridViewColumn column = GetCurrentSortColumn(grid); |
||||
if (column != null && GetSortMode(grid) == ListViewSortMode.Automatic && currentDirection != ColumnSortDirection.None) { |
||||
ICollectionView dataView = CollectionViewSource.GetDefaultView(grid.ItemsSource); |
||||
|
||||
string sortBy = column.SortBy; |
||||
if (sortBy == null) { |
||||
Binding binding = column.DisplayMemberBinding as Binding; |
||||
if (binding != null && binding.Path != null) { |
||||
sortBy = binding.Path.Path; |
||||
} |
||||
} |
||||
|
||||
dataView.SortDescriptions.Clear(); |
||||
if (sortBy != null) { |
||||
ListSortDirection direction; |
||||
if (currentDirection == ColumnSortDirection.Descending) |
||||
direction = ListSortDirection.Descending; |
||||
else |
||||
direction = ListSortDirection.Ascending; |
||||
dataView.SortDescriptions.Add(new SortDescription(sortBy, direction)); |
||||
} |
||||
dataView.Refresh(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public enum ColumnSortDirection |
||||
{ |
||||
None, |
||||
Ascending, |
||||
Descending |
||||
} |
||||
|
||||
public enum ListViewSortMode |
||||
{ |
||||
/// <summary>
|
||||
/// Disable automatic sorting when sortable columns are clicked.
|
||||
/// </summary>
|
||||
None, |
||||
/// <summary>
|
||||
/// Fully automatic sorting.
|
||||
/// </summary>
|
||||
Automatic, |
||||
/// <summary>
|
||||
/// Automatically update SortDirection and CurrentSortColumn properties,
|
||||
/// but do not actually sort the data.
|
||||
/// </summary>
|
||||
HalfAutomatic |
||||
} |
||||
|
||||
sealed class ColumnSortDirectionToVisibilityConverter : IValueConverter |
||||
{ |
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) |
||||
{ |
||||
return Equals(value, parameter) ? Visibility.Visible : Visibility.Collapsed; |
||||
} |
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) |
||||
{ |
||||
throw new NotSupportedException(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
xmlns:local="clr-namespace:ICSharpCode.ILSpy" |
||||
> |
||||
<!-- SortableGridViewColumn. |
||||
Displays an up arrow or down arrow in the column header when the grid is sorted using that column. |
||||
--> |
||||
<local:ColumnSortDirectionToVisibilityConverter x:Key="ColumnSortDirectionToVisibilityConverter"/> |
||||
|
||||
<DataTemplate x:Key="{ComponentResourceKey {x:Type local:SortableGridViewColumn}, ColumnHeaderTemplate}"> |
||||
<StackPanel Orientation="Horizontal"> |
||||
<TextBlock HorizontalAlignment="Center" Text="{Binding}"/> |
||||
<Path x:Name="upArrow" |
||||
Visibility="{Binding Path=Column.SortDirection, ConverterParameter={x:Static local:ColumnSortDirection.Ascending}, RelativeSource={RelativeSource AncestorType={x:Type GridViewColumnHeader}}, Converter={StaticResource ColumnSortDirectionToVisibilityConverter}}" |
||||
StrokeThickness = "1" |
||||
Fill = "Gray" |
||||
Data = "M 5,10 L 15,10 L 10,5 L 5,10"/> |
||||
<Path x:Name="downArrow" |
||||
Visibility="{Binding Path=Column.SortDirection, ConverterParameter={x:Static local:ColumnSortDirection.Descending}, RelativeSource={RelativeSource AncestorType={x:Type GridViewColumnHeader}}, Converter={StaticResource ColumnSortDirectionToVisibilityConverter}}" |
||||
StrokeThickness = "1" |
||||
Fill = "Gray" |
||||
Data = "M 5,5 L 10,10 L 15,5 L 5,5"/> |
||||
</StackPanel> |
||||
</DataTemplate> |
||||
</ResourceDictionary> |
Loading…
Reference in new issue