FadeListView

Na Windows Phone som potreboval zväčšiť ListView, aby sa tam zmestilo aspoň o jednu položku viac. Tak ma napadlo, že by som spravil vrchná časť ListView by bola polopriehľadná a spravil by som plynulý prechod medzi ListView a kontrolkou nad tým. Avšak WinRT nepodporuje vlastnosť OpacityMask.

Tak som dostal nápad, že čo tak spraviť ListView, v ktorom každá položka je viac priehľadná, čim je viac pri vrchu. Až sa samozrejme položka stratí úplne. Toto samozrejme musí fungovať, aj keď užívateľ scrolluje v ListView. A tu je výsledný kód.

public class FadeListView : ListView

{

    #region Fields

 

    private ScrollViewer scrollViewer;

    private bool itemsPanelRegistered;

    private double? originalItemsPanelTopMargin;

 

    #endregion

 

    #region Constructor

 

    public FadeListView()

    {

        this.Loaded += this.OnLoaded;

    }

 

    #endregion

 

    #region Properties

 

    public static readonly DependencyProperty fadeSizeProperty = DependencyProperty.Register("FadeSize", typeof(double), typeof(FadeListView),

        new PropertyMetadata(0.0, new PropertyChangedCallback(OnFadeSizeChanged)));

 

    public double FadeSize

    {

        get { return (double)this.GetValue(fadeSizeProperty); }

        set { this.SetValue(fadeSizeProperty, value); }

    }

 

    #endregion

 

    #region Protected methods

 

    protected override void OnApplyTemplate()

    {

        base.OnApplyTemplate();

 

        if (this.scrollViewer != null)

        {

            this.scrollViewer.ViewChanged -= this.ScrollViewerOnViewChanged;

        }

 

        this.scrollViewer = this.GetTemplateChild("ScrollViewer") as ScrollViewer;

 

        if (this.scrollViewer != null)

        {

            this.scrollViewer.ViewChanged += this.ScrollViewerOnViewChanged;

        }

    }

    #endregion

 

    #region Private methods

 

    private static void OnFadeSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)

    {

        ((FadeListView)d).OnFadeSizeChanged(e);

    }

 

    private void OnFadeSizeChanged(DependencyPropertyChangedEventArgs e)

    {

        var fadeSize = (double)e.NewValue;

        this.SetItemsPanelMargin(fadeSize);

        this.UpdateContainersOpacity(fadeSize);

    }

 

    private void OnLoaded(object sender, RoutedEventArgs e)

    {

        if (!this.itemsPanelRegistered && this.ItemsPanelRoot != null)

        {

            this.ItemsPanelRoot.LayoutUpdated += this.ItemsPanelRootOnLayoutUpdated;

            this.itemsPanelRegistered = true;

        }

 

        this.SetItemsPanelMargin(this.FadeSize);

        this.UpdateContainersOpacity();

    }

 

    private void ScrollViewerOnViewChanged(object sender, ScrollViewerViewChangedEventArgs e)

    {

        this.UpdateContainersOpacity();

    }

 

    private void ItemsPanelRootOnLayoutUpdated(object sender, object e)

    {

        this.UpdateContainersOpacity();

    }

 

    private void SetItemsPanelMargin(double fadeSize)

    {

        var panel = this.ItemsPanelRoot;

        if (panel != null)

        {

            var margin = panel.Margin;

            if (fadeSize > 0)

            {

                if (!this.originalItemsPanelTopMargin.HasValue)

                {

                    this.originalItemsPanelTopMargin = margin.Top;

                }

                margin.Top = this.originalItemsPanelTopMargin.Value + (fadeSize / 2);

            }

            else

            {

                margin.Top = this.originalItemsPanelTopMargin.GetValueOrDefault();

                this.originalItemsPanelTopMargin = null;

            }

 

            panel.Margin = margin;

        }

    }

 

    private void UpdateContainersOpacity()

    {

        if (this.FadeSize > 0)

        {

            this.UpdateContainersOpacity(this.FadeSize);

        }

    }

 

    private void UpdateContainersOpacity(double fadeSize)

    {

        bool opaque = fadeSize <= 0;

        var panel = this.ItemsPanelRoot;

        if (panel != null)

        {

            foreach (var container in panel.Children)

            {

                if (opaque)

                {

                    container.Opacity = 1;

                }

                else

                {

                    var transform = container.TransformToVisual(this);

                    var point = transform.TransformPoint(new Point(0, 0));

                    double opacity = point.Y / fadeSize;

                    if (opacity < 0)

                    {

                        opacity = 0;

                    }

                    if (opacity > 1)

                    {

                        opacity = 1;

                    }

 

                    if (opacity > 0 && opacity < 1)

                    {

                        opacity = opacity * (Math.E - 1) + 1;

                        opacity = Math.Log(opacity);

                    }

 

                    container.Opacity = opacity;

                }

            }

        }

    }

 

    #endregion

}

 

Kód nie je úplne univerzálny, ale môžete si ho upraviť podľa potreby. FadeListView má jednu vlastnosť FadeSize. Táto vlastnosť určuje, koľko pixelov od vrchu je položka plne viditeľna (bez čiastočnej priehľadnosti). Pri zmene tejto vlastnosti sa na nastaví horný okraj, aby mohli položky scrollovať aj o niečo vyššie. A pri scrollovani alebo akejkoľvek zmene položiek vo FadeListView sa pre každú položku nastaví vyššia priehľadnosť, čím je bližšie k hornému okraju. Priehľadnosť sa vypočítava logaritmicky, aby bola aj rýchlosť zmiznutia vyššia pre hornom okraji.

FadeListView screenshot 

Komentáre

Bez komentárov

Prihlásiť | Registrovať | Pomoc