简体   繁体   中英

Pull To Refresh with Long List Selector Windows Phone 8

I'm currently working with Windows Phone 8 application and get some problems with Long List Selector(LLS). Listbox do it by using TranslateY value:

 UIElement scrollContent = (UIElement)this.targetScrollViewer.Content;
 CompositeTransform ct = scrollContent.RenderTransform as CompositeTransform;
 //ct.TranslateY: I need this value in Viewport's LLS to detect exactly the distance moving from the TOP

I'm try to detect pull-to-refresh with LLS but it has some flaws(using Mouse Enter, MOve, and Leave) as:

    double manipulationStart = 0;
    double manipulationEnd = 0;

    void targetLLS_MouseEnter(object sender, MouseEventArgs e)
    {
        if (!this.IsRefreshing)
        {
            var pos = e.GetPosition(null);
            manipulationStart = pos.Y;
            IsMoving = false;
        }
    }

    private void targetLLS_MouseMove(object sender, MouseEventArgs e)
    {
        if (!this.IsRefreshing)
        {
            var pos = e.GetPosition(null);

            manipulationEnd = pos.Y;
            IsMoving = true;

            double TranslateY = manipulationEnd - manipulationStart;

            if (TranslateY > this.PullThreshold)
            {
                this.PullDistance = 100;
                this.PullFraction = 1.0;
                activityState = PullDownToRefreshPanel.ReadyToReleaseVisualState;
            }
            else if (TranslateY > 0)
            {
                this.PullDistance = 100;
                double threshold = this.PullThreshold;
                this.PullFraction = 1;// threshold == 0.0 ? 1.0 : Math.Min(1.0, TranslateY / threshold);
                activityState = PullDownToRefreshPanel.PullingDownVisualState;
            }
            else
            {
                this.PullDistance = 0;
                this.PullFraction = 0;
                activityState = PullDownToRefreshPanel.InactiveVisualState;
            }

            VisualStateManager.GoToState(this, activityState, false);
        }
    }

    bool IsMoving = false;
    void targetLLS_MouseLeave(object sender, MouseEventArgs e)
    {
        if (!this.IsRefreshing && IsMoving)
        {
            double TranslateY = manipulationEnd - manipulationStart;
            EventHandler handler = this.RefreshRequested;

            if (this.targetLLS.IsAtTop()
                && (activityState == PullDownToRefreshPanel.ReadyToReleaseVisualState))// TranslateY >= this.PullThreshold
            {
                if (handler != null)
                {
                    IsRefreshing = true;
                    handler(this, EventArgs.Empty);
                }
            }

            IsMoving = false;
        }

        PullDistance = 0;
        PullFraction = 0;
        manipulationStart = 0;
        manipulationEnd = 0;
        activityState = PullDownToRefreshPanel.InactiveVisualState;
        //VisualStateManager.GoToState(this, activityState, false);
    }

How can i do with below template:

 <Style x:Key="ViewportControlStyle" TargetType="ViewportControl">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ViewportControl">
                <ContentPresenter x:Name="ContentElement" Cursor="{TemplateBinding Cursor}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<Style x:Key="LongListSelectorNormalStyle" TargetType="phone:LongListSelector">
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="phone:LongListSelector">
                <Grid Background="{TemplateBinding Background}">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="ScrollStates">
                            <VisualStateGroup.Transitions>
                                <VisualTransition GeneratedDuration="00:00:00.5"/>
                            </VisualStateGroup.Transitions>
                            <VisualState x:Name="Scrolling">
                                <Storyboard>
                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="VerticalScrollBar"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="NotScrolling"/>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Grid Margin="{TemplateBinding Padding}">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <ViewportControl x:Name="ViewportControl" HorizontalContentAlignment="Stretch" VerticalAlignment="Top" Style="{StaticResource ViewportControlStyle}"/>
                        <ScrollBar x:Name="VerticalScrollBar" Grid.Column="0" Margin="4,0,4,0" Opacity="0" Orientation="Vertical" HorizontalAlignment="Right"/>
                    </Grid>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

My project need to implement pull function so please help me. Thanks for reading!

You can accomplish this with the ItemRealized event and using a ListHeader (or ListFooter to pull from bottom). Within the ItemRealized event you check if the item is your header object. If it is then load more items.

From the Windows Phone Dev Blog

The ItemRealized event is raised every time a LongListSelector item acquires a UI container to be displayed on the screen. In other words, every time an item enters the UI buffers above or below the current viewport, the ItemRealized event is raised. The event argument property ItemKind indicates whether the UI container is an Item , ListHeader , GroupHeader , or ListFooter . Using the property Container.Content you can get the actual object associated with the UI container that was realized. This way you can monitor the objects within the UI container buffer.

Note how the app code in this example contains a private variable _offsetKnob . This helps fine-tune the LongListSelector scrolling experience by helping to determine when to load more items depending on how heavy your item template is, or on how slow the response is from the service sending the data.

From the Twitter sample that they provide

void resultList_ItemRealized(object sender, ItemRealizationEventArgs e)
{
    if (!_viewModel.IsLoading && resultList.ItemsSource != null && resultList.ItemsSource.Count >= _offsetKnob)
    {
        if (e.ItemKind == LongListSelectorItemKind.Item)
        {
            if ((e.Container.Content as TwitterSearchResult).Equals(resultList.ItemsSource[resultList.ItemsSource.Count - _offsetKnob]))
            {
                _viewModel.LoadPage(_searchTerm, _pageNumber++);
            }
        }
    }
}

Download the complete Twitter Search sample

This is not completely trivial, but one way of doing it is to use GestureService

        this.gestureListener = GestureService.GetGestureListener(containerPage);
        this.gestureListener.DragStarted += gestureListener_DragStarted;
        this.gestureListener.DragCompleted += gestureListener_DragCompleted;
        this.gestureListener.DragDelta += gestureListener_DragDelta;

However, it has some bugs. For example, DragCompleted is not always raised, so you need to double-check for that using ManipulationCompleted event, which seems to be more reliable.

        containerPage.ManipulationStarted += delegate { this.manipulationInProgress = true; };
        containerPage.ManipulationCompleted += delegate
        { 
            this.manipulationInProgress = false;
            PerformDragComplete(); 
        };

Another issue is that DragDelta occasionally reports bad coordinates. So you would need a fix like this:

    Point refPosition = e.GetPosition(null);
    if (refPosition.X == 0 && refPosition.Y == 0)
    {
        Tracer.WriteLine("Skipping buggy event");
        return;
    }

Finally, you can find if list is all the way at the top:

public double VerticalOffset
{
    get
    {
        ViewportControl viewportControl = this.FindChildByName("ViewportControl") as ViewportControl;
        if (viewportControl != null)
        {
            Tracer.WriteLine("ViewPort.Bounds.Top=" + viewportControl.Bounds.Top +  " ViewPort.Top=" + viewportControl.Viewport.Top.ToString() + " State=" + this.ManipulationState);
            return viewportControl.Bounds.Top - viewportControl.Viewport.Top;
        }
        return double.NaN;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM