图库包裹Pannel未正确包装

时间:2018-01-29 14:19:54

标签: c# image xaml xamarin

我的Images似乎有问题,它们显示不正确,似乎是WrapPanel的问题。

侧面有很多白色空间,当时似乎只显示了一张图像。图像正在加载和一切。

以下是我XAML

的一部分
 <ScrollView Grid.Column="0" Grid.Row="1" x:Name="ssView" Orientation="Vertical" >
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="*">
                                </RowDefinition>
                                <RowDefinition Height="128">
                                </RowDefinition>
                                <RowDefinition Height="Auto">
                                </RowDefinition>
                            </Grid.RowDefinitions>
                            <controls:WrapPanelFixed x:Name="wpImages" Orientation="Horizontal" ItemHeightRequest="{Binding RequestedTileSize}" ItemWidthRequest="{Binding RequestedTileSize}" HeightScaling="1">
                                <controls:WrapPanelFixed.ItemTemplate>
                                    <DataTemplate>
                                        <RelativeLayout BackgroundColor="White">
                                            <Image
                                 RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1.0}"
                                RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=1.0}"
                                RelativeLayout.XConstraint="0"
                                RelativeLayout.YConstraint="0"
                                Source="{Binding LocalImagePath}" HorizontalOptions="Center" VerticalOptions="Center" Aspect="AspectFit">
                                                <Image.GestureRecognizers>
                                                    <TapGestureRecognizer Tapped="OnImageTapped" NumberOfTapsRequired="1"/>
                                                </Image.GestureRecognizers>
                                            </Image>

                                        </RelativeLayout>
                                    </DataTemplate>
                                </controls:WrapPanelFixed.ItemTemplate>
                            </controls:WrapPanelFixed>
                        </Grid>
                    </ScrollView>

c#部分代码

    public void Prepare()
    {
        RefreshImageList(Issue.Id);
    }

    protected void OnItemHeightChanged(object sender, ItemHeightChangedArg e)
    {
        int width = e.newHeigth;
        e.newWidth = width;
    }

    protected void OnItemWidthChanged(object sender, ItemWidthChangedArg e)
    {
        int width = e.newWidth;
        e.newHeigth = width;
    }
    protected void OnSizeChanged(object sender, EventArgs e)
    {
        wpImages.NeedLayoutOfChildren = true;
        wpImages.ForceLayout();
    }        public void Prepare()
    {
        RefreshImageList(Issue.Id);
    }

    protected void OnItemHeightChanged(object sender, ItemHeightChangedArg e)
    {
        int width = e.newHeigth;
        e.newWidth = width;
    }

    protected void OnItemWidthChanged(object sender, ItemWidthChangedArg e)
    {
        int width = e.newWidth;
        e.newHeigth = width;
    }
    protected void OnSizeChanged(object sender, EventArgs e)
    {
        wpImages.NeedLayoutOfChildren = true;
        wpImages.ForceLayout();
    }

WrapPanel

namespace WrapPanelFixed.Controls
{
  public class ItemHeightChangedArg : EventArgs
  {
    public int newWidth { get; set; }
    public int newHeigth { get; private set; }
    public ItemHeightChangedArg(int h)
    {
      newHeigth = h;
    }
  }
  public class ItemWidthChangedArg : EventArgs
  {
    public int newHeigth  { get; set; }
    public int newWidth{ get; private set; }
    public ItemWidthChangedArg(int h)
    {
      newWidth = h;
    }
  }
  // Fork of the Wrappanel at https://gist.github.com/NicoVermeir/7ffb34ebd639ed958382
  // This panel only allow fixed size items. and it will also strech/shrink item size to makesure it fills the orientation size
  public class WrapPanelFixed : Layout<View>
  {
    private event EventHandler<NotifyCollectionChangedEventArgs> _collectionChanged;
    public event EventHandler<ItemHeightChangedArg> ItemHeightChanged;
    public event EventHandler<ItemWidthChangedArg> ItemWidthChanged;
    public bool NeedLayoutOfChildren { get; set; }

