Force pivot item to preload before it's shown

2019-04-30 02:18发布

问题:

I have a Pivot with several PivotItems, one of which contains a canvas that places its items in dynamic locations (depending on the data). I get the data, and I can place the items in their place before the user can choose this item (this isn't the first pivot). However, only when I select the PivotItem, the canvas renders itself, so you can see it flicker before it's shown as it should.

Is there a way to force the canvas to render before it's shown, so everything's prepared by the time the user sees it?

My code looks something like this:

In the page.xaml.cs:

private async void GameCenterView_OnDataContextChanged(object sender, EventArgs e)
{
    // Load data...

    // Handle other pivots

    // This is the problem pivot
    if (ViewModel.CurrentGame.SportTypeId == 1)
    {
        _hasLineups = ViewModel.CurrentGame.HasLineups.GetValueOrDefault();
        HasFieldPositions = ViewModel.CurrentGame.HasFieldPositions.GetValueOrDefault();

        // I only add the pivot when I need it, otherwise, it won't be shown
        if (_hasLineups)
        {
            if (MainPivot.Items != null) MainPivot.Items.Add(LineupPivotItem);
        }

        if (HasFieldPositions)
        {
            // Here I place all the items in their proper place on the canvas
            ArrangeLineup(ViewModel.TeamOneLineup, TeamOneCanvas);
            ArrangeLineup(ViewModel.TeamTwoLineup, TeamTwoCanvas);
        }
    }

    // Handle other pivots
}

private void ArrangeLineup(ObservableCollection<PlayerInLineupViewModel> teamLineup, RationalCanvas canvas)
{
    if (teamLineup == null)
        return;

    foreach (var player in teamLineup)
    {
        var control = new ContentControl
        {
            Content = player,
            ContentTemplate = LinupPlayerInFieldDataTemplate
        };
        control.SetValue(RationalCanvas.RationalTopProperty, player.Player.FieldPositionLine);
        control.SetValue(RationalCanvas.RationalLeftProperty, player.Player.FieldPositionSide);

        canvas.Children.Add(control);
    }
}

The canvas isn't the stock canvas. I created a new canvas that displays items according to their relative position (I get the positions in a scale of 0-99).

The logic happens in the OverrideArrange method:

protected override Size ArrangeOverride(Size finalSize)
{
    if (finalSize.Height == 0 || finalSize.Width == 0)
    {
        return base.ArrangeOverride(finalSize);
    }

    var yRatio = finalSize.Height/100.0;
    var xRatio = finalSize.Width/100.0;

    foreach (var child in Children)
    {
        var top = (double) child.GetValue(TopProperty);
        var left = (double) child.GetValue(LeftProperty);

        if (top > 0 || left > 0)
            continue;

        var rationalTop = (int) child.GetValue(RationalTopProperty);
        var rationalLeft = (int) child.GetValue(RationalLeftProperty);

        if (InvertY)
            rationalTop = 100 - rationalTop;

        if (InvertX)
            rationalLeft = 100 - rationalLeft;

        child.SetValue(TopProperty, rationalTop*yRatio);
        child.SetValue(LeftProperty, rationalLeft*xRatio);
    }

    return base.ArrangeOverride(finalSize);
}

Thanks.

回答1:

There are several tricks you could try. For example:

  1. In your ArrangeOverride you can short-circuit the logic if the size hasn't changed since last time you executed (and the data is the same)
  2. Make sure you're listening to the events on Pivot that tell you to get ready for presentation - PivotItemLoading for example
  3. You can have the control not actually be part of the Pivot, but instead be in the parent container (eg a Grid) and have it with Opacity of zero. Then set it to 100 when the target PivotItem comes into view.