mirror of https://github.com/icsharpcode/ILSpy.git
12 changed files with 1924 additions and 3 deletions
@ -0,0 +1,50 @@ |
|||||||
|
// 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.Windows; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.AvalonEdit |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Content of text editor tooltip (used as <see cref="ToolTipRequestEventArgs.ContentToShow"/>),
|
||||||
|
/// specifying whether it should be displayed in a WPF Popup.
|
||||||
|
/// </summary>
|
||||||
|
public interface ITooltip |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// If true, this ITooltip will be displayed in a WPF Popup.
|
||||||
|
/// Otherwise it will be displayed in a WPF Tooltip.
|
||||||
|
/// WPF Popups are (unlike WPF Tooltips) focusable.
|
||||||
|
/// </summary>
|
||||||
|
bool ShowAsPopup { get; } |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Closes this tooltip.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mouseClick">True if close request is raised
|
||||||
|
/// because of mouse click on some SharpDevelop GUI element.</param>
|
||||||
|
/// <returns>True if Close succeeded (that is, can close). False otherwise.</returns>
|
||||||
|
bool Close(bool mouseClick); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when this tooltip decides to close.
|
||||||
|
/// </summary>
|
||||||
|
event RoutedEventHandler Closed; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,417 @@ |
|||||||
|
// 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; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Collections.ObjectModel; |
||||||
|
using System.IO; |
||||||
|
using System.Linq; |
||||||
|
using System.Text; |
||||||
|
using System.Windows; |
||||||
|
using System.Windows.Controls; |
||||||
|
using System.Windows.Media; |
||||||
|
using System.Xml; |
||||||
|
using System.Xml.Linq; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Services |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Extension methods used in SharpDevelop.
|
||||||
|
/// </summary>
|
||||||
|
public static class ExtensionMethods |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Raises the event.
|
||||||
|
/// Does nothing if eventHandler is null.
|
||||||
|
/// Because the event handler is passed as parameter, it is only fetched from the event field one time.
|
||||||
|
/// This makes
|
||||||
|
/// <code>MyEvent.RaiseEvent(x,y);</code>
|
||||||
|
/// thread-safe
|
||||||
|
/// whereas
|
||||||
|
/// <code>if (MyEvent != null) MyEvent(x,y);</code>
|
||||||
|
/// would not be safe.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Using this method is only thread-safe under the Microsoft .NET memory model,
|
||||||
|
/// not under the less strict memory model in the CLI specification.</remarks>
|
||||||
|
public static void RaiseEvent(this EventHandler eventHandler, object sender, EventArgs e) |
||||||
|
{ |
||||||
|
if (eventHandler != null) { |
||||||
|
eventHandler(sender, e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raises the event.
|
||||||
|
/// Does nothing if eventHandler is null.
|
||||||
|
/// Because the event handler is passed as parameter, it is only fetched from the event field one time.
|
||||||
|
/// This makes
|
||||||
|
/// <code>MyEvent.RaiseEvent(x,y);</code>
|
||||||
|
/// thread-safe
|
||||||
|
/// whereas
|
||||||
|
/// <code>if (MyEvent != null) MyEvent(x,y);</code>
|
||||||
|
/// would not be safe.
|
||||||
|
/// </summary>
|
||||||
|
public static void RaiseEvent<T>(this EventHandler<T> eventHandler, object sender, T e) where T : EventArgs |
||||||
|
{ |
||||||
|
if (eventHandler != null) { |
||||||
|
eventHandler(sender, e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs an action for all elements in the input.
|
||||||
|
/// </summary>
|
||||||
|
public static void ForEach<T>(this IEnumerable<T> input, Action<T> action) |
||||||
|
{ |
||||||
|
if (input == null) |
||||||
|
throw new ArgumentNullException("input"); |
||||||
|
foreach (T element in input) { |
||||||
|
action(element); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds all <paramref name="elements"/> to <paramref name="list"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static void AddRange<T>(this ICollection<T> list, IEnumerable<T> elements) |
||||||
|
{ |
||||||
|
foreach (T o in elements) |
||||||
|
list.Add(o); |
||||||
|
} |
||||||
|
|
||||||
|
public static ReadOnlyCollection<T> AsReadOnly<T>(this IList<T> arr) |
||||||
|
{ |
||||||
|
return new ReadOnlyCollection<T>(arr); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a recursive data structure into a flat list.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">The root elements of the recursive data structure.</param>
|
||||||
|
/// <param name="recursion">The function that gets the children of an element.</param>
|
||||||
|
/// <returns>Iterator that enumerates the tree structure in preorder.</returns>
|
||||||
|
public static IEnumerable<T> Flatten<T>(this IEnumerable<T> input, Func<T, IEnumerable<T>> recursion) |
||||||
|
{ |
||||||
|
Stack<IEnumerator<T>> stack = new Stack<IEnumerator<T>>(); |
||||||
|
try { |
||||||
|
stack.Push(input.GetEnumerator()); |
||||||
|
while (stack.Count > 0) { |
||||||
|
while (stack.Peek().MoveNext()) { |
||||||
|
T element = stack.Peek().Current; |
||||||
|
yield return element; |
||||||
|
IEnumerable<T> children = recursion(element); |
||||||
|
if (children != null) { |
||||||
|
stack.Push(children.GetEnumerator()); |
||||||
|
} |
||||||
|
} |
||||||
|
stack.Pop().Dispose(); |
||||||
|
} |
||||||
|
} finally { |
||||||
|
while (stack.Count > 0) { |
||||||
|
stack.Pop().Dispose(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an array containing a part of the array (similar to string.Substring).
|
||||||
|
/// </summary>
|
||||||
|
public static T[] Splice<T>(this T[] array, int startIndex) |
||||||
|
{ |
||||||
|
if (array == null) |
||||||
|
throw new ArgumentNullException("array"); |
||||||
|
return Splice(array, startIndex, array.Length - startIndex); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an array containing a part of the array (similar to string.Substring).
|
||||||
|
/// </summary>
|
||||||
|
public static T[] Splice<T>(this T[] array, int startIndex, int length) |
||||||
|
{ |
||||||
|
if (array == null) |
||||||
|
throw new ArgumentNullException("array"); |
||||||
|
if (startIndex < 0 || startIndex > array.Length) |
||||||
|
throw new ArgumentOutOfRangeException("startIndex", startIndex, "Value must be between 0 and " + array.Length); |
||||||
|
if (length < 0 || length > array.Length - startIndex) |
||||||
|
throw new ArgumentOutOfRangeException("length", length, "Value must be between 0 and " + (array.Length - startIndex)); |
||||||
|
T[] result = new T[length]; |
||||||
|
Array.Copy(array, startIndex, result, 0, length); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
#region System.Drawing <-> WPF conversions
|
||||||
|
public static System.Drawing.Point ToSystemDrawing(this Point p) |
||||||
|
{ |
||||||
|
return new System.Drawing.Point((int)p.X, (int)p.Y); |
||||||
|
} |
||||||
|
|
||||||
|
public static System.Drawing.Size ToSystemDrawing(this Size s) |
||||||
|
{ |
||||||
|
return new System.Drawing.Size((int)s.Width, (int)s.Height); |
||||||
|
} |
||||||
|
|
||||||
|
public static System.Drawing.Rectangle ToSystemDrawing(this Rect r) |
||||||
|
{ |
||||||
|
return new System.Drawing.Rectangle(r.TopLeft.ToSystemDrawing(), r.Size.ToSystemDrawing()); |
||||||
|
} |
||||||
|
|
||||||
|
public static System.Drawing.Color ToSystemDrawing(this System.Windows.Media.Color c) |
||||||
|
{ |
||||||
|
return System.Drawing.Color.FromArgb(c.A, c.R, c.G, c.B); |
||||||
|
} |
||||||
|
|
||||||
|
public static Point ToWpf(this System.Drawing.Point p) |
||||||
|
{ |
||||||
|
return new Point(p.X, p.Y); |
||||||
|
} |
||||||
|
|
||||||
|
public static Size ToWpf(this System.Drawing.Size s) |
||||||
|
{ |
||||||
|
return new Size(s.Width, s.Height); |
||||||
|
} |
||||||
|
|
||||||
|
public static Rect ToWpf(this System.Drawing.Rectangle rect) |
||||||
|
{ |
||||||
|
return new Rect(rect.Location.ToWpf(), rect.Size.ToWpf()); |
||||||
|
} |
||||||
|
|
||||||
|
public static System.Windows.Media.Color ToWpf(this System.Drawing.Color c) |
||||||
|
{ |
||||||
|
return System.Windows.Media.Color.FromArgb(c.A, c.R, c.G, c.B); |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region DPI independence
|
||||||
|
public static Rect TransformToDevice(this Rect rect, Visual visual) |
||||||
|
{ |
||||||
|
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice; |
||||||
|
return Rect.Transform(rect, matrix); |
||||||
|
} |
||||||
|
|
||||||
|
public static Rect TransformFromDevice(this Rect rect, Visual visual) |
||||||
|
{ |
||||||
|
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformFromDevice; |
||||||
|
return Rect.Transform(rect, matrix); |
||||||
|
} |
||||||
|
|
||||||
|
public static Size TransformToDevice(this Size size, Visual visual) |
||||||
|
{ |
||||||
|
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice; |
||||||
|
return new Size(size.Width * matrix.M11, size.Height * matrix.M22); |
||||||
|
} |
||||||
|
|
||||||
|
public static Size TransformFromDevice(this Size size, Visual visual) |
||||||
|
{ |
||||||
|
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformFromDevice; |
||||||
|
return new Size(size.Width * matrix.M11, size.Height * matrix.M22); |
||||||
|
} |
||||||
|
|
||||||
|
public static Point TransformToDevice(this Point point, Visual visual) |
||||||
|
{ |
||||||
|
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice; |
||||||
|
return new Point(point.X * matrix.M11, point.Y * matrix.M22); |
||||||
|
} |
||||||
|
|
||||||
|
public static Point TransformFromDevice(this Point point, Visual visual) |
||||||
|
{ |
||||||
|
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformFromDevice; |
||||||
|
return new Point(point.X * matrix.M11, point.Y * matrix.M22); |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes <param name="stringToRemove" /> from the start of this string.
|
||||||
|
/// Throws ArgumentException if this string does not start with <param name="stringToRemove" />.
|
||||||
|
/// </summary>
|
||||||
|
public static string RemoveStart(this string s, string stringToRemove) |
||||||
|
{ |
||||||
|
if (s == null) |
||||||
|
return null; |
||||||
|
if (string.IsNullOrEmpty(stringToRemove)) |
||||||
|
return s; |
||||||
|
if (!s.StartsWith(stringToRemove)) |
||||||
|
throw new ArgumentException(string.Format("{0} does not start with {1}", s, stringToRemove)); |
||||||
|
return s.Substring(stringToRemove.Length); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes <param name="stringToRemove" /> from the end of this string.
|
||||||
|
/// Throws ArgumentException if this string does not end with <param name="stringToRemove" />.
|
||||||
|
/// </summary>
|
||||||
|
public static string RemoveEnd(this string s, string stringToRemove) |
||||||
|
{ |
||||||
|
if (s == null) |
||||||
|
return null; |
||||||
|
if (string.IsNullOrEmpty(stringToRemove)) |
||||||
|
return s; |
||||||
|
if (!s.EndsWith(stringToRemove)) |
||||||
|
throw new ArgumentException(string.Format("{0} does not end with {1}", s, stringToRemove)); |
||||||
|
return s.Substring(0, s.Length - stringToRemove.Length); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Takes at most <param name="length" /> first characters from string.
|
||||||
|
/// String can be null.
|
||||||
|
/// </summary>
|
||||||
|
public static string TakeStart(this string s, int length) |
||||||
|
{ |
||||||
|
if (string.IsNullOrEmpty(s) || length >= s.Length) |
||||||
|
return s; |
||||||
|
return s.Substring(0, length); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Takes at most <param name="length" /> first characters from string, and appends '...' if string is longer.
|
||||||
|
/// String can be null.
|
||||||
|
/// </summary>
|
||||||
|
public static string TakeStartEllipsis(this string s, int length) |
||||||
|
{ |
||||||
|
if (string.IsNullOrEmpty(s) || length >= s.Length) |
||||||
|
return s; |
||||||
|
return s.Substring(0, length) + "..."; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public static string Replace(this string original, string pattern, string replacement, StringComparison comparisonType) |
||||||
|
{ |
||||||
|
if (original == null) |
||||||
|
throw new ArgumentNullException("original"); |
||||||
|
if (pattern == null) |
||||||
|
throw new ArgumentNullException("pattern"); |
||||||
|
if (pattern.Length == 0) |
||||||
|
throw new ArgumentException("String cannot be of zero length.", "pattern"); |
||||||
|
if (comparisonType != StringComparison.Ordinal && comparisonType != StringComparison.OrdinalIgnoreCase) |
||||||
|
throw new NotSupportedException("Currently only ordinal comparisons are implemented."); |
||||||
|
|
||||||
|
StringBuilder result = new StringBuilder(original.Length); |
||||||
|
int currentPos = 0; |
||||||
|
int nextMatch = original.IndexOf(pattern, comparisonType); |
||||||
|
while (nextMatch >= 0) { |
||||||
|
result.Append(original, currentPos, nextMatch - currentPos); |
||||||
|
// The following line restricts this method to ordinal comparisons:
|
||||||
|
// for non-ordinal comparisons, the match length might be different than the pattern length.
|
||||||
|
currentPos = nextMatch + pattern.Length; |
||||||
|
result.Append(replacement); |
||||||
|
|
||||||
|
nextMatch = original.IndexOf(pattern, currentPos, comparisonType); |
||||||
|
} |
||||||
|
|
||||||
|
result.Append(original, currentPos, original.Length - currentPos); |
||||||
|
return result.ToString(); |
||||||
|
} |
||||||
|
|
||||||
|
public static byte[] GetBytesWithPreamble(this Encoding encoding, string text) |
||||||
|
{ |
||||||
|
byte[] encodedText = encoding.GetBytes(text); |
||||||
|
byte[] bom = encoding.GetPreamble(); |
||||||
|
if (bom != null && bom.Length > 0) { |
||||||
|
byte[] result = new byte[bom.Length + encodedText.Length]; |
||||||
|
bom.CopyTo(result, 0); |
||||||
|
encodedText.CopyTo(result, bom.Length); |
||||||
|
return result; |
||||||
|
} else { |
||||||
|
return encodedText; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the index of the first element for which <paramref name="predicate"/> returns true.
|
||||||
|
/// If none of the items in the list fits the <paramref name="predicate"/>, -1 is returned.
|
||||||
|
/// </summary>
|
||||||
|
public static int FindIndex<T>(this IList<T> list, Func<T, bool> predicate) |
||||||
|
{ |
||||||
|
for (int i = 0; i < list.Count; i++) { |
||||||
|
if (predicate(list[i])) |
||||||
|
return i; |
||||||
|
} |
||||||
|
|
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds item to the list if the item is not null.
|
||||||
|
/// </summary>
|
||||||
|
public static void AddIfNotNull<T>(this IList<T> list, T itemToAdd) |
||||||
|
{ |
||||||
|
if (itemToAdd != null) |
||||||
|
list.Add(itemToAdd); |
||||||
|
} |
||||||
|
|
||||||
|
public static void WriteTo(this Stream sourceStream, Stream targetStream) |
||||||
|
{ |
||||||
|
byte[] buffer = new byte[4096]; |
||||||
|
int bytes; |
||||||
|
while ((bytes = sourceStream.Read(buffer, 0, buffer.Length)) > 0) |
||||||
|
targetStream.Write(buffer, 0, bytes); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scrolling related helpers.
|
||||||
|
/// </summary>
|
||||||
|
public static class ScrollUtils |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Searches VisualTree of given object for a ScrollViewer.
|
||||||
|
/// </summary>
|
||||||
|
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; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scrolls ScrollViewer up by given offset.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">Offset by which to scroll up. Should be positive.</param>
|
||||||
|
public static void ScrollUp(this ScrollViewer scrollViewer, double offset) |
||||||
|
{ |
||||||
|
ScrollUtils.ScrollByVerticalOffset(scrollViewer, -offset); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scrolls ScrollViewer down by given offset.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">Offset by which to scroll down. Should be positive.</param>
|
||||||
|
public static void ScrollDown(this ScrollViewer scrollViewer, double offset) |
||||||
|
{ |
||||||
|
ScrollUtils.ScrollByVerticalOffset(scrollViewer, offset); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scrolls ScrollViewer by given vertical offset.
|
||||||
|
/// </summary>
|
||||||
|
public static void ScrollByVerticalOffset(this ScrollViewer scrollViewer, double offset) |
||||||
|
{ |
||||||
|
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + offset); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,98 @@ |
|||||||
|
// 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.Collections.Generic; |
||||||
|
using System.Windows.Controls.Primitives; |
||||||
|
|
||||||
|
using ICSharpCode.NRefactory.CSharp; |
||||||
|
using ILSpy.Debugger.Models.TreeModel; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Tooltips |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Popup containing <see cref="DebuggerTooltipControl"></see>.
|
||||||
|
/// </summary>
|
||||||
|
public class DebuggerPopup : Popup |
||||||
|
{ |
||||||
|
internal DebuggerTooltipControl contentControl; |
||||||
|
|
||||||
|
public DebuggerPopup(DebuggerTooltipControl parentControl, AstLocation logicalPosition, bool showPins = true) |
||||||
|
{ |
||||||
|
this.contentControl = new DebuggerTooltipControl(parentControl, logicalPosition, showPins); |
||||||
|
this.contentControl.containingPopup = this; |
||||||
|
this.Child = this.contentControl; |
||||||
|
this.IsLeaf = false; |
||||||
|
|
||||||
|
//this.KeyDown += new KeyEventHandler(DebuggerPopup_KeyDown);
|
||||||
|
|
||||||
|
//this.contentControl.Focusable = true;
|
||||||
|
//Keyboard.Focus(this.contentControl);
|
||||||
|
//this.AllowsTransparency = true;
|
||||||
|
//this.PopupAnimation = PopupAnimation.Slide;
|
||||||
|
} |
||||||
|
|
||||||
|
// attempt to propagate shortcuts to main windows when Popup is focusable (needed for keyboard scrolling + editing)
|
||||||
|
/*void DebuggerPopup_KeyDown(object sender, KeyEventArgs e) |
||||||
|
{ |
||||||
|
LoggingService.Debug("Unhandled popup key down: " + e.Key); |
||||||
|
RaiseEventPair(WorkbenchSingleton.MainWindow, PreviewKeyDownEvent, KeyDownEvent, |
||||||
|
new KeyEventArgs(e.KeyboardDevice, e.InputSource, e.Timestamp, e.Key)); |
||||||
|
} |
||||||
|
|
||||||
|
// copied from CompletionWindowBase
|
||||||
|
static bool RaiseEventPair(UIElement target, RoutedEvent previewEvent, RoutedEvent @event, RoutedEventArgs args) |
||||||
|
{ |
||||||
|
if (target == null) |
||||||
|
throw new ArgumentNullException("target"); |
||||||
|
if (previewEvent == null) |
||||||
|
throw new ArgumentNullException("previewEvent"); |
||||||
|
if (@event == null) |
||||||
|
throw new ArgumentNullException("event"); |
||||||
|
if (args == null) |
||||||
|
throw new ArgumentNullException("args"); |
||||||
|
args.RoutedEvent = previewEvent; |
||||||
|
target.RaiseEvent(args); |
||||||
|
args.RoutedEvent = @event; |
||||||
|
target.RaiseEvent(args); |
||||||
|
return args.Handled; |
||||||
|
}*/ |
||||||
|
|
||||||
|
public IEnumerable<ITreeNode> ItemsSource |
||||||
|
{ |
||||||
|
get { return this.contentControl.ItemsSource; } |
||||||
|
set { this.contentControl.SetItemsSource(value); } |
||||||
|
} |
||||||
|
|
||||||
|
private bool isLeaf; |
||||||
|
public bool IsLeaf |
||||||
|
{ |
||||||
|
get { return isLeaf; } |
||||||
|
set |
||||||
|
{ |
||||||
|
isLeaf = value; |
||||||
|
// leaf popup closes on lost focus
|
||||||
|
this.StaysOpen = !isLeaf; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected override void OnClosed(EventArgs e) |
||||||
|
{ |
||||||
|
base.OnClosed(e); |
||||||
|
if (isLeaf) { |
||||||
|
this.contentControl.CloseOnLostFocus(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void Open() |
||||||
|
{ |
||||||
|
this.IsOpen = true; |
||||||
|
} |
||||||
|
|
||||||
|
public void CloseSelfAndChildren() |
||||||
|
{ |
||||||
|
this.contentControl.CloseChildPopups(); |
||||||
|
this.IsOpen = false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,230 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<UserControl |
||||||
|
x:Class="ILSpy.Debugger.Tooltips.DebuggerTooltipControl" |
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||||
|
xmlns:aero="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero" |
||||||
|
xmlns:debugging="clr-namespace:ILSpy.Debugger.Tooltips" |
||||||
|
xmlns:localControls="clr-namespace:ILSpy.Debugger.Tooltips" |
||||||
|
Background="Transparent"> |
||||||
|
<UserControl.Resources> |
||||||
|
<ResourceDictionary> |
||||||
|
<ResourceDictionary.MergedDictionaries> |
||||||
|
<ResourceDictionary |
||||||
|
Source="VisualizerPicker.xaml" /> |
||||||
|
<ResourceDictionary |
||||||
|
Source="PinControlsDictionary.xaml" /> |
||||||
|
</ResourceDictionary.MergedDictionaries> |
||||||
|
</ResourceDictionary> |
||||||
|
</UserControl.Resources> |
||||||
|
<StackPanel |
||||||
|
Orientation="Vertical"> |
||||||
|
<RepeatButton |
||||||
|
Name="btnUp" |
||||||
|
Focusable="False" |
||||||
|
Style="{StaticResource upButtonStyle}" |
||||||
|
Content="^" |
||||||
|
Click="BtnUp_Click"></RepeatButton> |
||||||
|
<DataGrid |
||||||
|
VerticalScrollBarVisibility="Disabled" |
||||||
|
HorizontalScrollBarVisibility="Disabled" |
||||||
|
GridLinesVisibility="None" |
||||||
|
RowHeight="18" |
||||||
|
MaxHeight="202" |
||||||
|
SelectionMode="Single" |
||||||
|
SelectionUnit="FullRow" |
||||||
|
ItemsSource="{Binding}" |
||||||
|
Name="dataGrid" |
||||||
|
AutoGenerateColumns="False" |
||||||
|
CanUserAddRows="False" |
||||||
|
HeadersVisibility="None" |
||||||
|
BorderBrush="Gray" |
||||||
|
BorderThickness="1"> |
||||||
|
<DataGrid.Background> |
||||||
|
<!-- Control backgound --> |
||||||
|
<LinearGradientBrush |
||||||
|
StartPoint="0,-0.03" |
||||||
|
EndPoint="0,1"> |
||||||
|
<GradientStop |
||||||
|
Color="White" /> |
||||||
|
<GradientStop |
||||||
|
Color="#FFFAFCFE" |
||||||
|
Offset="0.983" /> |
||||||
|
<GradientStop |
||||||
|
Color="#FFECF7FC" |
||||||
|
Offset="0.07" /> |
||||||
|
<GradientStop |
||||||
|
Color="#FFEEF7FA" |
||||||
|
Offset="0.436" /> |
||||||
|
</LinearGradientBrush> |
||||||
|
</DataGrid.Background> |
||||||
|
<DataGrid.RowStyle> |
||||||
|
<Style |
||||||
|
TargetType="{x:Type DataGridRow}"> |
||||||
|
<Setter |
||||||
|
Property="Background" |
||||||
|
Value="Transparent"></Setter> |
||||||
|
<Style.Triggers> |
||||||
|
<Trigger |
||||||
|
Property="IsMouseOver" |
||||||
|
Value="True"> |
||||||
|
<Setter |
||||||
|
Property="Background" |
||||||
|
Value="#FFE2F6FE" /> |
||||||
|
</Trigger> |
||||||
|
</Style.Triggers> |
||||||
|
</Style> |
||||||
|
</DataGrid.RowStyle> |
||||||
|
<DataGrid.CellStyle> |
||||||
|
<Style |
||||||
|
TargetType="{x:Type DataGridCell}"> |
||||||
|
<Setter |
||||||
|
Property="Focusable" |
||||||
|
Value="false" /> |
||||||
|
<!-- Focusable=true blocks shortcuts if cell is focused --> |
||||||
|
<Setter |
||||||
|
Property="BorderThickness" |
||||||
|
Value="0" /> |
||||||
|
<Style.Triggers> |
||||||
|
<Trigger |
||||||
|
Property="IsSelected" |
||||||
|
Value="True"> |
||||||
|
<!-- disable selection highlight --> |
||||||
|
<Setter |
||||||
|
Property="Foreground" |
||||||
|
Value="Black" /> |
||||||
|
<Setter |
||||||
|
Property="Background" |
||||||
|
Value="{x:Null}" /> |
||||||
|
</Trigger> |
||||||
|
</Style.Triggers> |
||||||
|
</Style> |
||||||
|
</DataGrid.CellStyle> |
||||||
|
<DataGrid.Columns> |
||||||
|
<DataGridTemplateColumn> |
||||||
|
<!-- "Plus" expander --> |
||||||
|
<DataGridTemplateColumn.CellTemplate> |
||||||
|
<DataTemplate> |
||||||
|
<Grid |
||||||
|
Background="White"> |
||||||
|
<StackPanel |
||||||
|
VerticalAlignment="Center"> |
||||||
|
<ToggleButton |
||||||
|
x:Name="btnExpander" |
||||||
|
Style="{StaticResource ExpandCollapseToggleStyle}" |
||||||
|
Click="btnExpander_Click" |
||||||
|
Padding="0" |
||||||
|
Margin="0" /> |
||||||
|
</StackPanel> |
||||||
|
</Grid> |
||||||
|
<DataTemplate.Triggers> |
||||||
|
<DataTrigger |
||||||
|
Binding="{Binding Path=HasChildNodes}" |
||||||
|
Value="False"> |
||||||
|
<Setter |
||||||
|
TargetName="btnExpander" |
||||||
|
Property="Visibility" |
||||||
|
Value="Collapsed" /> |
||||||
|
</DataTrigger> |
||||||
|
</DataTemplate.Triggers> |
||||||
|
</DataTemplate> |
||||||
|
</DataGridTemplateColumn.CellTemplate> |
||||||
|
</DataGridTemplateColumn> |
||||||
|
<!-- Icon --> |
||||||
|
<DataGridTemplateColumn> |
||||||
|
<DataGridTemplateColumn.CellTemplate> |
||||||
|
<DataTemplate> |
||||||
|
<Image |
||||||
|
Source="{Binding ImageSource}"></Image> |
||||||
|
</DataTemplate> |
||||||
|
</DataGridTemplateColumn.CellTemplate> |
||||||
|
</DataGridTemplateColumn> |
||||||
|
<DataGridTemplateColumn |
||||||
|
MinWidth="20" |
||||||
|
Header="Name"> |
||||||
|
<!-- Name --> |
||||||
|
<DataGridTemplateColumn.CellTemplate> |
||||||
|
<DataTemplate> |
||||||
|
<Border |
||||||
|
BorderBrush="#FFDDDDDD" |
||||||
|
BorderThickness="0 0 1 0"> |
||||||
|
<TextBlock |
||||||
|
Style="{StaticResource TextBlockStyle}" |
||||||
|
Text="{Binding Path=Name, Mode=OneWay}" |
||||||
|
VerticalAlignment="Top"></TextBlock> |
||||||
|
</Border> |
||||||
|
</DataTemplate> |
||||||
|
</DataGridTemplateColumn.CellTemplate> |
||||||
|
</DataGridTemplateColumn> |
||||||
|
<!-- Visualizer picker --> |
||||||
|
<!--<DataGridTemplateColumn> |
||||||
|
<DataGridTemplateColumn.CellTemplate> |
||||||
|
<DataTemplate> |
||||||
|
<debugging:VisualizerPicker |
||||||
|
Focusable="False" |
||||||
|
x:Name="visPicker" |
||||||
|
ItemsSource="{Binding Path=VisualizerCommands, Mode=OneWay}" |
||||||
|
Margin="4 0 0 0"></debugging:VisualizerPicker> |
||||||
|
<DataTemplate.Triggers> |
||||||
|
<DataTrigger |
||||||
|
Binding="{Binding Path=HasVisualizerCommands}" |
||||||
|
Value="False"> |
||||||
|
<Setter |
||||||
|
TargetName="visPicker" |
||||||
|
Property="Visibility" |
||||||
|
Value="Collapsed" /> |
||||||
|
</DataTrigger> |
||||||
|
</DataTemplate.Triggers> |
||||||
|
</DataTemplate> |
||||||
|
</DataGridTemplateColumn.CellTemplate> |
||||||
|
</DataGridTemplateColumn>--> |
||||||
|
<DataGridTemplateColumn |
||||||
|
MinWidth="20" |
||||||
|
Header="Text"> |
||||||
|
<!-- Text (value) --> |
||||||
|
<DataGridTemplateColumn.CellTemplate> |
||||||
|
<DataTemplate> |
||||||
|
<TextBox |
||||||
|
Style="{StaticResource TextStyle}" |
||||||
|
IsEnabled="{Binding CanSetText}" |
||||||
|
KeyUp="TextBox_KeyUp" |
||||||
|
Text="{Binding Path=Text}" /> |
||||||
|
</DataTemplate> |
||||||
|
</DataGridTemplateColumn.CellTemplate> |
||||||
|
</DataGridTemplateColumn> |
||||||
|
<DataGridTemplateColumn> |
||||||
|
<!-- Pin --> |
||||||
|
<DataGridTemplateColumn.CellTemplate> |
||||||
|
<DataTemplate> |
||||||
|
<ToggleButton |
||||||
|
IsChecked="{Binding IsPinned}" |
||||||
|
DataContext="{Binding}" |
||||||
|
Visibility="Collapsed" |
||||||
|
Name="PinButton" |
||||||
|
VerticalAlignment="Center" |
||||||
|
Checked="PinButton_Checked" |
||||||
|
Unchecked="PinButton_Unchecked" |
||||||
|
Template="{StaticResource PinTooltipButtonTemplate}" /> |
||||||
|
<DataTemplate.Triggers> |
||||||
|
<DataTrigger |
||||||
|
Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGridRow}}" |
||||||
|
Value="True"> |
||||||
|
<Setter |
||||||
|
TargetName="PinButton" |
||||||
|
Property="Visibility" |
||||||
|
Value="Visible" /> |
||||||
|
</DataTrigger> |
||||||
|
</DataTemplate.Triggers> |
||||||
|
</DataTemplate> |
||||||
|
</DataGridTemplateColumn.CellTemplate> |
||||||
|
</DataGridTemplateColumn> |
||||||
|
</DataGrid.Columns> |
||||||
|
</DataGrid> |
||||||
|
<RepeatButton |
||||||
|
Name="btnDown" |
||||||
|
Focusable="False" |
||||||
|
Style="{StaticResource downButtonStyle}" |
||||||
|
Content="v" |
||||||
|
Click="BtnDown_Click"></RepeatButton> |
||||||
|
</StackPanel> |
||||||
|
</UserControl> |
@ -0,0 +1,403 @@ |
|||||||
|
// 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.Collections.Generic; |
||||||
|
using System.Globalization; |
||||||
|
using System.Windows; |
||||||
|
using System.Windows.Controls; |
||||||
|
using System.Windows.Controls.Primitives; |
||||||
|
using System.Windows.Documents; |
||||||
|
using System.Windows.Input; |
||||||
|
using System.Windows.Media; |
||||||
|
using System.Windows.Media.Animation; |
||||||
|
using System.Windows.Shapes; |
||||||
|
|
||||||
|
using ICSharpCode.NRefactory.CSharp; |
||||||
|
using ILSpy.Debugger.AvalonEdit; |
||||||
|
using ILSpy.Debugger.Models.TreeModel; |
||||||
|
using ILSpy.Debugger.Services; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Tooltips |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Default Control used as content of SharpDevelop debugger tooltips.
|
||||||
|
/// </summary>
|
||||||
|
public partial class DebuggerTooltipControl : UserControl, ITooltip |
||||||
|
{ |
||||||
|
private const double ChildPopupOpenXOffet = 16; |
||||||
|
private const double ChildPopupOpenYOffet = 15; |
||||||
|
private const int InitialItemsCount = 12; |
||||||
|
private const int VisibleItemsCount = 11; |
||||||
|
|
||||||
|
private bool showPins = true; |
||||||
|
private LazyItemsControl<ITreeNode> lazyGrid; |
||||||
|
private IEnumerable<ITreeNode> itemsSource; |
||||||
|
readonly AstLocation logicalPosition; |
||||||
|
|
||||||
|
public DebuggerTooltipControl(AstLocation logicalPosition) |
||||||
|
{ |
||||||
|
this.logicalPosition = logicalPosition; |
||||||
|
InitializeComponent(); |
||||||
|
|
||||||
|
Loaded += new RoutedEventHandler(OnLoaded); |
||||||
|
} |
||||||
|
|
||||||
|
public DebuggerTooltipControl(AstLocation logicalPosition, ITreeNode node) |
||||||
|
: this(logicalPosition, new ITreeNode[] { node }) |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public DebuggerTooltipControl(AstLocation logicalPosition, IEnumerable<ITreeNode> nodes) |
||||||
|
: this(logicalPosition) |
||||||
|
{ |
||||||
|
this.itemsSource = nodes; |
||||||
|
} |
||||||
|
|
||||||
|
public DebuggerTooltipControl(DebuggerTooltipControl parentControl, AstLocation logicalPosition, bool showPins = true) |
||||||
|
: this(logicalPosition) |
||||||
|
{ |
||||||
|
this.parentControl = parentControl; |
||||||
|
this.showPins = showPins; |
||||||
|
} |
||||||
|
|
||||||
|
private void OnLoaded(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
if (!showPins) { |
||||||
|
dataGrid.Columns[5].Visibility = Visibility.Collapsed; |
||||||
|
} |
||||||
|
|
||||||
|
SetItemsSource(this.itemsSource); |
||||||
|
} |
||||||
|
|
||||||
|
public event RoutedEventHandler Closed; |
||||||
|
protected void OnClosed() |
||||||
|
{ |
||||||
|
if (this.Closed != null) { |
||||||
|
this.Closed(this, new RoutedEventArgs()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public IEnumerable<ITreeNode> ItemsSource { |
||||||
|
get { return this.itemsSource; } |
||||||
|
} |
||||||
|
|
||||||
|
public void SetItemsSource(IEnumerable<ITreeNode> value) { |
||||||
|
this.itemsSource = value; |
||||||
|
this.lazyGrid = new LazyItemsControl<ITreeNode>(this.dataGrid, InitialItemsCount); |
||||||
|
|
||||||
|
// // HACK for updating the pins in tooltip
|
||||||
|
// var observable = new List<ITreeNode>();
|
||||||
|
// this.itemsSource.ForEach(item => observable.Add(item));
|
||||||
|
//
|
||||||
|
// // verify if at the line of the root there's a pin bookmark
|
||||||
|
// ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveViewContent as ITextEditorProvider;
|
||||||
|
// var editor = provider.TextEditor;
|
||||||
|
// if (editor != null) {
|
||||||
|
// var pin = BookmarkManager.Bookmarks.Find(
|
||||||
|
// b => b is PinBookmark &&
|
||||||
|
// b.Location.Line == logicalPosition.Line &&
|
||||||
|
// b.FileName == editor.FileName) as PinBookmark;
|
||||||
|
//
|
||||||
|
// if (pin != null) {
|
||||||
|
// observable.ForEach(item => { // TODO: find a way not to use "observable"
|
||||||
|
// if (pin.ContainsNode(item))
|
||||||
|
// item.IsPinned = true;
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
var source = new VirtualizingIEnumerable<ITreeNode>(value); |
||||||
|
lazyGrid.ItemsSource = source; |
||||||
|
this.dataGrid.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(handleScroll)); |
||||||
|
|
||||||
|
if (this.lazyGrid.ItemsSourceTotalCount != null) { |
||||||
|
// hide up/down buttons if too few items
|
||||||
|
btnUp.Visibility = btnDown.Visibility = |
||||||
|
this.lazyGrid.ItemsSourceTotalCount.Value <= VisibleItemsCount ? Visibility.Collapsed : Visibility.Visible; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//public Location LogicalPosition { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool ShowAsPopup |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool Close(bool mouseClick) |
||||||
|
{ |
||||||
|
if (mouseClick || (!mouseClick && !isChildExpanded)) { |
||||||
|
CloseChildPopups(); |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private DebuggerPopup childPopup { get; set; } |
||||||
|
private DebuggerTooltipControl parentControl { get; set; } |
||||||
|
internal DebuggerPopup containingPopup { get; set; } |
||||||
|
|
||||||
|
bool isChildExpanded |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.childPopup != null && this.childPopup.IsOpen; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private ToggleButton expandedButton; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Closes the child popup of this control, if it exists.
|
||||||
|
/// </summary>
|
||||||
|
public void CloseChildPopups() |
||||||
|
{ |
||||||
|
if (this.expandedButton != null) { |
||||||
|
this.expandedButton.IsChecked = false; |
||||||
|
this.expandedButton = null; |
||||||
|
// nice simple example of indirect recursion
|
||||||
|
this.childPopup.CloseSelfAndChildren(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void CloseOnLostFocus() |
||||||
|
{ |
||||||
|
// when we close, parent becomes leaf
|
||||||
|
if (this.containingPopup != null) { |
||||||
|
this.containingPopup.IsLeaf = true; |
||||||
|
} |
||||||
|
if (!this.IsMouseOver) { |
||||||
|
if (this.containingPopup != null) { |
||||||
|
this.containingPopup.IsOpen = false; |
||||||
|
this.containingPopup.IsLeaf = false; |
||||||
|
} |
||||||
|
if (this.parentControl != null) { |
||||||
|
this.parentControl.CloseOnLostFocus(); |
||||||
|
} |
||||||
|
OnClosed(); |
||||||
|
} else { |
||||||
|
// leaf closed because of click inside this control - stop the closing chain
|
||||||
|
if (this.expandedButton != null && !this.expandedButton.IsMouseOver) { |
||||||
|
this.expandedButton.IsChecked = false; |
||||||
|
this.expandedButton = null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void btnExpander_Click(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
var clickedButton = (ToggleButton)e.OriginalSource; |
||||||
|
var clickedNode = (ITreeNode)clickedButton.DataContext; |
||||||
|
// use device independent units, because child popup Left/Top are in independent units
|
||||||
|
Point buttonPos = clickedButton.PointToScreen(new Point(0, 0)).TransformFromDevice(clickedButton); |
||||||
|
|
||||||
|
if (clickedButton.IsChecked.GetValueOrDefault(false)) { |
||||||
|
CloseChildPopups(); |
||||||
|
this.expandedButton = clickedButton; |
||||||
|
|
||||||
|
// open child Popup
|
||||||
|
if (this.childPopup == null) { |
||||||
|
this.childPopup = new DebuggerPopup(this, logicalPosition, false); |
||||||
|
this.childPopup.Placement = PlacementMode.Absolute; |
||||||
|
} |
||||||
|
if (this.containingPopup != null) { |
||||||
|
this.containingPopup.IsLeaf = false; |
||||||
|
} |
||||||
|
this.childPopup.IsLeaf = true; |
||||||
|
this.childPopup.HorizontalOffset = buttonPos.X + ChildPopupOpenXOffet; |
||||||
|
this.childPopup.VerticalOffset = buttonPos.Y + ChildPopupOpenYOffet; |
||||||
|
this.childPopup.ItemsSource = clickedNode.ChildNodes; |
||||||
|
this.childPopup.Open(); |
||||||
|
} else { |
||||||
|
CloseChildPopups(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void handleScroll(object sender, ScrollChangedEventArgs e) |
||||||
|
{ |
||||||
|
btnUp.IsEnabled = !this.lazyGrid.IsScrolledToStart; |
||||||
|
btnDown.IsEnabled = !this.lazyGrid.IsScrolledToEnd; |
||||||
|
} |
||||||
|
|
||||||
|
void BtnUp_Click(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
this.lazyGrid.ScrollViewer.ScrollUp(1); |
||||||
|
} |
||||||
|
|
||||||
|
void BtnDown_Click(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
this.lazyGrid.ScrollViewer.ScrollDown(1); |
||||||
|
} |
||||||
|
|
||||||
|
#region Edit value in tooltip
|
||||||
|
|
||||||
|
void TextBox_KeyUp(object sender, KeyEventArgs e) |
||||||
|
{ |
||||||
|
if (e.Key == Key.Escape) { |
||||||
|
dataGrid.Focus(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (e.Key == Key.Enter) { |
||||||
|
dataGrid.Focus(); |
||||||
|
// set new value
|
||||||
|
var textBox = (TextBox)e.OriginalSource; |
||||||
|
var newValue = textBox.Text; |
||||||
|
var node = ((FrameworkElement)sender).DataContext as ITreeNode; |
||||||
|
SaveNewValue(node, textBox.Text); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void TextBox_LostFocus(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
var textBox = (TextBox)e.OriginalSource; |
||||||
|
var newValue = textBox.Text; |
||||||
|
var node = ((FrameworkElement)sender).DataContext as ITreeNode; |
||||||
|
SaveNewValue(node, textBox.Text); |
||||||
|
} |
||||||
|
|
||||||
|
void SaveNewValue(ITreeNode node, string newValue) |
||||||
|
{ |
||||||
|
if(node != null && node.SetText(newValue)) { |
||||||
|
// show adorner
|
||||||
|
var adornerLayer = AdornerLayer.GetAdornerLayer(dataGrid); |
||||||
|
var adorners = adornerLayer.GetAdorners(dataGrid); |
||||||
|
if (adorners != null && adorners.Length != 0) |
||||||
|
adornerLayer.Remove(adorners[0]); |
||||||
|
SavedAdorner adorner = new SavedAdorner(dataGrid); |
||||||
|
adornerLayer.Add(adorner); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Pining checked/unchecked
|
||||||
|
|
||||||
|
void PinButton_Checked(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
// ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveViewContent as ITextEditorProvider;
|
||||||
|
// var editor = provider.TextEditor;
|
||||||
|
// if (editor == null) return;
|
||||||
|
// var node = (ITreeNode)(((ToggleButton)(e.OriginalSource)).DataContext);
|
||||||
|
//
|
||||||
|
// if (!string.IsNullOrEmpty(editor.FileName)) {
|
||||||
|
//
|
||||||
|
// // verify if at the line of the root there's a pin bookmark
|
||||||
|
// var pin = BookmarkManager.Bookmarks.Find(
|
||||||
|
// b => b is PinBookmark &&
|
||||||
|
// b.LineNumber == logicalPosition.Line &&
|
||||||
|
// b.FileName == editor.FileName) as PinBookmark;
|
||||||
|
//
|
||||||
|
// if (pin == null) {
|
||||||
|
// pin = new PinBookmark(editor.FileName, logicalPosition);
|
||||||
|
// // show pinned DebuggerPopup
|
||||||
|
// if (pin.Popup == null) {
|
||||||
|
// pin.Popup = new PinDebuggerControl();
|
||||||
|
// pin.Popup.Mark = pin;
|
||||||
|
// Rect rect = new Rect(this.DesiredSize);
|
||||||
|
// var point = this.PointToScreen(rect.TopRight);
|
||||||
|
// pin.Popup.Location = new Point { X = 500, Y = point.Y - 150 };
|
||||||
|
// pin.Nodes.Add(node);
|
||||||
|
// pin.Popup.ItemsSource = pin.Nodes;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // do actions
|
||||||
|
// pin.Popup.Open();
|
||||||
|
// BookmarkManager.AddMark(pin);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// if (!pin.ContainsNode(node)) {
|
||||||
|
// pin.Nodes.Add(node);
|
||||||
|
// pin.Popup.ItemsSource = pin.Nodes;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
} |
||||||
|
|
||||||
|
void PinButton_Unchecked(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
// ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveViewContent as ITextEditorProvider;
|
||||||
|
// var editor = provider.TextEditor;
|
||||||
|
// if (editor == null) return;
|
||||||
|
//
|
||||||
|
// if (!string.IsNullOrEmpty(editor.FileName)) {
|
||||||
|
// // remove from pinned DebuggerPopup
|
||||||
|
// var pin = BookmarkManager.Bookmarks.Find(
|
||||||
|
// b => b is PinBookmark &&
|
||||||
|
// b.LineNumber == logicalPosition.Line &&
|
||||||
|
// b.FileName == editor.FileName) as PinBookmark;
|
||||||
|
// if (pin == null) return;
|
||||||
|
//
|
||||||
|
// ToggleButton button = (ToggleButton)e.OriginalSource;
|
||||||
|
// pin.RemoveNode((ITreeNode)button.DataContext);
|
||||||
|
// pin.Popup.ItemsSource = pin.Nodes;
|
||||||
|
// // remove if no more data pins are available
|
||||||
|
// if (pin.Nodes.Count == 0) {
|
||||||
|
// pin.Popup.Close();
|
||||||
|
//
|
||||||
|
// BookmarkManager.RemoveMark(pin);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
} |
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Saved Adorner
|
||||||
|
|
||||||
|
class SavedAdorner : Adorner |
||||||
|
{ |
||||||
|
public SavedAdorner(UIElement adornedElement) : base(adornedElement) |
||||||
|
{ |
||||||
|
Loaded += delegate { Show(); }; |
||||||
|
} |
||||||
|
|
||||||
|
protected override void OnRender(DrawingContext drawingContext) |
||||||
|
{ |
||||||
|
Rect adornedElementRect = new Rect(this.AdornedElement.DesiredSize); |
||||||
|
|
||||||
|
// Some arbitrary drawing implements.
|
||||||
|
var formatedText = new FormattedText("Saved", |
||||||
|
CultureInfo.CurrentCulture, |
||||||
|
FlowDirection.LeftToRight, |
||||||
|
new Typeface(new FontFamily("Arial"), |
||||||
|
FontStyles.Normal, |
||||||
|
FontWeights.Black, |
||||||
|
FontStretches.Expanded), |
||||||
|
8d, |
||||||
|
Brushes.Black); |
||||||
|
|
||||||
|
|
||||||
|
drawingContext.DrawText(formatedText, |
||||||
|
new Point(adornedElementRect.TopRight.X - formatedText.Width - 2, |
||||||
|
adornedElementRect.TopRight.Y)); |
||||||
|
} |
||||||
|
|
||||||
|
private void Show() |
||||||
|
{ |
||||||
|
DoubleAnimation animation = new DoubleAnimation(); |
||||||
|
animation.From = 1; |
||||||
|
animation.To = 0; |
||||||
|
|
||||||
|
animation.Duration = new Duration(TimeSpan.FromSeconds(2)); |
||||||
|
animation.SetValue(Storyboard.TargetProperty, this); |
||||||
|
animation.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath(Rectangle.OpacityProperty)); |
||||||
|
|
||||||
|
Storyboard board = new Storyboard(); |
||||||
|
board.Children.Add(animation); |
||||||
|
|
||||||
|
board.Begin(this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endregion
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,113 @@ |
|||||||
|
// 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.Collections.Generic; |
||||||
|
using System.Windows.Controls; |
||||||
|
|
||||||
|
using ILSpy.Debugger.Services; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Tooltips |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// ItemsControl wrapper that takes VirtualizingIEnumerable as source,
|
||||||
|
/// and adds additional items from the source to underlying ItemsControl when scrolled to bottom.
|
||||||
|
/// </summary>
|
||||||
|
public class LazyItemsControl<T> |
||||||
|
{ |
||||||
|
private ItemsControl itemsControl; |
||||||
|
private int initialItemsCount; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates new instance of LazyItemsControl.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="wrappedItemsControl">ItemsControl to wrap and add items to it when scrolled to bottom.</param>
|
||||||
|
/// <param name="initialItemsCount">Number of items to be initially displayed in wrapped ItemsControl.</param>
|
||||||
|
public LazyItemsControl(ItemsControl wrappedItemsControl, int initialItemsCount) |
||||||
|
{ |
||||||
|
if (wrappedItemsControl == null) |
||||||
|
throw new ArgumentNullException("wrappedItemsControl"); |
||||||
|
|
||||||
|
this.initialItemsCount = initialItemsCount; |
||||||
|
this.itemsControl = wrappedItemsControl; |
||||||
|
this.itemsControl.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(handleScroll)); |
||||||
|
} |
||||||
|
|
||||||
|
private ScrollViewer scrollViewerCached; |
||||||
|
public ScrollViewer ScrollViewer |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
if (this.scrollViewerCached == null) |
||||||
|
this.scrollViewerCached = this.itemsControl.GetScrollViewer(); |
||||||
|
return this.scrollViewerCached; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public bool IsScrolledToStart |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
if (ScrollViewer == null) // Visual tree not initialized yet
|
||||||
|
return false; |
||||||
|
return ScrollViewer.VerticalOffset == 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public bool IsScrolledToEnd |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
if (itemsSourceTotalCount == null) { |
||||||
|
// not scrolled to end of IEnumerable yet
|
||||||
|
return false; |
||||||
|
} |
||||||
|
// already scrolled to end of IEnumerable
|
||||||
|
int totalItems = itemsSourceTotalCount.Value; |
||||||
|
return (ScrollViewer.VerticalOffset >= totalItems - ScrollViewer.ViewportHeight); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private int? itemsSourceTotalCount = null; |
||||||
|
/// <summary> Items count of underlying IEnumerable. Null until scrolled to the end of IEnumerable. </summary>
|
||||||
|
public int? ItemsSourceTotalCount |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.itemsSourceTotalCount; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private VirtualizingIEnumerable<T> itemsSource; |
||||||
|
/// <summary> The collection that underlying ItemsControl sees. </summary>
|
||||||
|
public VirtualizingIEnumerable<T> ItemsSource |
||||||
|
{ |
||||||
|
get { return itemsSource; } |
||||||
|
set |
||||||
|
{ |
||||||
|
this.itemsSource = value; |
||||||
|
addNextItems(this.itemsSource, initialItemsCount); |
||||||
|
this.itemsControl.ItemsSource = value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void addNextItems(VirtualizingIEnumerable<T> sourceToAdd, int nItems) |
||||||
|
{ |
||||||
|
sourceToAdd.AddNextItems(nItems); |
||||||
|
if (!sourceToAdd.HasNext) { |
||||||
|
// all items from IEnumerable have been added
|
||||||
|
this.itemsSourceTotalCount = sourceToAdd.Count; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void handleScroll(object sender, ScrollChangedEventArgs e) |
||||||
|
{ |
||||||
|
if (e.VerticalChange > 0) { |
||||||
|
// scrolled to bottom
|
||||||
|
if (e.VerticalOffset >= this.itemsSource.Count - e.ViewportHeight) { |
||||||
|
addNextItems(this.itemsSource, (int)e.VerticalChange); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,362 @@ |
|||||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||||
|
xmlns:local="clr-namespace:ILSpy.Debugger.Tooltips" |
||||||
|
> |
||||||
|
<LinearGradientBrush x:Key="OrangeBrushKey" EndPoint="0,1" StartPoint="0,0"> |
||||||
|
<LinearGradientBrush.GradientStops> |
||||||
|
<GradientStop Offset="0" Color="White" /> |
||||||
|
<GradientStop Offset="0.5" Color="Orange" /> |
||||||
|
<GradientStop Offset="1" Color="Orange" /> |
||||||
|
</LinearGradientBrush.GradientStops> |
||||||
|
</LinearGradientBrush> |
||||||
|
|
||||||
|
<LinearGradientBrush x:Key="OrangePressedBrushKey" EndPoint="0,1" StartPoint="0,0"> |
||||||
|
<LinearGradientBrush.GradientStops> |
||||||
|
<GradientStop Offset="1" Color="White" /> |
||||||
|
<GradientStop Offset="0.5" Color="Orange" /> |
||||||
|
<GradientStop Offset="0" Color="Orange" /> |
||||||
|
</LinearGradientBrush.GradientStops> |
||||||
|
</LinearGradientBrush> |
||||||
|
|
||||||
|
<LinearGradientBrush x:Key="SilverBrushKey" EndPoint="0,1" StartPoint="0,0"> |
||||||
|
<LinearGradientBrush.GradientStops> |
||||||
|
<GradientStop Offset="0" Color="White" /> |
||||||
|
<GradientStop Offset="0.5" Color="LightGray" /> |
||||||
|
<GradientStop Offset="1" Color="LightGray" /> |
||||||
|
</LinearGradientBrush.GradientStops> |
||||||
|
</LinearGradientBrush> |
||||||
|
|
||||||
|
<LinearGradientBrush x:Key="SilverPressedBrushKey" EndPoint="0,1" StartPoint="0,0"> |
||||||
|
<LinearGradientBrush.GradientStops> |
||||||
|
<GradientStop Offset="1" Color="White" /> |
||||||
|
<GradientStop Offset="0.5" Color="LightGray" /> |
||||||
|
<GradientStop Offset="0" Color="LightGray" /> |
||||||
|
</LinearGradientBrush.GradientStops> |
||||||
|
</LinearGradientBrush> |
||||||
|
|
||||||
|
<ControlTemplate x:Key="CloseButtonTemplate" TargetType="Button"> |
||||||
|
<Border Width="16" Height="16" Name="TheBorder" CornerRadius="2,2,0,0" BorderThickness="1" BorderBrush="Black" Background="{StaticResource SilverPressedBrushKey}"> |
||||||
|
<Canvas> |
||||||
|
<Line X1="3.5" X2="10.5" Y1="3.5" Y2="10.5" Stroke="Black" StrokeThickness="2"/> |
||||||
|
<Line X1="3.5" X2="10.5" Y1="10.5" Y2="3.5" Stroke="Black" StrokeThickness="2"/> |
||||||
|
</Canvas> |
||||||
|
</Border> |
||||||
|
<ControlTemplate.Triggers> |
||||||
|
<Trigger Property="UIElement.IsMouseOver" Value="true"> |
||||||
|
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangeBrushKey}"/> |
||||||
|
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/> |
||||||
|
</Trigger> |
||||||
|
<Trigger Property="ButtonBase.IsPressed" Value="True"> |
||||||
|
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangePressedBrushKey}"/> |
||||||
|
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/> |
||||||
|
</Trigger> |
||||||
|
</ControlTemplate.Triggers> |
||||||
|
</ControlTemplate> |
||||||
|
|
||||||
|
<TransformGroup x:Key="Rotate"> |
||||||
|
<RotateTransform Angle="270" CenterX="7" CenterY="7"/> |
||||||
|
</TransformGroup> |
||||||
|
|
||||||
|
<TransformGroup x:Key="RotateUnpin"> |
||||||
|
<RotateTransform Angle="270" CenterX="7" CenterY="7"/> |
||||||
|
<RotateTransform Angle="-90" CenterX="7" CenterY="7"/> |
||||||
|
<ScaleTransform ScaleY="-1" CenterX="7" CenterY="7"/> |
||||||
|
</TransformGroup> |
||||||
|
|
||||||
|
<TransformGroup x:Key="RotatePin"> |
||||||
|
<RotateTransform Angle="-90" CenterX="7" CenterY="7"/> |
||||||
|
</TransformGroup> |
||||||
|
|
||||||
|
<TransformGroup x:Key="FlipComment"> |
||||||
|
<ScaleTransform CenterX="7" CenterY="7" ScaleY="-1"/> |
||||||
|
</TransformGroup> |
||||||
|
|
||||||
|
<ControlTemplate x:Key="PinButtonTemplate" TargetType="ToggleButton"> |
||||||
|
<Border Width="16" Height="16" Name="TheBorder" CornerRadius="0" BorderThickness="1" BorderBrush="Black" Background="{StaticResource SilverPressedBrushKey}"> |
||||||
|
<Canvas Name="TheCanvas"> |
||||||
|
<Line X1="4" X2="10" Y1="2" Y2="2" Stroke="Black" StrokeThickness="1"/> |
||||||
|
<Line X1="9" X2="9" Y1="2" Y2="8" Stroke="Black" StrokeThickness="1"/> |
||||||
|
<Line X1="2" X2="12" Y1="8" Y2="8" Stroke="Black" StrokeThickness="1"/> |
||||||
|
<Rectangle Fill="Black" Width="2" Height="5" Canvas.Left="4" Canvas.Top="3"/> |
||||||
|
<Line X1="7" X2="7" Y1="9" Y2="12" Stroke="Black" StrokeThickness="1"/> |
||||||
|
</Canvas> |
||||||
|
</Border> |
||||||
|
<ControlTemplate.Triggers> |
||||||
|
<Trigger Property="UIElement.IsMouseOver" Value="true"> |
||||||
|
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangeBrushKey}"/> |
||||||
|
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/> |
||||||
|
</Trigger> |
||||||
|
<Trigger Property="ButtonBase.IsPressed" Value="True"> |
||||||
|
<Setter TargetName="TheCanvas" Property="RenderTransform" Value="{StaticResource RotatePin}"/> |
||||||
|
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangePressedBrushKey}"/> |
||||||
|
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/> |
||||||
|
</Trigger> |
||||||
|
<Trigger Property="IsChecked" Value="true"> |
||||||
|
<Setter TargetName="TheCanvas" Property="RenderTransform" Value="{StaticResource RotatePin}"/> |
||||||
|
</Trigger> |
||||||
|
</ControlTemplate.Triggers> |
||||||
|
</ControlTemplate> |
||||||
|
|
||||||
|
<ControlTemplate x:Key="CommentButtonTemplate" TargetType="ToggleButton"> |
||||||
|
<Border Width="16" Height="16" Name="TheBorder" CornerRadius="0,0,2,2" BorderThickness="1" BorderBrush="Black" Background="{StaticResource SilverPressedBrushKey}"> |
||||||
|
<Canvas Name="TheCanvas"> |
||||||
|
<Line X1="3" Y1="3" X2="7" Y2="7.5" Stroke="Black" StrokeThickness="1"/> |
||||||
|
<Line X1="7" Y1="7.4" X2="11" Y2="3" Stroke="Black" StrokeThickness="1"/> |
||||||
|
<Line X1="3" Y1="7.5" X2="7" Y2="12" Stroke="Black" StrokeThickness="1"/> |
||||||
|
<Line X1="7" Y1="12" X2="11" Y2="7.5" Stroke="Black" StrokeThickness="1"/> |
||||||
|
</Canvas> |
||||||
|
</Border> |
||||||
|
<ControlTemplate.Triggers> |
||||||
|
<Trigger Property="UIElement.IsMouseOver" Value="true"> |
||||||
|
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangeBrushKey}"/> |
||||||
|
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/> |
||||||
|
</Trigger> |
||||||
|
<Trigger Property="ButtonBase.IsPressed" Value="True"> |
||||||
|
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangePressedBrushKey}"/> |
||||||
|
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/> |
||||||
|
</Trigger> |
||||||
|
<Trigger Property="IsChecked" Value="True"> |
||||||
|
<Setter TargetName="TheCanvas" Property="RenderTransform" Value="{StaticResource FlipComment}"/> |
||||||
|
</Trigger> |
||||||
|
</ControlTemplate.Triggers> |
||||||
|
</ControlTemplate> |
||||||
|
|
||||||
|
<SolidColorBrush x:Key="MouseOverPinBrush" Color="Black" /> |
||||||
|
|
||||||
|
<ControlTemplate x:Key="PinTooltipButtonTemplate" TargetType="ToggleButton"> |
||||||
|
<Border Width="16" Height="16" Name="TheBorder" CornerRadius="2" BorderBrush="Transparent" BorderThickness="1" Background="Transparent"> |
||||||
|
<Canvas RenderTransform="{StaticResource Rotate}" Name="TheCanvas"> |
||||||
|
<Line X1="4" X2="10" Y1="2" Y2="2" Stroke="Silver" StrokeThickness="1" Name="Line1"/> |
||||||
|
<Line X1="9" X2="9" Y1="2" Y2="8" Stroke="Silver" StrokeThickness="1" Name="Line2"/> |
||||||
|
<Line X1="2" X2="12" Y1="8" Y2="8" Stroke="Silver" StrokeThickness="1" Name="Line3"/> |
||||||
|
<Rectangle Fill="Silver" Width="2" Height="7" Canvas.Left="4" Canvas.Top="2" Name="Rectangle"/> |
||||||
|
<Line X1="7" X2="7" Y1="9" Y2="12" Stroke="Silver" StrokeThickness="1" Name="Line4"/> |
||||||
|
</Canvas> |
||||||
|
</Border> |
||||||
|
<ControlTemplate.Triggers> |
||||||
|
<Trigger Property="ButtonBase.IsPressed" Value="True"> |
||||||
|
<Setter TargetName="TheCanvas" Property="RenderTransform" Value="{StaticResource RotateUnpin}"/> |
||||||
|
</Trigger> |
||||||
|
<Trigger Property="ButtonBase.IsMouseOver" Value="True"> |
||||||
|
<Setter TargetName="Line1" Property="Stroke" Value="{StaticResource MouseOverPinBrush}"/> |
||||||
|
<Setter TargetName="Line2" Property="Stroke" Value="{StaticResource MouseOverPinBrush}"/> |
||||||
|
<Setter TargetName="Line3" Property="Stroke" Value="{StaticResource MouseOverPinBrush}"/> |
||||||
|
<Setter TargetName="Line4" Property="Stroke" Value="{StaticResource MouseOverPinBrush}"/> |
||||||
|
<Setter TargetName="Rectangle" Property="Fill" Value="{StaticResource MouseOverPinBrush}"/> |
||||||
|
</Trigger> |
||||||
|
<Trigger Property="IsChecked" Value="True"> |
||||||
|
<Setter TargetName="TheCanvas" Property="RenderTransform" Value="{StaticResource RotateUnpin}"/> |
||||||
|
</Trigger> |
||||||
|
</ControlTemplate.Triggers> |
||||||
|
</ControlTemplate> |
||||||
|
|
||||||
|
<Style |
||||||
|
TargetType="{x:Type TextBox}" |
||||||
|
x:Key="TextStyle"> |
||||||
|
<Setter |
||||||
|
Property="OverridesDefaultStyle" |
||||||
|
Value="True" /> |
||||||
|
<Setter |
||||||
|
Property="VerticalAlignment" |
||||||
|
Value="Center" /> |
||||||
|
<Setter |
||||||
|
Property="FontFamily" Value="Khmer UI" /> |
||||||
|
<Setter Property="FontSize" Value="12" /> |
||||||
|
<Setter |
||||||
|
Property="KeyboardNavigation.TabNavigation" |
||||||
|
Value="None" /> |
||||||
|
<Setter |
||||||
|
Property="FocusVisualStyle" |
||||||
|
Value="{x:Null}" /> |
||||||
|
<Setter |
||||||
|
Property="Template"> |
||||||
|
<Setter.Value> |
||||||
|
<ControlTemplate |
||||||
|
TargetType="{x:Type TextBoxBase}"> |
||||||
|
<Border |
||||||
|
Name="Border" |
||||||
|
Background="Transparent" |
||||||
|
BorderBrush="Transparent" |
||||||
|
BorderThickness="0"> |
||||||
|
<ScrollViewer |
||||||
|
Margin="0" |
||||||
|
Name="PART_ContentHost" /> |
||||||
|
</Border> |
||||||
|
</ControlTemplate> |
||||||
|
</Setter.Value> |
||||||
|
</Setter> |
||||||
|
</Style> |
||||||
|
|
||||||
|
<Style TargetType="TextBlock" x:Key="TextBlockStyle"> |
||||||
|
<Setter Property="Margin" Value="4 0" /> |
||||||
|
<Setter |
||||||
|
Property="FontFamily" Value="Khmer UI" /> |
||||||
|
<Setter Property="FontSize" Value="12" /> |
||||||
|
</Style> |
||||||
|
|
||||||
|
<Style x:Key="PinThumbStyle" TargetType="Thumb"> |
||||||
|
<Setter Property="Template"> |
||||||
|
<Setter.Value> |
||||||
|
<ControlTemplate TargetType="Thumb"> |
||||||
|
<StackPanel x:Name="Container"/> |
||||||
|
</ControlTemplate> |
||||||
|
</Setter.Value> |
||||||
|
</Setter> |
||||||
|
</Style> |
||||||
|
|
||||||
|
<Style |
||||||
|
x:Key="ExpandCollapseToggleStyle" |
||||||
|
TargetType="{x:Type ToggleButton}"> |
||||||
|
<Setter |
||||||
|
Property="Focusable" |
||||||
|
Value="False" /> |
||||||
|
<Setter |
||||||
|
Property="Width" |
||||||
|
Value="19" /> |
||||||
|
<Setter |
||||||
|
Property="Height" |
||||||
|
Value="13" /> |
||||||
|
<Setter |
||||||
|
Property="Template"> |
||||||
|
<Setter.Value> |
||||||
|
<ControlTemplate |
||||||
|
TargetType="{x:Type ToggleButton}"> |
||||||
|
<Border |
||||||
|
Width="19" |
||||||
|
Height="13" |
||||||
|
Background="Transparent"> |
||||||
|
<Border |
||||||
|
Width="9" |
||||||
|
Height="9" |
||||||
|
BorderThickness="1" |
||||||
|
BorderBrush="#FF7898B5" |
||||||
|
CornerRadius="1" |
||||||
|
SnapsToDevicePixels="true"> |
||||||
|
<Border.Background> |
||||||
|
<LinearGradientBrush |
||||||
|
StartPoint="0,0" |
||||||
|
EndPoint="1,1"> |
||||||
|
<LinearGradientBrush.GradientStops> |
||||||
|
<GradientStop |
||||||
|
Color="White" |
||||||
|
Offset=".2" /> |
||||||
|
<GradientStop |
||||||
|
Color="#FFC0B7A6" |
||||||
|
Offset="1" /> |
||||||
|
</LinearGradientBrush.GradientStops> |
||||||
|
</LinearGradientBrush> |
||||||
|
</Border.Background> |
||||||
|
<Path |
||||||
|
x:Name="ExpandPath" |
||||||
|
Margin="1,1,1,1" |
||||||
|
Fill="Black" |
||||||
|
Data="M 0 2 L 0 3 L 2 3 L 2 5 L 3 5 L 3 3 L 5 3 L 5 2 L 3 2 L 3 0 L 2 0 L 2 2 Z" /> |
||||||
|
</Border> |
||||||
|
</Border> |
||||||
|
<ControlTemplate.Triggers> |
||||||
|
<Trigger |
||||||
|
Property="IsChecked" |
||||||
|
Value="True"> |
||||||
|
<Setter |
||||||
|
Property="Data" |
||||||
|
TargetName="ExpandPath" |
||||||
|
Value="M 0 2 L 0 3 L 5 3 L 5 2 Z" /> |
||||||
|
</Trigger> |
||||||
|
</ControlTemplate.Triggers> |
||||||
|
</ControlTemplate> |
||||||
|
</Setter.Value> |
||||||
|
</Setter> |
||||||
|
</Style> |
||||||
|
<Style |
||||||
|
x:Key="upDownBorderStyle" |
||||||
|
TargetType="{x:Type Border}"> |
||||||
|
<Setter |
||||||
|
Property="BorderBrush" |
||||||
|
Value="Gray" /> |
||||||
|
<Setter |
||||||
|
Property="HorizontalAlignment" |
||||||
|
Value="Stretch" /> |
||||||
|
<Setter |
||||||
|
Property="Margin" |
||||||
|
Value="0" /> |
||||||
|
<Setter |
||||||
|
Property="Padding" |
||||||
|
Value="0" /> |
||||||
|
<Setter |
||||||
|
Property="Background" |
||||||
|
Value="#FFECF7FC" /> |
||||||
|
<Setter |
||||||
|
Property="Height" |
||||||
|
Value="14" /> |
||||||
|
<Style.Triggers> |
||||||
|
<DataTrigger |
||||||
|
Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled}" |
||||||
|
Value="False"> |
||||||
|
<Setter |
||||||
|
Property="Background" |
||||||
|
Value="#FFE0E0E0"></Setter> |
||||||
|
</DataTrigger> |
||||||
|
</Style.Triggers> |
||||||
|
</Style> |
||||||
|
<Style |
||||||
|
x:Key="upButtonStyle" |
||||||
|
TargetType="{x:Type RepeatButton}"> |
||||||
|
<Setter |
||||||
|
Property="Template"> |
||||||
|
<Setter.Value> |
||||||
|
<ControlTemplate |
||||||
|
TargetType="{x:Type RepeatButton}"> |
||||||
|
<Border |
||||||
|
Style="{StaticResource upDownBorderStyle}" |
||||||
|
BorderThickness="1 1 1 0"> |
||||||
|
<ContentPresenter |
||||||
|
HorizontalAlignment="Center"></ContentPresenter> |
||||||
|
</Border> |
||||||
|
</ControlTemplate> |
||||||
|
</Setter.Value> |
||||||
|
</Setter> |
||||||
|
</Style> |
||||||
|
<Style |
||||||
|
x:Key="downButtonStyle" |
||||||
|
TargetType="{x:Type RepeatButton}"> |
||||||
|
<Setter |
||||||
|
Property="Template"> |
||||||
|
<Setter.Value> |
||||||
|
<ControlTemplate |
||||||
|
TargetType="{x:Type RepeatButton}"> |
||||||
|
<Border |
||||||
|
Style="{StaticResource upDownBorderStyle}" |
||||||
|
BorderThickness="1 0 1 1"> |
||||||
|
<ContentPresenter |
||||||
|
HorizontalAlignment="Center"></ContentPresenter> |
||||||
|
</Border> |
||||||
|
</ControlTemplate> |
||||||
|
</Setter.Value> |
||||||
|
</Setter> |
||||||
|
</Style> |
||||||
|
|
||||||
|
<ControlTemplate x:Key="RefreshButton" TargetType="Button"> |
||||||
|
<Border |
||||||
|
Name="ImageBorder" |
||||||
|
CornerRadius="7" |
||||||
|
BorderBrush="Transparent" |
||||||
|
BorderThickness="1" |
||||||
|
Height="14" |
||||||
|
Width="14"> |
||||||
|
<Image Width="9" Height="9" Margin="2 2" |
||||||
|
x:Name="RefreshContentImage" |
||||||
|
Tag="{Binding}" |
||||||
|
Source="pack://application:,,,/ILSpy;component/Images/Refresh.png"/> |
||||||
|
</Border> |
||||||
|
<ControlTemplate.Triggers> |
||||||
|
<Trigger Property="UIElement.IsMouseOver" Value="true"> |
||||||
|
<Setter TargetName="ImageBorder" Property="Background" Value="{StaticResource SilverBrushKey}"/> |
||||||
|
<Setter TargetName="ImageBorder" Property="BorderBrush" Value="Gray"/> |
||||||
|
</Trigger> |
||||||
|
<Trigger Property="ButtonBase.IsPressed" Value="True"> |
||||||
|
<Setter TargetName="ImageBorder" Property="Background" Value="{StaticResource SilverPressedBrushKey}"/> |
||||||
|
<Setter TargetName="ImageBorder" Property="BorderBrush" Value="Gray"/> |
||||||
|
</Trigger> |
||||||
|
</ControlTemplate.Triggers> |
||||||
|
</ControlTemplate> |
||||||
|
</ResourceDictionary> |
@ -0,0 +1,53 @@ |
|||||||
|
// 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.Collections.Generic; |
||||||
|
using System.Collections.ObjectModel; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Tooltips |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// A wrapper around IEnumerable<T> with AddNextItems method for pulling additional items
|
||||||
|
/// from underlying IEnumerable<T>.
|
||||||
|
/// Can be used as source for <see cref="LazyItemsControl" />.
|
||||||
|
/// </summary>
|
||||||
|
public class VirtualizingIEnumerable<T> : ObservableCollection<T> |
||||||
|
{ |
||||||
|
private IEnumerator<T> originalSourceEnumerator; |
||||||
|
|
||||||
|
public VirtualizingIEnumerable(IEnumerable<T> originalSource) |
||||||
|
{ |
||||||
|
if (originalSource == null) |
||||||
|
throw new ArgumentNullException("originalSource"); |
||||||
|
|
||||||
|
this.originalSourceEnumerator = originalSource.GetEnumerator(); |
||||||
|
} |
||||||
|
|
||||||
|
private bool hasNext = true; |
||||||
|
/// <summary>
|
||||||
|
/// False if all items from underlying IEnumerable have already been added.
|
||||||
|
/// </summary>
|
||||||
|
public bool HasNext |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.hasNext; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Requests next <paramref name="count"/> items from underlying IEnumerable source and adds them to the collection.
|
||||||
|
/// </summary>
|
||||||
|
public void AddNextItems(int count) |
||||||
|
{ |
||||||
|
for (int i = 0; i < count; i++) { |
||||||
|
if (!originalSourceEnumerator.MoveNext()) { |
||||||
|
this.hasNext = false; |
||||||
|
break; |
||||||
|
} |
||||||
|
this.Add(originalSourceEnumerator.Current); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
// 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.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using System.Windows.Controls; |
||||||
|
|
||||||
|
using ILSpy.Debugger.Models.TreeModel; |
||||||
|
|
||||||
|
namespace Debugger.AddIn.Tooltips |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Control displaying and executing <see cref="IVisualizerCommand">visualizer commands</see>.
|
||||||
|
/// </summary>
|
||||||
|
public class VisualizerPicker : ComboBox |
||||||
|
{ |
||||||
|
public VisualizerPicker() |
||||||
|
{ |
||||||
|
this.SelectionChanged += VisualizerPicker_SelectionChanged; |
||||||
|
} |
||||||
|
|
||||||
|
void VisualizerPicker_SelectionChanged(object sender, SelectionChangedEventArgs e) |
||||||
|
{ |
||||||
|
if (this.SelectedItem == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
var clickedCommand = this.SelectedItem as IVisualizerCommand; |
||||||
|
if (clickedCommand == null) { |
||||||
|
throw new InvalidOperationException( |
||||||
|
string.Format("{0} clicked, only instances of {1} must be present in {2}.", |
||||||
|
this.SelectedItem.GetType().ToString(), typeof(IVisualizerCommand).Name, typeof(VisualizerPicker).Name)); |
||||||
|
} |
||||||
|
|
||||||
|
clickedCommand.Execute(); |
||||||
|
// Make no item selected, so that multiple selections of the same item always execute the command.
|
||||||
|
// This triggers VisualizerPicker_SelectionChanged again, which returns immediately.
|
||||||
|
this.SelectedIndex = -1; |
||||||
|
} |
||||||
|
|
||||||
|
public new IEnumerable<IVisualizerCommand> ItemsSource |
||||||
|
{ |
||||||
|
get { return (IEnumerable<IVisualizerCommand>)base.ItemsSource; } |
||||||
|
set { base.ItemsSource = value; } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,133 @@ |
|||||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||||
|
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero" |
||||||
|
xmlns:debugging="clr-namespace:ILSpy.Debugger.Tooltips" |
||||||
|
xmlns:core="http://icsharpcode.net/sharpdevelop/core"> |
||||||
|
<Style x:Key="ComboBoxFocusVisual"> |
||||||
|
<Setter Property="Control.Template"> |
||||||
|
<Setter.Value> |
||||||
|
<ControlTemplate> |
||||||
|
<Rectangle Stroke="Black" StrokeDashArray="1 2" StrokeThickness="1" Margin="4,4,21,4" SnapsToDevicePixels="true"/> |
||||||
|
</ControlTemplate> |
||||||
|
</Setter.Value> |
||||||
|
</Setter> |
||||||
|
</Style> |
||||||
|
<LinearGradientBrush x:Key="ButtonNormalBackground" EndPoint="0,1" StartPoint="0,0"> |
||||||
|
<GradientStop Color="#F3F3F3" Offset="0"/> |
||||||
|
<GradientStop Color="#EBEBEB" Offset="0.5"/> |
||||||
|
<GradientStop Color="#DDDDDD" Offset="0.5"/> |
||||||
|
<GradientStop Color="#CDCDCD" Offset="1"/> |
||||||
|
</LinearGradientBrush> |
||||||
|
<SolidColorBrush x:Key="ButtonNormalBorder" Color="#FF707070"/> |
||||||
|
<Geometry x:Key="DownArrowGeometry">M 0 0 L 2 3 L 4 0 Z</Geometry> |
||||||
|
<Style x:Key="ComboBoxReadonlyToggleButton" TargetType="{x:Type ToggleButton}"> |
||||||
|
<Setter Property="OverridesDefaultStyle" Value="true"/> |
||||||
|
<Setter Property="IsTabStop" Value="false"/> |
||||||
|
<Setter Property="Focusable" Value="false"/> |
||||||
|
<Setter Property="ClickMode" Value="Press"/> |
||||||
|
<Setter Property="Template"> |
||||||
|
<Setter.Value> |
||||||
|
<ControlTemplate TargetType="{x:Type ToggleButton}"> |
||||||
|
|
||||||
|
<!-- Button face - changed from original ButtonChrome to Border --> |
||||||
|
<Border Name="FaceBorder" Background="Transparent" CornerRadius="2" BorderBrush="Transparent" BorderThickness="1" Padding="1" > |
||||||
|
<Grid> |
||||||
|
<Grid.ColumnDefinitions> |
||||||
|
<ColumnDefinition Width="*"/> |
||||||
|
<ColumnDefinition Width="5"/> |
||||||
|
</Grid.ColumnDefinitions> |
||||||
|
<!-- |
||||||
|
<Rectangle Width="10" Height="10" Grid.Column="0" Fill="Black"></Rectangle> --> |
||||||
|
<!--<Image Grid.Column="0" Width="10" Height="10" Stretch="Fill" Source="{core:GetBitmap Icons.Magnifier}" />--> |
||||||
|
<Path Grid.Column="1" x:Name="Arrow" Fill="Black" HorizontalAlignment="Center" Margin="0" VerticalAlignment="Center" Data="{StaticResource DownArrowGeometry}"/> |
||||||
|
</Grid> |
||||||
|
</Border> |
||||||
|
<ControlTemplate.Triggers> |
||||||
|
<Trigger Property="IsChecked" Value="true"> |
||||||
|
<Setter TargetName="FaceBorder" Property="Background" Value="White"/> |
||||||
|
<Setter TargetName="FaceBorder" Property="BorderBrush" Value="#FFE0EDFF"/> |
||||||
|
</Trigger> |
||||||
|
<Trigger Property="IsMouseOver" Value="true"> |
||||||
|
<Setter TargetName="FaceBorder" Property="Background" Value="White"/> |
||||||
|
<Setter TargetName="FaceBorder" Property="BorderBrush" Value="#FFE0EDFF"/> |
||||||
|
<!-- #FFE0EDFF border --> |
||||||
|
<!-- #FFD9D9E9 fill --> |
||||||
|
</Trigger> |
||||||
|
</ControlTemplate.Triggers> |
||||||
|
</ControlTemplate> |
||||||
|
</Setter.Value> |
||||||
|
</Setter> |
||||||
|
</Style> |
||||||
|
<LinearGradientBrush x:Key="TextBoxBorder" EndPoint="0,20" StartPoint="0,0" MappingMode="Absolute"> |
||||||
|
<GradientStop Color="#ABADB3" Offset="0.05"/> |
||||||
|
<GradientStop Color="#E2E3EA" Offset="0.07"/> |
||||||
|
<GradientStop Color="#E3E9EF" Offset="1"/> |
||||||
|
</LinearGradientBrush> |
||||||
|
<!--<Style TargetType="{x:Type debugging:VisualizerPicker}"> |
||||||
|
<Setter Property="FocusVisualStyle" Value="{StaticResource ComboBoxFocusVisual}"/> |
||||||
|
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/> |
||||||
|
<Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/> |
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}"/> |
||||||
|
<Setter Property="BorderThickness" Value="1"/> |
||||||
|
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/> |
||||||
|
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/> |
||||||
|
<Setter Property="Padding" Value="4,3"/> |
||||||
|
<Setter Property="Template"> |
||||||
|
<Setter.Value> |
||||||
|
<ControlTemplate TargetType="{x:Type debugging:VisualizerPicker}"> |
||||||
|
|
||||||
|
ComboBox face |
||||||
|
<Grid x:Name="MainGrid" SnapsToDevicePixels="true" Width="18" Height="16"> |
||||||
|
<Grid.ColumnDefinitions> |
||||||
|
<ColumnDefinition Width="*"/> |
||||||
|
<ColumnDefinition Width="5"/> |
||||||
|
</Grid.ColumnDefinitions> |
||||||
|
<Popup x:Name="PART_Popup" Margin="1" AllowsTransparency="true" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" Placement="Bottom" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Grid.ColumnSpan="2"> |
||||||
|
<Microsoft_Windows_Themes:SystemDropShadowChrome x:Name="Shdw" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=MainGrid}" Color="Transparent"> |
||||||
|
<Border x:Name="DropDownBorder" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1"> |
||||||
|
<ScrollViewer CanContentScroll="true"> |
||||||
|
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.DirectionalNavigation="Contained"/> |
||||||
|
</ScrollViewer> |
||||||
|
</Border> |
||||||
|
</Microsoft_Windows_Themes:SystemDropShadowChrome> |
||||||
|
</Popup> |
||||||
|
<ToggleButton Style="{StaticResource ComboBoxReadonlyToggleButton}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"/> |
||||||
|
|
||||||
|
Content of ComboBox face |
||||||
|
<ContentPresenter |
||||||
|
HorizontalAlignment="Left" |
||||||
|
Margin="0" |
||||||
|
VerticalAlignment="Center" |
||||||
|
IsHitTestVisible="false" |
||||||
|
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" |
||||||
|
ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" |
||||||
|
Content=" " |
||||||
|
|
||||||
|
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" |
||||||
|
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"/> |
||||||
|
</Grid> |
||||||
|
<ControlTemplate.Triggers> |
||||||
|
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true"> |
||||||
|
<Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5"/> |
||||||
|
<Setter Property="Color" TargetName="Shdw" Value="#71000000"/> |
||||||
|
</Trigger> |
||||||
|
<Trigger Property="HasItems" Value="false"> |
||||||
|
<Setter Property="Height" TargetName="DropDownBorder" Value="95"/> |
||||||
|
</Trigger> |
||||||
|
<Trigger Property="IsEnabled" Value="false"> |
||||||
|
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/> |
||||||
|
<Setter Property="Background" Value="#FFF4F4F4"/> |
||||||
|
</Trigger> |
||||||
|
<Trigger Property="IsGrouping" Value="true"> |
||||||
|
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/> |
||||||
|
</Trigger> |
||||||
|
</ControlTemplate.Triggers> |
||||||
|
</ControlTemplate> |
||||||
|
</Setter.Value> |
||||||
|
</Setter> |
||||||
|
<Style.Triggers> |
||||||
|
<Trigger Property="IsEditable" Value="true"> |
||||||
|
</Trigger> |
||||||
|
</Style.Triggers> |
||||||
|
</Style>--> |
||||||
|
</ResourceDictionary> |
Loading…
Reference in new issue