Sic.Framework-Nanjing-Baishi/MECF.Framework.UI.Client/DataGridTransform/ExtendedGrid/Classes/ScrollingPreviewService.cs

372 lines
21 KiB
C#

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Windows.Threading;
namespace ExtendedGrid.Classes
{
/// <summary>
/// Displays a ToolTip next to the ScrollBar thumb while it is being dragged.
/// </summary>
public static class ScrollingPreviewService
{
public static DataTemplate GetVerticalScrollingPreviewTemplate(DependencyObject obj)
{
return (DataTemplate) obj.GetValue(VerticalScrollingPreviewTemplateProperty);
}
public static void SetVerticalScrollingPreviewTemplate(DependencyObject obj, DataTemplate value)
{
obj.SetValue(VerticalScrollingPreviewTemplateProperty, value);
}
/// <summary>
/// Allows for specifying a ContentTemplate for a ToolTip that will appear next to the
/// vertical ScrollBar while dragging the thumb.
/// </summary>
public static readonly DependencyProperty VerticalScrollingPreviewTemplateProperty =
DependencyProperty.RegisterAttached("VerticalScrollingPreviewTemplate", typeof (DataTemplate),
typeof (ScrollingPreviewService),
new FrameworkPropertyMetadata(null,
OnVerticalScrollingPreviewTemplateChanged));
private static void OnVerticalScrollingPreviewTemplateChanged(DependencyObject obj,
DependencyPropertyChangedEventArgs e)
{
if ((e.OldValue == null) && (e.NewValue != null))
{
PostAttachToEvents(obj, (DataTemplate) e.NewValue, true);
}
else
{
throw new NotSupportedException("Cannot change the template once it has been set.");
}
}
public static void OnVerticalScrollingPreviewTemplateChanged(ScrollViewer obj,
DataTemplate datatemplate)
{
obj.Dispatcher.BeginInvoke((NoParamCallback)(() => AttachToEvents(obj, datatemplate, true)),
DispatcherPriority.Loaded);
}
public static DataTemplate GetHorizontalScrollingPreviewTemplate(DependencyObject obj)
{
return (DataTemplate) obj.GetValue(HorizontalScrollingPreviewTemplateProperty);
}
public static void SetHorizontalScrollingPreviewTemplate(DependencyObject obj, DataTemplate value)
{
obj.SetValue(HorizontalScrollingPreviewTemplateProperty, value);
}
/// <summary>
/// Allows for specifying a ContentTemplate for a ToolTip that will appear next to the
/// horizontal ScrollBar while dragging the thumb.
/// </summary>
public static readonly DependencyProperty HorizontalScrollingPreviewTemplateProperty =
DependencyProperty.RegisterAttached("HorizontalScrollingPreviewTemplate", typeof (DataTemplate),
typeof (ScrollingPreviewService),
new FrameworkPropertyMetadata(null,
OnHorizontalScrollingPreviewTemplateChanged));
private static void OnHorizontalScrollingPreviewTemplateChanged(DependencyObject obj,
DependencyPropertyChangedEventArgs e)
{
if ((e.OldValue == null) && (e.NewValue != null))
{
PostAttachToEvents(obj, (DataTemplate) e.NewValue, false);
}
}
#region Event Handling
private static void PostAttachToEvents(DependencyObject obj, DataTemplate dataTemplate, bool vertical)
{
// Most likely, the control hasn't expanded its template, wait until
// Loaded priority is reached before looking for elements.
obj.Dispatcher.BeginInvoke((NoParamCallback) (() => AttachToEvents(obj, dataTemplate, vertical)),
DispatcherPriority.Loaded);
}
private delegate void NoParamCallback();
private const double Epsilon = 0;
private static void AttachToEvents(DependencyObject obj, DataTemplate dataTemplate, bool vertical)
{
DependencyObject source = obj;
var scrollViewer = FindElementOfType<ScrollViewer>(obj as FrameworkElement);
if (scrollViewer != null)
{
string scrollBarPartName = vertical ? "PART_VerticalScrollBar" : "PART_HorizontalScrollBar";
var scrollBar = FindName<ScrollBar>(scrollBarPartName, scrollViewer);
if (scrollBar != null)
{
var track = FindName<Track>("PART_Track", scrollBar);
if (track != null)
{
Thumb thumb = track.Thumb;
if (thumb != null)
{
// At this point, all of the control parts have been found.
// Attach to DragStarted to open the tooltip
thumb.DragStarted += delegate
{
var parentGrid = FindControls.FindParent<ExtendedGridControl.ExtendedDataGrid>(source);
if(parentGrid!=null)
parentGrid.IsVerticalScrolling = true;
if(dataTemplate==null)
return;
ScrollingPreviewData data;
if (_previewToolTip == null)
{
// Create the ToolTip if this is the first time it's been used.
_previewToolTip = new ToolTip();
data = new ScrollingPreviewData();
_previewToolTip.Content = data;
}
else
{
data = _previewToolTip.Content as ScrollingPreviewData;
}
// Update the content in the ToolTip
if (data != null)
{
data.UpdateScrollingValues(scrollBar);
if (source is ScrollViewer)
{
parentGrid = FindControls.FindParent<ExtendedGridControl.ExtendedDataGrid>(source);
if (parentGrid != null)
{
data.UpdateItem(parentGrid, vertical);
}
}
else
{
data.UpdateItem(source as ItemsControl, vertical);
}
}
// Set the Placement and the PlacementTarget
_previewToolTip.PlacementTarget = thumb;
_previewToolTip.Placement = vertical
? PlacementMode.Left
: PlacementMode.Top;
_previewToolTip.VerticalOffset = 0.0;
_previewToolTip.HorizontalOffset = 0.0;
_previewToolTip.ContentTemplate = dataTemplate;
_previewToolTip.IsOpen = true;
};
// Attach to DragDelta to update the ToolTip's position
thumb.DragDelta += delegate
{
if (dataTemplate == null)
return;
if ((_previewToolTip != null) &&
// Check that we're within the range of the ScrollBar
(scrollBar.Value > scrollBar.Minimum) &&
(scrollBar.Value < scrollBar.Maximum))
{
// This is a little trick to cause the ToolTip to update its position next to the Thumb
if (vertical)
{
_previewToolTip.VerticalOffset =
Math.Abs(_previewToolTip.VerticalOffset - 0.0) < Epsilon ? 0.001 : 0.0;
}
else
{
_previewToolTip.HorizontalOffset =
Math.Abs(_previewToolTip.HorizontalOffset - 0.0) < Epsilon ? 0.001 : 0.0;
}
}
};
// Attach to DragCompleted to close the ToolTip
thumb.DragCompleted += delegate
{
var parentGrid = FindControls.FindParent<ExtendedGridControl.ExtendedDataGrid>(source);
if (parentGrid != null)
parentGrid.IsVerticalScrolling = false;
if (dataTemplate == null)
return;
if (_previewToolTip != null)
{
_previewToolTip.IsOpen = false;
}
};
// Attach to the Scroll event to update the ToolTip content
scrollBar.Scroll += delegate
{
if (dataTemplate == null)
return;
if (_previewToolTip != null)
{
// The ScrollBar's value isn't updated quite yet, so
// wait until Input priority
scrollBar.Dispatcher.BeginInvoke((NoParamCallback) delegate
{
var
data
=
(
ScrollingPreviewData
)
_previewToolTip
.
Content;
data
.
UpdateScrollingValues
(scrollBar);
data
.
UpdateItem
(source
as
ItemsControl,
vertical);
},
DispatcherPriority.Input);
}
};
return;
}
}
}
// At this point, something wasn't found. If the computed visibility is not visible,
// then add a handler to wait for it to become visible.
if ((vertical
? scrollViewer.ComputedVerticalScrollBarVisibility
: scrollViewer.ComputedHorizontalScrollBarVisibility) != Visibility.Visible)
{
DependencyPropertyDescriptor propertyDescriptor =
DependencyPropertyDescriptor.FromProperty(
vertical
? ScrollViewer.ComputedVerticalScrollBarVisibilityProperty
: ScrollViewer.ComputedHorizontalScrollBarVisibilityProperty, typeof (ScrollViewer));
if (propertyDescriptor != null)
{
EventHandler handler = delegate
{
if (dataTemplate == null)
return;
if ((vertical
? scrollViewer.ComputedVerticalScrollBarVisibility
: scrollViewer.ComputedHorizontalScrollBarVisibility) ==
Visibility.Visible)
{
EventHandler storedHandler =
GetWaitForVisibleScrollBar(source);
propertyDescriptor.RemoveValueChanged(scrollViewer,
storedHandler);
PostAttachToEvents(obj, dataTemplate, vertical);
}
};
SetWaitForVisibleScrollBar(source, handler);
propertyDescriptor.AddValueChanged(scrollViewer, handler);
}
}
}
}
private static EventHandler GetWaitForVisibleScrollBar(DependencyObject obj)
{
return (EventHandler) obj.GetValue(WaitForVisibleScrollBarProperty);
}
private static void SetWaitForVisibleScrollBar(DependencyObject obj, EventHandler value)
{
obj.SetValue(WaitForVisibleScrollBarProperty, value);
}
/// <summary>
/// Storage for the property change handler if waiting for a ScrollBar to appear
/// for the first time.
/// </summary>
private static readonly DependencyProperty WaitForVisibleScrollBarProperty =
DependencyProperty.RegisterAttached("WaitForVisibleScrollBar", typeof (EventHandler),
typeof (ScrollingPreviewService), new UIPropertyMetadata(null));
// Keep one instance of a ToolTip and re-use it
[ThreadStatic] private static ToolTip _previewToolTip;
#endregion
#region Element Search Helpers
/// <summary>
/// Returns the template element of the given name within the Control.
/// </summary>
private static T FindName<T>(string name, Control control) where T : FrameworkElement
{
ControlTemplate template = control.Template;
if (template != null)
{
return template.FindName(name, control) as T;
}
return null;
}
/// <summary>
/// Searches the subtree of an element (including that element)
/// for an element of a particluar type.
/// </summary>
private static T FindElementOfType<T>(FrameworkElement element) where T : FrameworkElement
{
var correctlyTyped = element as T;
if (correctlyTyped != null)
{
return correctlyTyped;
}
if (element != null)
{
int numChildren = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < numChildren; i++)
{
var child = FindElementOfType<T>(VisualTreeHelper.GetChild(element, i) as FrameworkElement);
if (child != null)
{
return child;
}
}
// Popups continue in another window, jump to that tree
var popup = element as Popup;
if (popup != null)
{
return FindElementOfType<T>(popup.Child as FrameworkElement);
}
}
return null;
}
#endregion
}
}