Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/xamarin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 视图根据子项计数扩展其自身的宽度 问题_C#_Xamarin_Xamarin.forms - Fatal编程技术网

C# 视图根据子项计数扩展其自身的宽度 问题

C# 视图根据子项计数扩展其自身的宽度 问题,c#,xamarin,xamarin.forms,C#,Xamarin,Xamarin.forms,标题说明了一切,我想做的是得到视图的宽度(一旦它被计算出来)并乘以x来创建“页面”的平铺。然后,它将位于滚动视图中,因此我们可以左右导航 为此,我使用了一个名为WrapLayout的自定义视图,这为我完成了大部分工作。我修改了它,试图用我自己计算的宽度覆盖它的宽度(width*pagecount) 我关注的主要方法是OnMeasure,我相信这是针对这种情况进行覆盖的正确方法 protected override SizeRequest OnMeasure(double widthConstra

标题说明了一切,我想做的是得到视图的宽度(一旦它被计算出来)并乘以x来创建“页面”的平铺。然后,它将位于
滚动视图中
,因此我们可以左右导航

为此,我使用了一个名为
WrapLayout
的自定义视图,这为我完成了大部分工作。我修改了它,试图用我自己计算的宽度覆盖它的宽度(
width*pagecount

我关注的主要方法是
OnMeasure
,我相信这是针对这种情况进行覆盖的正确方法

protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
    var pageCount = (Children.Count / (Rows * 3)) + (Children.Count % (Rows * 3) > 0 ? 1 : 0);
    if (HeightRequest > 0)
        heightConstraint = Math.Min(heightConstraint, HeightRequest);
    double internalHeight = double.IsPositiveInfinity(heightConstraint) ? double.PositiveInfinity : Math.Max(0, heightConstraint);
    if (double.IsPositiveInfinity(widthConstraint) && double.IsPositiveInfinity(heightConstraint))
    {
        return new SizeRequest(Size.Zero, Size.Zero);
    }

    var deviceWidth = Application.Current.MainPage.Width;
    return new SizeRequest(new Size(deviceWidth * pageCount, internalHeight));
}
因此,我使用覆盖将视图大小调整为
deviceWidth*pageCount
,除了使用设备宽度之外,这项工作不是我想要的,它应该使用自己计算的宽度,以便在不拉伸整个设备宽度的情况下使用此视图

在Xamarin计算出视图的宽度并覆盖该值以替换为我自己的宽度,从而使视图变大x倍后,如何获得该宽度?

我理解这是一个大问题,所以如果我遗漏了任何关键信息,请随时发表评论

注意:如果有一个好的答案,我会在我有能力的时候给你一笔赏金。这快把我逼疯了

预期结果演示 按照评论中的要求。您可以看到平铺已正确缩放,并且视图已扩展其宽度,以允许每个视图有6个平铺。这个演示是使用本文中的代码创建的(使用设备宽度,而不是视图宽度)


代码 这将是大量的代码,因为这是一个自定义视图,所以请容忍我,我将把这篇文章放在文章的末尾,以缩短问题:

可重复播放
公共类RepeatableWrapLayout:WrapLayoutSimple
{
public static BindableProperty ItemsSourceProperty=BindableProperty.Create(nameof(ItemsSource)、typeof(IEnumerable)、typeof(RepeatableWrapLayout)、null、defaultBindingMode:BindingMode.OneWay、propertyChanged:itemshanged);
公共IEnumerable ItemsSource
{
得到
{
返回(IEnumerable)GetValue(ItemsSourceProperty);
}
设置
{
设置值(ItemsSourceProperty,value);
}
}
public static BindableProperty ItemTemplateProperty=BindableProperty.Create(nameof(ItemTemplate)、typeof(DataTemplate)、typeof(RepeatableWrapLayout)、default(DataTemplate)、propertyChanged:(bindable、oldValue、newValue)=>
{
变量控制=(RepeatableWrapLayout)可绑定;
//当发生属性更改早于ItemTemplate的ItemsSource时,请手动提高ItemsChanged
if(newValue!=null&&control.ItemsSource!=null&&control.doneItemSourceChanged)
{
ItemsChanged(可绑定、空、control.ItemsSource);
}
}
);
公共数据模板ItemTemplate
{
得到
{
返回(DataTemplate)GetValue(ItemTemplateProperty);
}
设置
{
SetValue(ItemTemplateProperty,值);
}
}
public static BindableProperty ItemTapCommandProperty=BindableProperty.Create(nameof(ItemTapCommand)、typeof(ICommand)、typeof(RepeatableWrapLayout)、default(ICommand)、defaultBindingMode:BindingMode.OneWay、propertyChanged:ItemTapCommandChanged);
/// 
///在点击某个项目时调用的命令。
/// 
公共ICommand ItemTapCommand
{
得到
{
返回(ICommand)GetValue(ItemTapCommandProperty);
}
设置
{
SetValue(ItemTapCommandProperty,值);
}
}
private bool doneItemSourceChanged=false;
私有静态void ItemTapCommandChanged(BindableObject bindable、object oldValue、object newValue)
{
变量控制=(RepeatableWrapLayout)可绑定;
if(oldValue!=newValue&&control.ItemsSource!=null)
{
更新命令(控制);
}
}
私有静态void ItemsChanged(BindableObject bindable、object oldValue、object newValue)
{
变量控制=(RepeatableWrapLayout)可绑定;
//当发生propertychanged早于ItemsSource而不是ItemTemplate时,不执行任何操作。
if(control.ItemTemplate==null)
{
control.doneItemSourceChanged=false;
返回;
}
control.doneItemSourceChanged=true;
IEnumerable新值可计算;
尝试
{
newValueAsEnumerable=作为IEnumerable的newValue;
}
捕获(例外e)
{
投掷e;
}
var oldObservableCollection=InotifyCollection更改时的oldValue;
if(oldObservableCollection!=null)
{
oldObservableCollection.CollectionChanged-=control.OnItemSourceCollectionChanged;
}
var newObservableCollection=InotifyCollection更改时的新值;
if(newobserveCollection!=null)
{
newObservableCollection.CollectionChanged+=control.OnItemSourceCollectionChanged;
}
control.Children.Clear();
if(newValueAsEnumerable!=null)
{
foreach(NewValuesEnumerable中的var项)
{
var view=CreateChildViewFor(control.ItemTemplate,item,control);
控件.Children.Add(视图);
}
}
if(control.ItemTapCommand!=null)
{
更新命令(控制);
}
control.UpdateChildrenLayout();
控件。InvalidateLayout();
}
私有静态void UpdateCommand(RepeatableWrapLayout控件)
{
foreach(control.Children中的var视图)
{
添加(新的TapGestureRecognizer{Command=control.ItemTapCommand,CommandParameter=view.BindingContext,});
}
}
私有资源集合已更改(对象发送方,NotifyCollect
public class RepeatableWrapLayout : WrapLayoutSimple
{
    public static BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof (IEnumerable), typeof (RepeatableWrapLayout), null, defaultBindingMode: BindingMode.OneWay, propertyChanged: ItemsChanged);
    public IEnumerable ItemsSource
    {
        get
        {
            return (IEnumerable)GetValue(ItemsSourceProperty);
        }

        set
        {
            SetValue(ItemsSourceProperty, value);
        }
    }

    public static BindableProperty ItemTemplateProperty = BindableProperty.Create(nameof(ItemTemplate), typeof (DataTemplate), typeof (RepeatableWrapLayout), default (DataTemplate), propertyChanged: (bindable, oldValue, newValue) =>
    {
        var control = (RepeatableWrapLayout)bindable;
        //when to occur propertychanged earlier ItemsSource than ItemTemplate, raise ItemsChanged manually
        if (newValue != null && control.ItemsSource != null && !control.doneItemSourceChanged)
        {
            ItemsChanged(bindable, null, control.ItemsSource);
        }
    }

    );
    public DataTemplate ItemTemplate
    {
        get
        {
            return (DataTemplate)GetValue(ItemTemplateProperty);
        }

        set
        {
            SetValue(ItemTemplateProperty, value);
        }
    }

    public static BindableProperty ItemTapCommandProperty = BindableProperty.Create(nameof(ItemTapCommand), typeof (ICommand), typeof (RepeatableWrapLayout), default (ICommand), defaultBindingMode: BindingMode.OneWay, propertyChanged: ItemTapCommandChanged);
    /// <summary>
    /// Command invoked when it tapped a item.
    /// </summary>
    public ICommand ItemTapCommand
    {
        get
        {
            return (ICommand)GetValue(ItemTapCommandProperty);
        }

        set
        {
            SetValue(ItemTapCommandProperty, value);
        }
    }

    private bool doneItemSourceChanged = false;
    private static void ItemTapCommandChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var control = (RepeatableWrapLayout)bindable;
        if (oldValue != newValue && control.ItemsSource != null)
        {
            UpdateCommand(control);
        }
    }

    private static void ItemsChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var control = (RepeatableWrapLayout)bindable;
        // when to occur propertychanged earlier ItemsSource than ItemTemplate, do nothing.
        if (control.ItemTemplate == null)
        {
            control.doneItemSourceChanged = false;
            return;
        }

        control.doneItemSourceChanged = true;
        IEnumerable newValueAsEnumerable;
        try
        {
            newValueAsEnumerable = newValue as IEnumerable;
        }
        catch (Exception e)
        {
            throw e;
        }

        var oldObservableCollection = oldValue as INotifyCollectionChanged;
        if (oldObservableCollection != null)
        {
            oldObservableCollection.CollectionChanged -= control.OnItemsSourceCollectionChanged;
        }

        var newObservableCollection = newValue as INotifyCollectionChanged;
        if (newObservableCollection != null)
        {
            newObservableCollection.CollectionChanged += control.OnItemsSourceCollectionChanged;
        }

        control.Children.Clear();
        if (newValueAsEnumerable != null)
        {
            foreach (var item in newValueAsEnumerable)
            {
                var view = CreateChildViewFor(control.ItemTemplate, item, control);
                control.Children.Add(view);
            }
        }

        if (control.ItemTapCommand != null)
        {
            UpdateCommand(control);
        }

        control.UpdateChildrenLayout();
        control.InvalidateLayout();
    }

    private static void UpdateCommand(RepeatableWrapLayout control)
    {
        foreach (var view in control.Children)
        {
            view.GestureRecognizers.Add(new TapGestureRecognizer{Command = control.ItemTapCommand, CommandParameter = view.BindingContext, });
        }
    }

    private void OnItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        var invalidate = false;
        if (e.Action == NotifyCollectionChangedAction.Replace)
        {
            this.Children.RemoveAt(e.OldStartingIndex);
            var item = e.NewItems[e.NewStartingIndex];
            var view = CreateChildViewFor(this.ItemTemplate, item, this);
            if (ItemTapCommand != null)
            {
                view.GestureRecognizers.Add(new TapGestureRecognizer{Command = ItemTapCommand, CommandParameter = item, });
            }

            this.Children.Insert(e.NewStartingIndex, view);
        }
        else if (e.Action == NotifyCollectionChangedAction.Add)
        {
            if (e.NewItems != null)
            {
                for (var i = 0; i < e.NewItems.Count; ++i)
                {
                    var item = e.NewItems[i];
                    var view = CreateChildViewFor(this.ItemTemplate, item, this);
                    if (ItemTapCommand != null)
                    {
                        view.GestureRecognizers.Add(new TapGestureRecognizer{Command = ItemTapCommand, CommandParameter = item, });
                    }

                    this.Children.Insert(i + e.NewStartingIndex, view);
                }
            }
        }
        else if (e.Action == NotifyCollectionChangedAction.Remove)
        {
            if (e.OldItems != null)
            {
                this.Children.RemoveAt(e.OldStartingIndex);
            }
        }
        else if (e.Action == NotifyCollectionChangedAction.Reset)
        {
            this.Children.Clear();
        }
        else
        {
            return;
        }

        if (invalidate)
        {
            this.UpdateChildrenLayout();
            this.InvalidateLayout();
        }
    }

    private View CreateChildViewFor(object item)
    {
        this.ItemTemplate.SetValue(BindableObject.BindingContextProperty, item);
        return (View)this.ItemTemplate.CreateContent();
    }

    private static View CreateChildViewFor(DataTemplate template, object item, BindableObject container)
    {
        var selector = template as DataTemplateSelector;
        if (selector != null)
        {
            template = selector.SelectTemplate(item, container);
        }

        //Binding context
        template.SetValue(BindableObject.BindingContextProperty, item);
        return (View)template.CreateContent();
    }
}
public class WrapLayoutSimple : Layout<View>
{
    Dictionary<Size, LayoutData> layoutDataCache = new Dictionary<Size, LayoutData>();
#region Props
    public static readonly BindableProperty RowsProperty = BindableProperty.Create("Rows", typeof (int), typeof (WrapLayout), 2, propertyChanged: (bindable, oldvalue, newvalue) =>
    {
    //((WrapTestLayout)bindable).InvalidateLayout();
    }

    );
    public static readonly BindableProperty ColumnsProperty = BindableProperty.Create("Columns", typeof (int), typeof (WrapLayout), 3, propertyChanged: (bindable, oldvalue, newvalue) =>
    {
    // ((WrapTestLayout)bindable).InvalidateLayout();
    }

    );
    public static readonly BindableProperty ColumnSpacingProperty = BindableProperty.Create("ColumnSpacing", typeof (double), typeof (WrapLayout), 0.00, propertyChanged: (bindable, oldvalue, newvalue) =>
    {
    //((WrapTestLayout)bindable).InvalidateLayout();
    }

    );
    public static readonly BindableProperty RowSpacingProperty = BindableProperty.Create("RowSpacing", typeof (double), typeof (WrapLayout), 0.00, propertyChanged: (bindable, oldvalue, newvalue) =>
    {
    //((WrapTestLayout)bindable).InvalidateLayout();
    }

    );
    public static readonly BindableProperty PagePaddingProperty = BindableProperty.Create("RowSpacing", typeof (Thickness), typeof (WrapLayout), new Thickness(0), propertyChanged: (bindable, oldvalue, newvalue) =>
    {
    //((WrapTestLayout)bindable).InvalidateLayout();
    }

    );
    public double ColumnSpacing
    {
        set
        {
            SetValue(ColumnSpacingProperty, value);
        }

        get
        {
            return (double)GetValue(ColumnSpacingProperty);
        }
    }