    public int ItemHeight { get; private set; }
    public int ItemWidth { get; private set; }
    public int UnusedWidth { get; private set; }
    public int UnusedHeight { get; private set; }
    public double HeightScaling { get; set; }

    // Minimum number of items per constraint. (width or height)
    public int MinimumItems {get; set;}


    protected virtual void OnItemHeightChanged(int newHeight, out int newWidth)
    {
      newWidth = -1;
      EventHandler<ItemHeightChangedArg> handler = ItemHeightChanged;
      if(handler != null)
      {
        ItemHeightChangedArg e = new ItemHeightChangedArg(newHeight);
        handler(this, e);
        newWidth = e.newWidth;
      }
    }

    protected virtual void OnItemWidthChanged(int newHeight, out int newWidth)
    {
      newWidth = -1;
      EventHandler<ItemWidthChangedArg> handler = ItemWidthChanged;
      if (handler != null)
      {
        ItemWidthChangedArg e = new ItemWidthChangedArg(newHeight);
        handler(this, e);
        newWidth = e.newWidth;
      }
    }

    /// <summary>
    /// Backing Storage for the Orientation property
    /// </summary>
    public static readonly BindableProperty OrientationProperty =
        BindableProperty.Create<WrapPanelFixed, StackOrientation>(w => w.Orientation, StackOrientation.Vertical,
            propertyChanged: (bindable, oldvalue, newvalue) => ((WrapPanelFixed)bindable).OnSizeChanged());

    /// <summary>
    /// Orientation (Horizontal or Vertical)
    /// </summary>
    public StackOrientation Orientation
    {
      get { return (StackOrientation)GetValue(OrientationProperty); }
      set { SetValue(OrientationProperty, value); }
    }

    /// <summary>
    /// Backing Storage for the Spacing property
    /// </summary>
    public static readonly BindableProperty SpacingProperty =
        BindableProperty.Create<WrapPanelFixed, double>(w => w.Spacing, 6,
            propertyChanged: (bindable, oldvalue, newvalue) => ((WrapPanelFixed)bindable).OnSizeChanged());

    /// <summary>
    /// Spacing added between elements (both directions)
    /// </summary>
    /// <value>The spacing.</value>
    public double Spacing
    {
      get { return (double)GetValue(SpacingProperty); }
      set { SetValue(SpacingProperty, value); }
    }

    public static readonly BindableProperty ItemHeightRequestProperty =
        BindableProperty.Create<WrapPanelFixed, int>(w => w.ItemHeightRequest, 100,
            propertyChanged: (bindable, oldvalue, newvalue) => ((WrapPanelFixed)bindable).OnSizeChanged());

    public int ItemHeightRequest
    {
      get { return (int)GetValue(ItemHeightRequestProperty); }
      set { SetValue(ItemHeightRequestProperty, value); }
    }

    public static readonly BindableProperty ItemWidthRequestProperty =
    BindableProperty.Create<WrapPanelFixed, int>(w => w.ItemWidthRequest, 100,
        propertyChanged: (bindable, oldvalue, newvalue) => ((WrapPanelFixed)bindable).OnSizeChanged());

    public int ItemWidthRequest
    {
      get { return (int)GetValue(ItemWidthRequestProperty); }
      set { SetValue(ItemWidthRequestProperty, value); }
    }

    /// <summary>
    /// Backing Storage for the Spacing property
    /// </summary>
    public static readonly BindableProperty ItemTemplateProperty =
        BindableProperty.Create<WrapPanelFixed, DataTemplate>(w => w.ItemTemplate, null,
            propertyChanged: (bindable, oldvalue, newvalue) => ((WrapPanelFixed)bindable).OnSizeChanged());

    /// <summary>
    /// Spacing added between elements (both directions)
    /// </summary>
    /// <value>The spacing.</value>
    public DataTemplate ItemTemplate
    {
      get { return (DataTemplate)GetValue(ItemTemplateProperty); }
      set { SetValue(ItemTemplateProperty, value); }
    }

    /// <summary>
    /// Backing Storage for the Spacing property
    /// </summary>
    public static readonly BindableProperty ItemsSourceProperty =
        BindableProperty.Create<WrapPanelFixed, IEnumerable>(w => w.ItemsSource, null,
            propertyChanged: ItemsSource_OnPropertyChanged);

    /// <summary>
    /// Spacing added between elements (both directions)
    /// </summary>
    /// <value>The spacing.</value>
    public IEnumerable ItemsSource
    {
      get { return (IEnumerable)GetValue(ItemsSourceProperty); }
      set { SetValue(ItemsSourceProperty, value); }
    }

    private static void ItemsSource_OnPropertyChanged(BindableObject bindable, IEnumerable oldvalue, IEnumerable newvalue)
    {
      WrapPanelFixed wp = bindable as WrapPanelFixed;
      if (oldvalue != null)
      {
        var coll = (INotifyCollectionChanged)oldvalue;
        // Unsubscribe from CollectionChanged on the old collection
        coll.CollectionChanged -= wp.ItemsSource_OnItemChanged;
      }

      if (newvalue != null)
      {
        var coll = (INotifyCollectionChanged)newvalue;
        // Subscribe to CollectionChanged on the new collection
        coll.CollectionChanged += wp.ItemsSource_OnItemChanged;
      }
    }

    public WrapPanelFixed()
    {
      MinimumItems = 1;
      HeightScaling = 1.0;
      _collectionChanged += OnCollectionChanged;
      NeedLayoutOfChildren = true;
    }

    private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
    {
      if(args.Action == NotifyCollectionChangedAction.Reset)
      {
        Children.Clear();
        NeedLayoutOfChildren = true;
      }
      else
      {
        foreach (object item in args.NewItems)
        {
          var child = ItemTemplate.CreateContent() as View;
          if (child == null)
            return;

          child.BindingContext = item;
          Children.Add(child);
        }
        NeedLayoutOfChildren = true;
      }
    }

    public void OnCollectionRebuild(IList Items)
    {
      foreach (object item in Items)
      {
        var child = ItemTemplate.CreateContent() as View;
        if (child == null)
          return;

        child.BindingContext = item;
        Children.Add(child);
      }
      NeedLayoutOfChildren = true;
    }

    public void ItemsSource_OnItemChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
      if (_collectionChanged != null)
        _collectionChanged(sender, e);
    }

    /// <summary>
    /// This is called when the spacing or orientation properties are changed - it forces
    /// the control to go back through a layout pass.
    /// </summary>
    private void OnSizeChanged()
    {
      ForceLayout();
    }

    /// <summary>
    /// This method is called during the measure pass of a layout cycle to get the desired size of an element.
    /// </summary>
    /// <param name="widthConstraint">The available width for the element to use.</param>
    /// <param name="heightConstraint">The available height for the element to use.</param>
    protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
    {
      if (WidthRequest > 0)
        widthConstraint = Math.Min(widthConstraint, WidthRequest);
      if (HeightRequest > 0)
        heightConstraint = Math.Min(heightConstraint, HeightRequest);

      double internalWidth = double.IsPositiveInfinity(widthConstraint) ? double.PositiveInfinity : Math.Max(0, widthConstraint);
      double internalHeight = double.IsPositiveInfinity(heightConstraint) ? double.PositiveInfinity : Math.Max(0, heightConstraint);

      return Orientation == StackOrientation.Vertical ? DoVerticalMeasure(internalWidth, internalHeight) : DoHorizontalMeasure(internalWidth, internalHeight);

    }

