내용 보기
작성자
관리자 (IP : 172.17.0.1)
날짜
2020-07-10 03:49
제목
[WPF] 특정 컨트롤 스크롤 고정 Panel
|
Scroll Freeze Panel in WPF using System;using System.Windows;using System.Windows.Controls;using System.Windows.Controls.Primitives;using System.Windows.Data;using System.Windows.Media;namespace Iimaginec.Lib.Controls.Panels{public enum ScrollBehaviours{Scrollable = 0,NonScrollable,AlwaysVisible}public class BooleanToAlwaysVisibleScrollBehaviour : IValueConverter{public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture){var b = (bool)value;return b ? ScrollBehaviours.AlwaysVisible : ScrollBehaviours.Scrollable;}public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture){return (ScrollBehaviours)value == ScrollBehaviours.AlwaysVisible;}}/// <summary>/// ScrollFreezePanel arranges elements similar to a StackPanel, but has special/// behaviour when attached to a ScrollViewer. To use this control, it must be/// nested in a ScrollViewer with CanContentScroll set to true.////// Children of this panel can request special scrolling behaviour via the/// ScrollBehaviour attached property:////// Scrollable - default behaviour////// NonScrollable - element will remain in place while other children scroll.////// AlwaysVisible - element will scroll within the visible area, but will remain/// in place once it hits the edge.////// NOTE: This control currently only supports horizontal scrolling and orientation./// </summary>public class ScrollFreezePanel : Panel, IScrollInfo{#region [ Constants ]//constant for amount of scrolling when using keyboard or mouse wheelprivate const double LineSize = 10;#endregion#region [ Private Fields ]private Point _offset;private Size _extent = new Size(0, 0);private Size _viewport = new Size(0, 0);#endregion#region [ Attached Properties ]/// <summary>/// Orientation/// </summary>public static readonly DependencyProperty OrientationProperty =DependencyProperty.RegisterAttached("Orientation",typeof(Orientation),typeof(ScrollFreezePanel),new FrameworkPropertyMetadata(Orientation.Horizontal));/// <summary>/// Gets the Orientation property/// </summary>public static Orientation GetOrientation(DependencyObject d){return (Orientation)d.GetValue(OrientationProperty);}/// <summary>/// Sets the Orientation property/// </summary>public static void SetOrientation(DependencyObject d, Orientation value){d.SetValue(OrientationProperty, value);}/// <summary>/// ScrollBehaviour/// </summary>public static readonly DependencyProperty ScrollBehaviourProperty =DependencyProperty.RegisterAttached("ScrollBehaviour",typeof(ScrollBehaviours),typeof(ScrollFreezePanel),new FrameworkPropertyMetadata(ScrollBehaviours.Scrollable));/// <summary>/// Gets the ScrollBehaviour property/// </summary>public static ScrollBehaviours GetScrollBehaviour(DependencyObject d){return (ScrollBehaviours)d.GetValue(ScrollBehaviourProperty);}/// <summary>/// Sets the ScrollBehaviour property/// </summary>public static void SetScrollBehaviour(DependencyObject d, ScrollBehaviours value){d.SetValue(ScrollBehaviourProperty, value);}#endregion#region [ Overriden Methods ]/// <summary>/// Positions child elements and determines a size for the panel/// </summary>/// <param name="arrangeSize">The final area within the parent that this element/// should use to arrange itself and its children.</param>/// <returns>The actual size used.</returns>protected override Size ArrangeOverride(Size arrangeSize){var finalRect = new Rect(arrangeSize);if (IsHorizontal)finalRect.X -= _offset.X;elsefinalRect.Y -= _offset.Y;double width = 0.0;double height = 0.0;double offSetLeftUsed = 0;double offSetTopUsed = 0;Rect[] elementsOffsetRight = ScrollFreezeCalculateInFront(Children);for (int i = 0; i < Children.Count; i++){UIElement element = Children[i];if (IsHorizontal)finalRect.X += width;elsefinalRect.Y += height;width = element.DesiredSize.Width;height = element.DesiredSize.Height;finalRect.Width = IsHorizontal ? width : Math.Max(arrangeSize.Width, element.DesiredSize.Width);finalRect.Height = IsHorizontal ? Math.Max(arrangeSize.Height, element.DesiredSize.Height) : height;switch (GetScrollBehaviour(element)){case ScrollBehaviours.NonScrollable:if (IsHorizontal ?(finalRect.X + width) >= (_viewport.Width - elementsOffsetRight[i + 1].X): (finalRect.Y + height) >= (_viewport.Height - elementsOffsetRight[i + 1].Y)){SetZIndex(element, 1);Rect tempVisible = finalRect;if (IsHorizontal)tempVisible.X = _viewport.Width - elementsOffsetRight[i].X;elsetempVisible.Y = _viewport.Height - elementsOffsetRight[i].Y;element.Arrange(tempVisible);}else{SetZIndex(element, 1);Rect temp = finalRect;if (IsHorizontal)temp.X += _offset.X;elsetemp.Y += _offset.Y;element.Arrange(temp);offSetLeftUsed += width;offSetTopUsed += height;}break;case ScrollBehaviours.AlwaysVisible:if (IsHorizontal ? finalRect.X <= offSetLeftUsed : finalRect.Y <= offSetTopUsed){SetZIndex(element, 1);Rect tempVisible = finalRect;if (IsHorizontal)tempVisible.X = offSetLeftUsed;elsetempVisible.Y = offSetTopUsed;element.Arrange(tempVisible);offSetLeftUsed += width;offSetTopUsed += height;}else if (IsHorizontal ?(finalRect.X + width) >= (_viewport.Width - elementsOffsetRight[i + 1].X): (finalRect.Y + height) >= (_viewport.Height - elementsOffsetRight[i + 1].Y)){SetZIndex(element, 1);Rect tempVisible = finalRect;if (IsHorizontal)tempVisible.X = _viewport.Width - elementsOffsetRight[i].X;elsetempVisible.Y = _viewport.Height - elementsOffsetRight[i].Y;element.Arrange(tempVisible);}else{SetZIndex(element, 0);element.Arrange(finalRect);}break;default:SetZIndex(element, 0);element.Arrange(finalRect);break;}}return arrangeSize;}/// <summary>/// Measures the size in layout required for child elements and determines/// a size for the panel/// </summary>/// <param name="availableSize">The available size that this element can give/// to child elements. Infinity can be specified as a value to indicate that the/// element will size to whatever content is available.</param>/// <returns>The size that this element determines it needs during layout, based on/// its calculations of child element sizes.</returns>protected override Size MeasureOverride(Size availableSize){var size = new Size();for (int i = 0; i < InternalChildren.Count; i++){UIElement element = InternalChildren[i];element.Measure(availableSize);Size desiredSize = element.DesiredSize;if (IsHorizontal){size.Width += desiredSize.Width;size.Height = Math.Max(size.Height, desiredSize.Height);}else{size.Width = Math.Max(size.Width, desiredSize.Width);size.Height += desiredSize.Height;}}UpdateScrollInfo(availableSize, size);return size;}#endregion#region [ Private Methods ]//calculate the horizontal offset for each element in a collection based on//the ScrollBehaviour attached propertyprivate static Rect[] ScrollFreezeCalculateInFront(UIElementCollection children){var elementsOffsetRight = new Rect[children.Count + 1];var calculated = new Rect();for (int i = children.Count - 1; i >= 0; i--){UIElement element = children[i];ScrollBehaviours behaviour = GetScrollBehaviour(element);if (behaviour == ScrollBehaviours.AlwaysVisible ||behaviour == ScrollBehaviours.NonScrollable){calculated.X += element.DesiredSize.Width;calculated.Y += element.DesiredSize.Height;}elementsOffsetRight[i] = calculated;}return elementsOffsetRight;}//update scroll info based on availble and actual sizesprivate void UpdateScrollInfo(Size available, Size actual){if (IsHorizontal ? available.Width < actual.Width : available.Height < actual.Height){_viewport = available;_extent = actual;if (ScrollOwner != null){ScrollOwner.InvalidateScrollInfo();}else{_offset = new Point();}}else{_viewport = actual;_extent = new Size(0, 0);_offset = new Point();}}#endregion#region [ IScrollInfo Properties ]/// <summary>/// Gets or sets a value that indicates whether scrolling on the horizontal/// axis is possible./// </summary>public bool CanHorizontallyScroll{get;set;}/// <summary>/// Gets or sets a value that indicates whether scrolling on the vertical/// axis is possible. This will always return false./// </summary>public bool CanVerticallyScroll{get;set;}/// <summary>/// Gets or sets a ScrollViewer element that controls scrolling behavior./// </summary>public ScrollViewer ScrollOwner{get;set;}/// <summary>/// Gets the horizontal offset of the scrolled content./// </summary>public double HorizontalOffset{get { return _offset.X; }}/// <summary>/// Gets the vertical offset of the scrolled content./// </summary>public double VerticalOffset{get { return _offset.Y; }}/// <summary>/// Gets the vertical size of the extent./// </summary>public double ExtentHeight{get { return _extent.Height; }}/// <summary>/// Gets the horizontal size of the extent./// </summary>public double ExtentWidth{get { return _extent.Width; }}/// <summary>/// Gets the vertical size of the viewport for this content./// </summary>public double ViewportHeight{get { return _viewport.Height; }}/// <summary>/// Gets the horizontal size of the viewport for this content./// </summary>public double ViewportWidth{get { return _viewport.Width; }}#endregion#region [ IScrollInfo Methods ]/// <summary>/// Sets the amount of horizontal offset./// </summary>/// <param name="offset">The degree to which content is horizontally offset from the/// containing viewport.</param>public void SetHorizontalOffset(double offset){if (offset < 0 || _viewport.Width >= _extent.Width){offset = 0;}else if (offset + _viewport.Width >= _extent.Width){offset = _extent.Width - _viewport.Width;}_offset.X = offset;if (ScrollOwner != null){ScrollOwner.InvalidateScrollInfo();}InvalidateArrange();}/// <summary>/// Scrolls left within content by one logical unit./// </summary>public void LineLeft(){SetHorizontalOffset(HorizontalOffset - LineSize);}/// <summary>/// Scrolls right within content by one logical unit./// </summary>public void LineRight(){SetHorizontalOffset(HorizontalOffset + LineSize);}/// <summary>/// Scrolls left within content after a user clicks the wheel button on a mouse./// </summary>public void MouseWheelLeft(){LineLeft();}/// <summary>/// Scrolls right within content after a user clicks the wheel button on a mouse./// </summary>public void MouseWheelRight(){LineRight();}/// <summary>/// Scrolls down within content after a user scrolls down the wheel on a mouse./// In Horizontal orientation will scroll content RIGHT/// </summary>public void MouseWheelDown(){LineDown();}/// <summary>/// Scrolls up within content after a user scrolls up the wheel on a mouse./// In Horizontal orientation will scroll content LEFT/// </summary>public void MouseWheelUp(){LineUp();}/// <summary>/// Scrolls down within content by one logical unit./// </summary>public void LineDown(){SetHorizontalOffset(HorizontalOffset + LineSize);}/// <summary>/// Scrolls up within content by one logical unit./// </summary>public void LineUp(){SetHorizontalOffset(HorizontalOffset - LineSize);}/// <summary>/// Scrolls down within content by one page./// </summary>public void PageDown(){SetHorizontalOffset(HorizontalOffset + LineSize * 10);}/// <summary>/// Scrolls left within content by one page./// NOT IMPLEMENTED/// </summary>public void PageLeft(){}/// <summary>/// Scrolls right within content by one page./// NOT IMPLEMENTED/// </summary>public void PageRight(){}/// <summary>/// Scrolls up within content by one page./// </summary>public void PageUp(){SetHorizontalOffset(HorizontalOffset - LineSize * 10);}/// <summary>/// Forces content to scroll until the coordinate space of a Visual object is visible./// NOT IMPLEMENTED/// </summary>/// <param name="visual">A Visual that becomes visible.</param>/// <param name="rectangle">A bounding rectangle that identifies the coordinate space to/// make visible.</param>/// <returns>A Rect that is visible.</returns>public Rect MakeVisible(Visual visual, Rect rectangle){return rectangle;//TODO:IMPLEMENT}/// <summary>/// Sets the amount of vertical offset./// </summary>/// <param name="offset">The degree to which content is vertically offset from the/// containing viewport.</param>public void SetVerticalOffset(double offset){if (offset < 0 || _viewport.Height >= _extent.Height){offset = 0;}else if (offset + _viewport.Height >= _extent.Height){offset = _extent.Height - _viewport.Height;}_offset.Y = offset;if (ScrollOwner != null){ScrollOwner.InvalidateScrollInfo();}InvalidateArrange();}#endregion#region [ Private Properties ]private bool IsHorizontal { get { return GetOrientation(this) == Orientation.Horizontal; } }#endregion}} |
출처1
https://iimaginec.wordpress.com/2010/06/19/scroll-freeze-panel-in-wpf/
출처2
