Click here to Skip to main content
15,891,529 members
Articles / Desktop Programming / WPF

Wrap Panel Virtualization

Rate me:
Please Sign up or sign in to vote.
4.95/5 (18 votes)
2 Jan 2012CPOL2 min read 53.5K   5.6K   41  
WrapPanel doesn't support virtualization. But we can improve the performance by simulating virtualization.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Animation;

namespace MefBasic.UserControls
{
    public partial class CollapsibleContainer
    {
        private const string LeftIcon = "LeftIcon";
        private const string RightIcon = "RightIcon";
        private const string UpIcon = "UpIcon";
        private const string DownIcon = "DownIcon";
        private const double Delay = 0.25;

        public static readonly DependencyProperty IsExpandedProperty =
            DependencyProperty.Register("IsExpanded", typeof (bool), typeof (CollapsibleContainer),
                                        new UIPropertyMetadata(true, OnPropertyChanged));

        private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = (CollapsibleContainer) d;
            control.DoPropertyChanged(e);
        }

        private void DoPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            switch (e.Property.Name)
            {
                case "IsExpanded":
                    RefreshView();
                    break;
                case "Child":
                    childContentControl.Content = Child;
                    break;
                case "Direction":
                    HideAllBorder();
                    RefreshView();
                    break;
            }
        }

        private void HideAllBorder()
        {
            btnLeft.Visibility = Visibility.Collapsed;
            btnTop.Visibility = Visibility.Collapsed;
            btnRight.Visibility = Visibility.Collapsed;
            btnBottom.Visibility = Visibility.Collapsed;
        }

        public static readonly DependencyProperty ChildProperty =
            DependencyProperty.Register("Child", typeof (object), typeof (CollapsibleContainer),
                                        new UIPropertyMetadata(null,OnPropertyChanged));

        private double _delay;
        private Point _initialMousePosition;
        private bool _isDragging;

        private bool _isLoaded;

        public CollapsibleContainer()
        {
            InitializeComponent();
            InitializeMyComponent();
            imgLeft.Content = FindResource(LeftIcon);
            imgTop.Content = FindResource(UpIcon);
            imgRight.Content = FindResource(RightIcon);
            imgBottom.Content = FindResource(DownIcon);
        }

        public bool IsAnimationOn { set; get; }
        public bool CanCollapse { get; set; }

        public bool IsExpanded
        {
            get { return (bool) GetValue(IsExpandedProperty); }
            set { SetValue(IsExpandedProperty, value); }
        }

        public object Child
        {
            get { return GetValue(ChildProperty); }
            set { SetValue(ChildProperty, value); }
        }

        public event EventHandler AnimationCompleted;


        private void InitializeMyComponent()
        {
            IsAnimationOn = true;
            SubscribeMouseDown();
            contentHolder.Loaded += HandleLoaded;
            contentHolder.SizeChanged += HandleContentHolderSizeChanged;
            _delay = Delay;
        }

        private void HandleContentHolderSizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (ContentSize==0)
                ContentSize = Math.Max(ContentSize, GetActualSize());
        }

        private void SubscribeMouseDown()
        {
            imgLeft.PreviewMouseDown += BtnShowHideMouseUp;
            imgTop.PreviewMouseDown += BtnShowHideMouseUp;
            imgRight.PreviewMouseDown += BtnShowHideMouseUp;
            imgBottom.PreviewMouseDown += BtnShowHideMouseUp;
        }

        private double GetActualSize()
        {
            switch (Direction)
            {
                case Direction.LeftToRight:
                case Direction.RightToLeft:
                    return contentHolder.ActualWidth;
                case Direction.TopToBottom:
                case Direction.BottomToTop:
                    return contentHolder.ActualHeight;
            }
            return 0;
        }

        private void HandleLoaded(object sender, RoutedEventArgs e)
        {
            if (_isLoaded) return;
            _isLoaded = true;
            ContentSize = GetActualSize();
            UpdateImage();
        }

        private void OnAnimationCompleted()
        {
            if (AnimationCompleted != null)
            {
                AnimationCompleted(this, new EventArgs());
            }
        }


        private void BtnShowHideMouseUp(object sender, MouseButtonEventArgs e)
        {
            IsExpanded = !IsExpanded;
            RefreshView();
            e.Handled = true;
        }


        public ICommand ExpandCollapseChangedCommand
        {
            get { return (ICommand)GetValue(ExpandCollapseChangedCommandProperty); }
            set { SetValue(ExpandCollapseChangedCommandProperty, value); }
        }

        public static readonly DependencyProperty ExpandCollapseChangedCommandProperty =
            DependencyProperty.Register("ExpandCollapseChangedCommand", typeof(ICommand), typeof(CollapsibleContainer), new UIPropertyMetadata(null));



        public object ExpandCollapseChangedCommandParameter
        {
            get { return (object)GetValue(ExpandCollapseChangedCommandParameterProperty); }
            set { SetValue(ExpandCollapseChangedCommandParameterProperty, value); }
        }

        public static readonly DependencyProperty ExpandCollapseChangedCommandParameterProperty =
            DependencyProperty.Register("ExpandCollapseChangedCommandParameter", typeof(object), typeof(CollapsibleContainer), new UIPropertyMetadata(null));


        private void RefreshView()
        {
            UpdateImage();
            if (IsExpanded)
                Expand();
            else
                Collapse();
        }


        public void SetSizable(bool isSizable)
        {
            if (isSizable)
            {
                SubscribeEventsForSizable(btnLeft);
                SubscribeEventsForSizable(btnTop);
                SubscribeEventsForSizable(btnRight);
                SubscribeEventsForSizable(btnBottom);
            }
            else
            {
                UnsubscribeEventsForSizable(btnLeft);
                UnsubscribeEventsForSizable(btnTop);
                UnsubscribeEventsForSizable(btnRight);
                UnsubscribeEventsForSizable(btnBottom);
            }
        }

        private void UnsubscribeEventsForSizable(Border border)
        {
            border.MouseMove -= OnBorderMouseMove;
            border.MouseLeftButtonDown -= OnBorderMouseLeftButtonDown;
            border.MouseLeftButtonUp -= OnBorderMouseLeftButtonUp;
            border.Cursor = Cursors.Arrow;
        }

        private void SubscribeEventsForSizable(Border border)
        {
            border.MouseMove += OnBorderMouseMove;
            border.MouseLeftButtonDown += OnBorderMouseLeftButtonDown;
            border.MouseLeftButtonUp += OnBorderMouseLeftButtonUp;
            border.Cursor = Cursors.SizeWE;
        }


        public void Collapse()
        {
            if (!_isLoaded) return;
            if (IsAnimationOn)
            {
                Animate(ContentSize, 0);
            }
            else
            {
                contentHolder.Visibility = Visibility.Collapsed;
            }
        }

        public void Expand()
        {
            if (!_isLoaded) return;
            if (IsAnimationOn)
            {
                Animate(0, ContentSize);
            }
            else
            {
                contentHolder.Visibility = Visibility.Visible;
            }
        }



        public Direction Direction
        {
            get { return (Direction)GetValue(DirectionProperty); }
            set { SetValue(DirectionProperty, value); }
        }

        public double ContentSize { get; set; }

        public static readonly DependencyProperty DirectionProperty =
            DependencyProperty.Register("Direction", typeof(Direction), typeof(CollapsibleContainer), new UIPropertyMetadata(Direction.LeftToRight,OnPropertyChanged));

        public void Animate(double from, double to)
        {
            var dbAnimation = new DoubleAnimation();
            dbAnimation.Completed += DoubleAnimationCompleted;
            dbAnimation.From = from;
            dbAnimation.To = to;
            dbAnimation.Duration = new Duration(TimeSpan.FromSeconds(_delay));
            switch (Direction)
            {
                case Direction.LeftToRight:
                case Direction.RightToLeft:
                    contentContainer.BeginAnimation(WidthProperty, dbAnimation);    
                    break;
                case Direction.TopToBottom:
                case Direction.BottomToTop:
                    contentContainer.BeginAnimation(HeightProperty, dbAnimation);
                    break;
            }
            
        }

        private void DoubleAnimationCompleted(object sender, EventArgs e)
        {
            UpdateImage();
            _delay = Delay;
            if (!_isDragging)
            {
                OnAnimationCompleted();
            }
            if (ExpandCollapseChangedCommand != null)
            {
                ExpandCollapseChangedCommand.Execute(ExpandCollapseChangedCommandParameter);
            }
        }


        private void UpdateImage()
        {
            switch (Direction)
            {
                case Direction.LeftToRight:
                    btnLeft.Visibility = Visibility.Visible;
                    imgLeft.Content = FindResource(IsExpanded ? RightIcon : LeftIcon);
                    break;
                case Direction.RightToLeft:
                    btnRight.Visibility = Visibility.Visible;
                    imgRight.Content = FindResource(IsExpanded ? LeftIcon : RightIcon);
                    break;
                case Direction.TopToBottom:
                    btnTop.Visibility = Visibility.Visible;
                    imgTop.Content = FindResource(IsExpanded ? DownIcon : UpIcon);
                    break;
                case Direction.BottomToTop:
                    btnBottom.Visibility = Visibility.Visible;
                    imgBottom.Content = FindResource(IsExpanded ? UpIcon : DownIcon);
                    break;
            }
        }


        private void OnBorderMouseMove(object sender, MouseEventArgs e)
        {
            if (!IsExpanded) return;
            if (e.LeftButton != MouseButtonState.Pressed) return;

            var currentPosition = e.GetPosition(this);
            var deltaX = currentPosition.X - _initialMousePosition.X;

            var absDelta = Math.Abs(deltaX);
            if (absDelta < SystemParameters.MinimumHorizontalDragDistance) return;
            if (!_isDragging)
            {
                _isDragging = true;
            }
            _initialMousePosition = currentPosition;
            var currentWidth = contentHolder.ActualWidth;
            var newWidth = contentHolder.ActualWidth;
            if (IsWidthIncreasing(deltaX))
            {
                if ((newWidth + absDelta) <= (Application.Current.MainWindow.ActualWidth/2))
                    newWidth += absDelta;
            }
            else
            {
                if (newWidth - absDelta >= contentHolder.MinWidth)
                    newWidth -= absDelta;
            }
            _delay = 0;
            Animate(currentWidth, newWidth);
        }

        private bool IsWidthIncreasing(double deltaX)
        {
            if (Direction == Direction.RightToLeft)
            {
                return deltaX > 0;
            }
            return deltaX < 0;
        }

        private void OnBorderMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var border = (Border) sender;
            Mouse.Capture(border);
            _initialMousePosition = e.GetPosition(this);
        }

        private void OnBorderMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            _isDragging = false;
            Mouse.Capture(null);
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) KAZ Software Limited
Bangladesh Bangladesh
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions