我的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;
}
}
}
答案 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>