h1

InertiaListBox – ListBox com inercia!!!

Março 13, 2009

Com o InertiaScrollViewer que podem encontrar mais abaixo tinhamos um problema ao usa-lo numa ListBox devido aos eventos do Selector, para isso resolvi criar esta classe que nos permite ter o mesmo comportamento mas agora com uma ListBox.

Portanto, cá esta o código do .cs

  [TemplatePart(Name = "PART_Scroll", Type = typeof(InertiaScrollViewer))]
    public class InertiaListBox : ListBox
    {

        #region Declara-se que
        private SelectionChangedEventArgs _selectionChangedEventArgs;
        private Point _offset;
        private Point _mouseDown;
        List<object> _oldSelecteds;
        #endregion

        static InertiaListBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(InertiaListBox), new FrameworkPropertyMetadata(typeof(InertiaListBox)));
        }

        public InertiaListBox()
        {
            _offset = new Point(0.0, 0.0);
            _selectionChangedEventArgs = null;
            _oldSelecteds = new List<object>();
            this.PreviewMouseDown += new MouseButtonEventHandler(InertiaListBox_PreviewMouseDown);
            Style _styleItems = new Style(typeof(ListBoxItem));
            TemplateBindingExtension b=new TemplateBindingExtension(InertiaListBox.IsSelectedProperty);
            _styleItems.Setters.Add(new Setter(Selector.IsSelectedProperty,b));
        }

        #region Eventos

        void InertiaListBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            _offset = new Point((this.Template.FindName("PART_Scroll", this) as InertiaScrollViewer).HorizontalOffset,
               (this.Template.FindName("PART_Scroll", this) as InertiaScrollViewer).VerticalOffset);
            _mouseDown = new Point(Mouse.GetPosition(this).X + _offset.X,
                Mouse.GetPosition(this).Y + _offset.Y);
        }

        #endregion

        #region Auxiliar
        private void SetIsSelected(SelectionChangedEventArgs e)
        {

                foreach (var item in _oldSelecteds)
                {
                    InertiaListBox.SetIsSelected(this.ItemContainerGenerator.ContainerFromItem(item), false);
                }
                _oldSelecteds = new List<object>();
                if (e != null)
                {
                    foreach (var item in e.AddedItems)
                    {
                        _oldSelecteds.Add(item);
                        InertiaListBox.SetIsSelected(this.ItemContainerGenerator.ContainerFromItem(item), true);
                    }
                }
        }
        #endregion

        #region Overrides

        protected override void OnPreviewMouseUp(MouseButtonEventArgs e)
        {
            Point _mouseUp = new Point(Mouse.GetPosition(this).X + _offset.X,
               Mouse.GetPosition(this).Y + _offset.Y);
            if (_selectionChangedEventArgs != null)
            {
                switch (Orientation)
                {
                    case Orientation.Horizontal:
                        if (Math.Abs(_mouseUp.X - _mouseDown.X) < 2)
                        {
                            SetIsSelected(_selectionChangedEventArgs);
                            base.OnSelectionChanged(_selectionChangedEventArgs);
                            _selectionChangedEventArgs = null;
                        }
                        break;
                    case Orientation.Vertical:
                        if (Math.Abs(_mouseUp.Y - _mouseDown.Y) < 2)
                        {
                            SetIsSelected(_selectionChangedEventArgs);
                            base.OnSelectionChanged(_selectionChangedEventArgs);
                            _selectionChangedEventArgs = null;
                        }
                        break;
                    default:
                        break;
                }
            }

            base.OnMouseUp(e);
        }

        protected override void OnSelectionChanged(SelectionChangedEventArgs e)
        {
            _selectionChangedEventArgs = e;
        }

        #endregion

        #region Properties
        public static new bool GetIsSelected(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsSelectedProperty);
        }

        public static new void SetIsSelected(DependencyObject obj, bool value)
        {
            obj.SetValue(IsSelectedProperty, value);
        }

        // Using a DependencyProperty as the backing store for IsSelected.  This enables animation, styling, binding, etc...
        public static new readonly DependencyProperty IsSelectedProperty =
            DependencyProperty.RegisterAttached("IsSelected", typeof(bool), typeof(InertiaListBox), new UIPropertyMetadata(false));

        public Orientation Orientation
        {
            get { return (Orientation)GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Orientation.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty OrientationProperty =
            DependencyProperty.Register("Orientation", typeof(Orientation), typeof(InertiaListBox), new UIPropertyMetadata(Orientation.Vertical));
        #endregion

    }

O template onde usamos o InertiaSrollviewer

<Style TargetType="{x:Type local:InertiaListBox}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:InertiaListBox}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <local:InertiaScrollViewer Focusable="False" x:Name="PART_Scroll">
                           <StackPanel Orientation="{TemplateBinding Orientation}" IsItemsHost="True" />
                        </local:InertiaScrollViewer>
                    </Border>
                </ControlTemplate
            </Setter.Value>
        </Setter>
    </Style>

A partir de agora e sempre que quisermos usa-la, basta definir o ItemTemplate ou o Style para ListBoxItem, por exemplo

<local:InertiaListBox  Orientation="Vertical">
            <local:InertiaListBox.Resources>
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                <TextBlock x:Name="mov" Text="{Binding .}"/>
                                <ControlTemplate.Triggers>
                                    <Trigger Property="local:InertiaListBox.IsSelected" Value="True">
                                        <Setter Property="Background" TargetName="mov" Value="Blue"/>
                                        <Setter Property="Foreground" TargetName="mov" Value="White"/>
                                    </Trigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                    <Style.Triggers>
                    </Style.Triggers>
                </Style>
            </local:InertiaListBox.Resources>
        </local:InertiaListBox>

Para já é tudo, confiram os updates ao InertiaScrollViewer :)
Até breve,
Miguel Duarte

Deixe um Comentário