    public double RowSpacing
    {
        set
        {
            SetValue(RowSpacingProperty, value);
        }

        get
        {
            return (double)GetValue(RowSpacingProperty);
        }
    }

    public int Rows
    {
        set
        {
            SetValue(RowsProperty, value);
        }

        get
        {
            return (int)GetValue(RowsProperty);
        }
    }

    public int Columns
    {
        set
        {
            SetValue(ColumnsProperty, value);
        }

        get
        {
            return (int)GetValue(ColumnsProperty);
        }
    }

    public Thickness PagePadding
    {
        set
        {
            SetValue(PagePaddingProperty, value);
        }

        get
        {
            return (Thickness)GetValue(PagePaddingProperty);
        }
    }

#endregion
    public WrapLayoutSimple()
    {
        HorizontalOptions = LayoutOptions.FillAndExpand;
    }

    protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
    {
        var pageCount = (Children.Count / (Rows * 3)) + (Children.Count % (Rows * 3) > 0 ? 1 : 0);
        if (HeightRequest > 0)
            heightConstraint = Math.Min(heightConstraint, HeightRequest);
        double internalHeight = double.IsPositiveInfinity(heightConstraint) ? double.PositiveInfinity : Math.Max(0, heightConstraint);
        if (double.IsPositiveInfinity(widthConstraint) && double.IsPositiveInfinity(heightConstraint))
        {
            return new SizeRequest(Size.Zero, Size.Zero);
        }

        var deviceWidth = Application.Current.MainPage.Width;
        return new SizeRequest(new Size(deviceWidth * pageCount, internalHeight));
    }