    public static int CalculateItemSize(double sizeConstraint, int requestItemSize, double itemSpacing, int MinimumItems, out int nItemsPerConstraint)
    {
      requestItemSize += (int)(itemSpacing / 2);

      // Minimum size for MinimumItems
      int MinSize = (requestItemSize * MinimumItems) + (int)(itemSpacing * (MinimumItems - 1));
      if (MinimumItems > 0 && MinSize > sizeConstraint)
      {
        // Shrink item size so we requestItemSize is minimum of wanted items
        requestItemSize = (int)((sizeConstraint - (itemSpacing * (MinimumItems - 1))) / MinimumItems); // Atleast 2 columns
      }

      int newItemSize = requestItemSize;

      // How many items with the requested size fit in the sizeConstraint 
      nItemsPerConstraint = (int)(sizeConstraint / (requestItemSize + itemSpacing));

      // How much space do we got left
      // (There is 1 less spacing then number of item. Only spacing between items)
      double spaceLeft = sizeConstraint - ((nItemsPerConstraint * (requestItemSize + itemSpacing)) - itemSpacing);

      // If spaceLeft is > then 50% of requestItemSize and less then 1 full item extra, then make items bigger so they fill the sizeContraint
      // else make them small so one more item fits.
      if (spaceLeft > (requestItemSize * 0.5) && spaceLeft < (requestItemSize + itemSpacing))
      {
        int extraWidth = (int)(spaceLeft / nItemsPerConstraint);
        newItemSize = (int)(requestItemSize + extraWidth);
      }
      else
      {
        nItemsPerConstraint++;
        newItemSize = (int)((sizeConstraint - (itemSpacing * (nItemsPerConstraint - 1))) / nItemsPerConstraint);
      }

      return newItemSize;
    }

    /// <summary>
    /// Does the vertical measure.
    /// </summary>
    /// <returns>The vertical measure.</returns>
    /// <param name="widthConstraint">Width constraint.</param>
    /// <param name="heightConstraint">Height constraint.</param>
    private SizeRequest DoVerticalMeasure(double widthConstraint, double heightConstraint)
    {
      int nItemsPerCol = 0;
      int newItemHeight = CalculateItemSize(heightConstraint, ItemHeightRequest, Spacing, MinimumItems, out nItemsPerCol);

      UnusedHeight = (int)(heightConstraint - ((nItemsPerCol * (newItemHeight + Spacing)) - Spacing));

      if(ItemHeight != newItemHeight)
      {
        int newItemWidth;
        OnItemHeightChanged(newItemHeight, out newItemWidth);
        if (newItemWidth == -1)
          newItemWidth = newItemHeight;

        ItemHeight = newItemWidth;
        ItemWidth = newItemWidth;
        NeedLayoutOfChildren = true;
      }

      // Correct for rounding error
      int colCount = (int)((Children.Count / (double)nItemsPerCol) + 0.9);

      double height = (ItemWidth * nItemsPerCol) + ((nItemsPerCol - 1) * Spacing);
      double width = ((ItemHeight + Spacing) * colCount) - Spacing;
      return new SizeRequest(new Size(width, height), new Size(width, height));

    }

    /// <summary>
    /// Does the horizontal measure.
    /// </summary>
    /// <returns>The horizontal measure.</returns>
    /// <param name="widthConstraint">Width constraint.</param>
    /// <param name="heightConstraint">Height constraint.</param>
    private SizeRequest DoHorizontalMeasure(double widthConstraint, double heightConstraint)
    {
      int nItemsPerRow = 0;
      int newItemWidth = CalculateItemSize(widthConstraint, ItemWidthRequest, Spacing, MinimumItems, out nItemsPerRow);

      UnusedWidth = (int)(widthConstraint - ((nItemsPerRow * (newItemWidth + Spacing)) - Spacing));

      if(ItemWidth != newItemWidth)
      {
        int newItemHeight = 0;
        OnItemWidthChanged(newItemWidth, out newItemHeight);
        if(newItemHeight == -1)
          newItemHeight = newItemWidth;

        ItemWidth = newItemWidth;
        ItemHeight = newItemWidth;
        NeedLayoutOfChildren = true;
      }

      ItemHeight = (int)(ItemHeight * HeightScaling);
      // Correct for rounding error
      int rowCount = (int)((Children.Count / (double)nItemsPerRow)+0.9); // So if we have 12.1 rows we get 13

      double width = (ItemWidth * nItemsPerRow) + ((nItemsPerRow - 1) * Spacing);
      double height = ((ItemHeight + Spacing) * rowCount) - Spacing;

      return new SizeRequest(new Size(width, height), new Size(width, height));
    }

