diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj index db7ae36c49..491568b9b8 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj @@ -145,9 +145,12 @@ + + + diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/GridVisualizerWindow.xaml.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/GridVisualizerWindow.xaml.cs index 0f2737e967..62f8bc54f5 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/GridVisualizerWindow.xaml.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/GridVisualizerWindow.xaml.cs @@ -39,7 +39,11 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer private void btnInspect_Click(object sender, RoutedEventArgs e) { + // clear ListView listView.ItemsSource = null; + ScrollViewer listViewScroller = listView.GetScrollViewer(); + if (listViewScroller != null) + listViewScroller.ScrollToVerticalOffset(0); this.debuggerService = DebuggerService.CurrentDebugger as WindowsDebugger; if (debuggerService == null) @@ -56,35 +60,40 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer } if (val != null && !val.IsNull) { - DebugType iListType, listItemType; - IList itemTypeMembers = null; + GridValuesProvider gridValuesProvider; + // Value is IList? + DebugType iListType, listItemType; if (val.Type.ResolveIListImplementation(out iListType, out listItemType)) { - var valuesProvider = new ListValuesProvider(val.ExpressionTree, iListType, listItemType); - var virtCollection = new VirtualizingCollection(valuesProvider); - - itemTypeMembers = valuesProvider.GetItemTypeMembers(); - + var listValuesProvider = new ListValuesProvider(val.ExpressionTree, iListType, listItemType); + var virtCollection = new VirtualizingCollection(listValuesProvider); this.listView.ItemsSource = virtCollection; + gridValuesProvider = listValuesProvider; } else { + // Value is IEnumerable? DebugType iEnumerableType, itemType; if (val.Type.ResolveIEnumerableImplementation(out iEnumerableType, out itemType)) { var lazyListViewWrapper = new LazyListView(this.listView); - var iEnumerableValuesProvider = new EnumerableValuesProvider(val.ExpressionTree, iEnumerableType, itemType); - - itemTypeMembers = iEnumerableValuesProvider.GetItemTypeMembers(); - - lazyListViewWrapper.ItemsSource = new VirtualizingIEnumerable(iEnumerableValuesProvider.ItemsSource); + var enumerableValuesProvider = new EnumerableValuesProvider(val.ExpressionTree, iEnumerableType, itemType); + lazyListViewWrapper.ItemsSource = new VirtualizingIEnumerable(enumerableValuesProvider.ItemsSource); + gridValuesProvider = enumerableValuesProvider; + } + else + { + // Value cannot be displayed in GridVisualizer + return; } } + IList itemTypeMembers = gridValuesProvider.GetItemTypeMembers(); + // create ListView columns createGridViewColumns((GridView)this.listView.View, itemTypeMembers); this.columnHider = new GridViewColumnHider((GridView)this.listView.View); - + // fill column-choosing ComboBox this.selectedProperties = initializeSelectedPropertiesWithEvents(itemTypeMembers); cmbColumns.ItemsSource = this.selectedProperties; } @@ -119,11 +128,11 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer var columnName = propertySeleted.Name; if (propertySeleted.IsSelected) { - columnHider.ShowColumn(columnName); + this.columnHider.ShowColumn(columnName); } else { - columnHider.HideColumn(columnName); + this.columnHider.HideColumn(columnName); } } } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/LazyListView.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/LazyListView.cs index 0e0b3e5d6b..9dc51b7188 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/LazyListView.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/LazyListView.cs @@ -37,7 +37,7 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer set { this.itemsSource = value; - this.itemsSource.RequestNextItems(14); + this.itemsSource.RequestNextItems(25); this.listView.ItemsSource = value; } } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ObjectValue.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ObjectValue.cs index f601ba4fdb..679330fccf 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ObjectValue.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ObjectValue.cs @@ -18,40 +18,77 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer /// public class ObjectValue { - // couldn't be properties loaded lazily? - when listview databinding asks for key, we could ask fetch the value from debugger + // Used to be able to expand items of IEnumerable + // Now we rely on PermanentReference to be able to get member values on demand. With IList, PermanentReference can be replaced by Expression + public Value PermanentReference { get; private set; } + private Dictionary properties = new Dictionary(); + + /// Used to quickly find MemberInfo by member name - DebugType.GetMember(name) uses a loop to search members + private Dictionary memberFromNameMap; + + internal ObjectValue(Dictionary memberFromNameMap) + { + this.memberFromNameMap = memberFromNameMap; + } /// /// Returns property with given name. /// public ObjectProperty this[string key] { - get { return properties.GetValue(key); } - set { properties[key] = value; } + get + { + ObjectProperty property; + // has property with name 'propertyName' already been evaluated? + if(!this.properties.TryGetValue(key, out property)) + { + if (this.PermanentReference == null) { + throw new DebuggerVisualizerException("Cannot get member of this ObjectValue - ObjectValue.PermanentReference is null"); + } + MemberInfo memberInfo = this.memberFromNameMap.GetValue(key); + if (memberInfo == null) { + throw new DebuggerVisualizerException("Cannot get member value - no member found with name " + key); + } + property = createPropertyFromValue(key, this.PermanentReference.GetMemberValue(memberInfo)); + this.properties.Add(key, property); + } + return property; + } + //set { properties[key] = value; } } - public static ObjectValue Create(Debugger.Value value, DebugType type, BindingFlags bindingFlags) + public static ObjectValue Create(Debugger.Value value, Dictionary memberFromName) { - ObjectValue result = new ObjectValue(); - foreach(MemberInfo memberInfo in value.Type.GetMembers(bindingFlags)) + ObjectValue result = new ObjectValue(memberFromName); + + // remember PermanentReference for expanding IEnumerable + Value permanentReference = value.GetPermanentReference(); + result.PermanentReference = permanentReference; + + // cannot use GetMemberValues because memberValue does not have CodeTail anymore - we need the name of the member + /*foreach(MemberInfo memberInfo in permanentReference.Type.GetMembers(bindingFlags)) { - Value memberValue = value.GetMemberValue(memberInfo); - - ObjectProperty property = new ObjectProperty(); - property.Name = memberInfo.Name; - // property.Expression = ?.Age - // - cannot use expression, - // if the value is returned from an enumerator, it has no meaningful expression - property.IsAtomic = memberValue.Type.IsPrimitive; - property.IsNull = memberValue.IsNull; - //property.Value = memberValue.AsString; - property.Value = memberValue.IsNull ? "" : memberValue.InvokeToString(); - result.properties.Add(property.Name, property); - } + Value memberValue = permanentReference.GetMemberValue(memberInfo); + result.properties.Add(createPropertyFromValue(memberInfo.Name, memberValue)); + }*/ return result; } - public static ObjectValue Create(Expression expr, DebugType type, BindingFlags bindingFlags) + private static ObjectProperty createPropertyFromValue(string propertyName, Value value) + { + ObjectProperty property = new ObjectProperty(); + property.Name = propertyName; + // property.Expression = ?.Age + // - cannot use expression, + // if the value is returned from an enumerator, it has no meaningful expression + property.IsAtomic = value.Type.IsPrimitive; + property.IsNull = value.IsNull; + property.Value = value.IsNull ? "" : value.InvokeToString(); + return property; + } + + /*public static ObjectValue Create(Expression expr, DebugType type, BindingFlags bindingFlags) { ObjectValue result = new ObjectValue(); foreach(MemberInfo memberInfo in type.GetMembers(bindingFlags)) @@ -69,6 +106,6 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer result.properties.Add(property.Name, property); } return result; - } + }*/ } } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/EnumerableValuesProvider.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/EnumerableValuesProvider.cs index 41bc716c22..39c99157fd 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/EnumerableValuesProvider.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/EnumerableValuesProvider.cs @@ -17,23 +17,11 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer /// /// Provides s for debugee objects implementing IEnumerable. /// - public class EnumerableValuesProvider + public class EnumerableValuesProvider : GridValuesProvider { - private readonly Debugger.MetaData.BindingFlags bindingFlags = - BindingFlags.Public | BindingFlags.Instance | BindingFlags.Field | BindingFlags.GetProperty; - - //private WindowsDebugger debugger; - - private Expression targetObject; - private DebugType iEnumerableType; - private DebugType itemType; - public EnumerableValuesProvider(Expression targetObject, DebugType iEnumerableType, DebugType itemType) + :base(targetObject, iEnumerableType, itemType) { - this.targetObject = targetObject; - this.iEnumerableType = iEnumerableType; - this.itemType = itemType; - this.itemsSource = enumerateItems(); } @@ -43,14 +31,9 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer get { return this.itemsSource; } } - public IList GetItemTypeMembers() - { - return itemType.GetMembers(this.bindingFlags); - } - private IEnumerable enumerateItems() { - MethodInfo enumeratorMethod = iEnumerableType.GetMethod("GetEnumerator"); + MethodInfo enumeratorMethod = collectionType.GetMethod("GetEnumerator"); Value enumerator = targetObject.Evaluate(WindowsDebugger.CurrentProcess).InvokeMethod(enumeratorMethod, null).GetPermanentReference(); MethodInfo moveNextMethod = enumerator.Type.GetMethod("MoveNext"); @@ -58,8 +41,8 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer while ((bool)enumerator.InvokeMethod(moveNextMethod, null).PrimitiveValue) { - Value currentValue = enumerator.GetPropertyValue(currentproperty).GetPermanentReference(); - yield return ObjectValue.Create(currentValue, this.itemType, this.bindingFlags); + Value currentValue = enumerator.GetPropertyValue(currentproperty); + yield return ObjectValue.Create(currentValue, this.memberFromNameMap); } } } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/GridValuesProvider.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/GridValuesProvider.cs new file mode 100644 index 0000000000..f084359d5c --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/GridValuesProvider.cs @@ -0,0 +1,50 @@ +// +// +// +// +// $Revision$ +// +using Debugger.MetaData; +using Debugger.AddIn.Visualizers.Utils; +using System; +using System.Collections; +using System.Collections.Generic; +using ICSharpCode.SharpDevelop.Services; +using ICSharpCode.NRefactory.Ast; + +namespace Debugger.AddIn.Visualizers.GridVisualizer +{ + /// + /// Provides s to be displayed in Grid visualizer. + /// Descandants implement getting values for IList and IEnumerable. + /// + public class GridValuesProvider + { + protected readonly Debugger.MetaData.BindingFlags memberBindingFlags = + BindingFlags.Public | BindingFlags.Instance | BindingFlags.Field | BindingFlags.GetProperty; + + /// Used to quickly find MemberInfo by member name - DebugType.GetMember(name) uses a loop to search members + protected Dictionary memberFromNameMap; + + protected Expression targetObject; + protected DebugType collectionType; + protected DebugType itemType; + + public GridValuesProvider(Expression targetObject, DebugType collectionType, DebugType itemType) + { + this.targetObject = targetObject; + this.collectionType = collectionType; + this.itemType = itemType; + + this.memberFromNameMap = this.GetItemTypeMembers().MakeDictionary(memberInfo => memberInfo.Name); + } + + /// + /// Gets members that will be displayed as columns. + /// + public IList GetItemTypeMembers() + { + return itemType.GetMembers(this.memberBindingFlags); + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/ListValuesProvider.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/ListValuesProvider.cs index 80729469f0..db0756eedb 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/ListValuesProvider.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/ListValuesProvider.cs @@ -18,23 +18,14 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer /// /// Provides s for debugee objects implementing IList. /// - public class ListValuesProvider : IListValuesProvider + public class ListValuesProvider : GridValuesProvider, IListValuesProvider { - private readonly Debugger.MetaData.BindingFlags bindingFlags = - BindingFlags.Public | BindingFlags.Instance | BindingFlags.Field | BindingFlags.GetProperty; - - private Expression targetObject; - private DebugType iListType; - private DebugType listItemType; - private bool countEvaluated = false; private int count = -1; public ListValuesProvider(Expression targetObject, DebugType iListType, DebugType listItemType) + :base(targetObject, iListType, listItemType) { - this.targetObject = targetObject; - this.iListType = iListType; - this.listItemType = listItemType; } public int GetCount() @@ -50,14 +41,9 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer public ObjectValue GetItemAt(int index) { return ObjectValue.Create( - targetObject.AppendIndexer(index).Evaluate(WindowsDebugger.CurrentProcess).GetPermanentReference(), - //targetObject.AppendIndexer(index), // don't use PermanentReference - possible only for IList though - this.listItemType, this.bindingFlags); - } - - public IList GetItemTypeMembers() - { - return listItemType.GetMembers(this.bindingFlags); + targetObject.AppendIndexer(index).Evaluate(WindowsDebugger.CurrentProcess), + //targetObject.AppendIndexer(index), // use Expression instead of value - possible only for IList though + this.memberFromNameMap); } // TODO move to Utils iListType.EvaluateCount(Expression targetObject) @@ -65,7 +51,7 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer // or targetObject.EvaluateIListCount() <- calls ResolveIListImplementation private int evaluateCount() { - PropertyInfo countProperty = iListType.GetGenericInterface("System.Collections.Generic.ICollection").GetProperty("Count"); + PropertyInfo countProperty = this.collectionType.GetGenericInterface("System.Collections.Generic.ICollection").GetProperty("Count"); try { // Do not get string representation since it can be printed in hex later Value countValue = targetObject.Evaluate(WindowsDebugger.CurrentProcess).GetPropertyValue(countProperty); diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/VirtualizingIEnumerable.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/VirtualizingIEnumerable.cs index 302a210262..3f35150137 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/VirtualizingIEnumerable.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/VirtualizingIEnumerable.cs @@ -13,7 +13,8 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer /// /// A wrapper around IEnumerable<T> with RequestNextItems method for pulling additional items /// from the IEnumerable<T> when needed. - /// Can be used as source for LazyListView. + /// Can be used as source for . + /// (Used to wrap EnumerableValuesProvider.ItemsSource) /// public class VirtualizingIEnumerable : ObservableCollection { diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/PresentationBindings/GridViewColumnHider.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/PresentationBindings/GridViewColumnHider.cs index 041866f802..3b6cf25521 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/PresentationBindings/GridViewColumnHider.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/PresentationBindings/GridViewColumnHider.cs @@ -18,7 +18,7 @@ namespace Debugger.AddIn.Visualizers } /// - /// Utility for hiding and showing columns in GridView (GridViewColumn does not have Visility). + /// Utility for hiding and showing columns in GridView (GridViewColumn does not have Visility property). /// public class GridViewColumnHider { diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/PresentationBindings/ScrollUtils.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/PresentationBindings/ScrollUtils.cs new file mode 100644 index 0000000000..ec511f5d8d --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/PresentationBindings/ScrollUtils.cs @@ -0,0 +1,41 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; + +namespace Debugger.AddIn.Visualizers +{ + /// + /// Description of ScrollUtils. + /// + public static class ScrollUtils + { + /// + /// Searches VisualTree of given object for a ScrollViewer. + /// + public static ScrollViewer GetScrollViewer(this DependencyObject o) + { + var scrollViewer = o as ScrollViewer; + if (scrollViewer != null) { + return scrollViewer; + } + + for (int i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++) + { + var child = VisualTreeHelper.GetChild(o, i); + var result = GetScrollViewer(child); + if (result != null) { + return result; + } + } + return null; + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Utils/IEnumerableExtensions.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Utils/IEnumerableExtensions.cs new file mode 100644 index 0000000000..573d7c5060 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Utils/IEnumerableExtensions.cs @@ -0,0 +1,48 @@ +// +// +// +// +// $Revision$ +// +using System.Collections.Generic; +using System.Linq; +using System; + +namespace Debugger.AddIn.Visualizers.Utils +{ + public static class IEnumerableExtensions + { + /// + /// Builds Dictionary for quickly searching a collection. + /// The keys must be unique. + /// + /// Collection for which to build Dictionary. + /// Function returning key by which to index. + public static Dictionary MakeDictionary(this IEnumerable collection, Func keySelector) + { + Dictionary dictionary = new Dictionary(); + foreach (V item in collection) + { + K key = keySelector(item); + if (dictionary.ContainsKey(key)) throw new InvalidOperationException("MakeDictionary: key " + key + " seen twice"); + dictionary[key] = item; + } + return dictionary; + } + + /// + /// Builds Lookup for quickly searching a collection. + /// + /// Collection for which to build Dictionary. + /// Function returning key by which to index. + public static Lookup MakeLookup(this IEnumerable collection, Func keySelector) + { + Lookup lookup = new Lookup(); + foreach (V item in collection) + { + lookup.Add(keySelector(item), item); + } + return lookup; + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Utils/Lookup.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Utils/Lookup.cs index 2851a2d7cd..5c7fa78f16 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Utils/Lookup.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Utils/Lookup.cs @@ -14,19 +14,20 @@ namespace Debugger.AddIn.Visualizers.Utils /// public class Lookup { - private Dictionary> _dictionary; + /// Wrapped dictionary + private Dictionary> dictionary; public Lookup() { - _dictionary = new Dictionary>(); + dictionary = new Dictionary>(); } public LookupValueCollection this[TKey key] { - get + get { LookupValueCollection values = null; - if (_dictionary.TryGetValue(key, out values)) + if (dictionary.TryGetValue(key, out values)) { return values; } @@ -36,13 +37,13 @@ namespace Debugger.AddIn.Visualizers.Utils public void Add(TKey key, TValue value) { - LookupValueCollection values = null;; - if (!_dictionary.TryGetValue(key, out values)) - { - values = new LookupValueCollection(); - _dictionary.Add(key, values); - } - values.Add(value); + LookupValueCollection values = null; + if (!dictionary.TryGetValue(key, out values)) + { + values = new LookupValueCollection(); + dictionary.Add(key, values); + } + values.Add(value); } } }