    protected override void LayoutChildren(double x, double y, double width, double height)
    {
        var PageCount = (Children.Count / (Rows * 3)) + (Children.Count % (Rows * 3) > 0 ? 1 : 0);
        var pageWidth = width / PageCount;
        LayoutData layoutData = GetLayoutData(pageWidth, height);
        if (layoutData.VisibleChildCount == 0)
        {
            return;
        }

        double xChild = x;
        double yChild = y;
        int row = 0;
        int column = 0;
        int count = 0;
        int page = 0;
        int itemsPerPage = Rows * 3;
        foreach (View child in Children)
        {
            if (!child.IsVisible)
            {
                continue;
            }

            // New page
            if (count % itemsPerPage == 0 & count != 0)
            {
                // Add a page on
                page++;
                // Reset the Y so we start from the top again
                yChild = y;
            }

            count++;
            // A check for a guff width, if not use the good stuff.
            // Width * page will get it to the right width
            double xLocation;
            if (Double.IsInfinity(pageWidth))
                xLocation = 0;
            else
                xLocation = (pageWidth * page);
            LayoutChildIntoBoundingRegion(child, new Rectangle(new Point(xChild + xLocation, yChild), layoutData.CellSize));
            Debug.WriteLine("Adding child x: {0} y: {1} page: {2}", xChild + xLocation, yChild, page);
            // Reset for Second row if we hit our col limit
            if (++column == layoutData.Columns)
            {
                // Reset col
                column = 0;
                // Add row
                row++;
                // Reset the x so we start fromt he x start again (start of new row)
                xChild = x;
                // Add the height ready for the next placement (a row down)
                yChild += layoutData.CellSize.Height;
            }
            else
            {
                // Add the width ready for the next placement
                xChild += layoutData.CellSize.Width;
            }
        }
    }