    /// <summary>
    /// Positions and sizes the children of a Layout.
    /// </summary>
    /// <param name="x">A value representing the x coordinate of the child region bounding box.</param>
    /// <param name="y">A value representing the y coordinate of the child region bounding box.</param>
    /// <param name="width">A value representing the width of the child region bounding box.</param>
    /// <param name="height">A value representing the height of the child region bounding box.</param>
    protected override void LayoutChildren(double x, double y, double width, double height)
    {
      // Prevent unnessecary layouting.(since all items are fixed in size, we only need to relayout them if itemsource is changed)
      if (NeedLayoutOfChildren == false)
        return;

      if (Orientation == StackOrientation.Vertical)
      {
        double colWidth = 0;
        double yPos = y, xPos = x;

        foreach (var child in Children.Where(c => c.IsVisible))
        {
          //var request = child.GetSizeRequest(width, height);

          double childWidth = ItemWidth;
          double childHeight = ItemHeight;

          colWidth = Math.Max(colWidth, childWidth);

          if (yPos + childHeight > height)
          {
            yPos = y;
            xPos += colWidth + Spacing;
            colWidth = 0;
          }

          var region = new Rectangle(xPos, yPos, childWidth, childHeight);
          LayoutChildIntoBoundingRegion(child, region);
          yPos += region.Height + Spacing;
        }
      }
      else
      {
        //double rowHeight = 0;
        x += UnusedWidth / 2;
        double yPos = y, xPos = x;

        foreach (var child in Children.Where(c => c.IsVisible))
        {
          double childWidth = ItemWidth;
          double childHeight = ItemHeight;

          //rowHeight = Math.Max(rowHeight, childHeight);

          if (xPos + childWidth > width)
          {
            xPos = x;
            yPos += childHeight + Spacing;
            //rowHeight = 0;
          }

          var region = new Rectangle(xPos, yPos, childWidth, childHeight);
          LayoutChildIntoBoundingRegion(child, region);
          xPos += region.Width + Spacing;
        }

      }
      NeedLayoutOfChildren = false;
    }
  }
}

1 个答案:

答案 0 :(得分:0)

试试这个:

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
  mc:Ignorable="d">

<Page.Resources>
    <DataTemplate x:Key="PhotoTemplate">
        <Grid Width="50"
              Height="50"
              Margin="0">
            <Image HorizontalAlignment="Center"
                   Stretch="UniformToFill">
                <Image.Source>
                    <BitmapImage DecodePixelHeight="200"
                                 UriSource="{Binding LocalImagePath}" />
                </Image.Source>
            </Image>
        </Grid>
    </DataTemplate>
    <Style TargetType="ListViewItem">
        <Setter Property="Margin" Value="0" />
        <Setter Property="Padding" Value="0" />
    </Style>
</Page.Resources>

<Grid Padding="48">
    <ListView Name="WrapPanelContainer"
              Margin="2"
              IsItemClickEnabled="True"
              ItemTemplate="{StaticResource PhotoTemplate}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <controls:WrapPanel Background="LightGray"
                                    VerticalSpacing="5"
                                    HorizontalSpacing="5"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ListView>
</Grid>
</Page>