    LayoutData GetLayoutData(double width, double height)
    {
        Debug.WriteLine("Page Width: " + width);
        Size size = new Size(width, height);
        // Check if cached information is available.
        if (layoutDataCache.ContainsKey(size))
        {
            return layoutDataCache[size];
        }

        int visibleChildCount = 0;
        Size maxChildSize = new Size();
        LayoutData layoutData = new LayoutData();
        // Enumerate through all the children.
        foreach (View child in Children)
        {
            // Skip invisible children.
            if (!child.IsVisible)
                continue;
            // Count the visible children.
            visibleChildCount++;
            // Get the child's requested size.
            SizeRequest childSizeRequest = child.Measure(Double.PositiveInfinity, Double.PositiveInfinity);
            // Accumulate the maximum child size.
            maxChildSize.Width = Math.Max(maxChildSize.Width, childSizeRequest.Request.Width);
            maxChildSize.Height = Math.Max(maxChildSize.Height, childSizeRequest.Request.Height);
        }

        if (visibleChildCount != 0)
        {
            // Now maximize the cell size based on the layout size.
            Size cellSize = new Size();
            if (Double.IsPositiveInfinity(width))
            {
                cellSize.Width = maxChildSize.Width;
            }
            else
            {
                cellSize.Width = width / Columns;
            }

            if (Double.IsPositiveInfinity(height))
            {
                cellSize.Height = maxChildSize.Height;
            }
            else
            {
                cellSize.Height = height / Rows;
            }

            layoutData = new LayoutData(visibleChildCount, cellSize, Rows, Columns);
        }

        layoutDataCache.Add(size, layoutData);
        Debug.WriteLine("Cell Width: " + layoutData.CellSize.Width + " Height: " + layoutData.CellSize.Height);
        return layoutData;
    }

    protected override void InvalidateLayout()
    {
        base.InvalidateLayout();
        // Discard all layout information for children added or removed.
        layoutDataCache.Clear();
    }

    protected override void OnChildMeasureInvalidated()
    {
        base.OnChildMeasureInvalidated();
        // Discard all layout information for child size changed.
        layoutDataCache.Clear();
    }
}
<l:RepeatableWrapLayout
                x:Name="rwl"
                HorizontalOptions="Fill" VerticalOptions="FillAndExpand" >
    <l:RepeatableWrapLayout.ItemTemplate>
        <DataTemplate>
            <StackLayout BackgroundColor="{Binding Color}">
                <Label VerticalTextAlignment="Center" HorizontalTextAlignment="Center"
                                Text="{Binding Name}" />
            </StackLayout>
        </DataTemplate>
    </l:RepeatableWrapLayout.ItemTemplate>
</l:RepeatableWrapLayout>
public static readonly BindableProperty ParentWidthProperty = BindableProperty.Create("ParentWidth",
    typeof(double),
    typeof(WrapLayout),
    0.00,
    propertyChanged: (bindable, oldvalue, newvalue) =>
    {
        ((RepeatableWrapLayout)bindable).SetNewWidth();
    });

public double ParentWidth
{
    set { SetValue(ParentWidthProperty, value); }
    get { return (double)GetValue(ParentWidthProperty); }
}
double oldParentWidth;
public void SetNewWidth()
{
    var pageCount = (Children.Count / (Rows * 3)) + (Children.Count % (Rows * 3) > 0 ? 1 : 0);

    if (ParentWidth > 0 && ParentWidth != oldParentWidth)
    {
        oldParentWidth = ParentWidth;
        WidthRequest = ParentWidth * pageCount;
    }
}
<Controls:RepeatableWrapLayout ParentWidth="{Binding Source={x:Reference Name=parentScrollView} ,Path=Width}" ...>
   ...
</Controls:RepeatableWrapLayout>