xaml - 可以包装Listview内集合的内容的布局

标签 xaml mvvm xamarin.forms itemscontrol

我正在尝试列出部门中工作的员工 list 。一个部门中有几个部门和几个员工。以下是我的代码,在滚动和包装内容(员工图像和姓名)时遇到困难。至于包装内容,如果一行没有足够的空间,我希望将内容(图像和员工的名字)显示在新行中。

到目前为止,我已经尝试了几种选择,但无济于事。我正在使用 ItemsControl ,我也尝试添加StackLayout而不是 WrapLayout

谁能告诉我如何解决滚动问题和内容包裹问题?我可以使用任何解决方法或任何其他布局吗?谢谢你。

XAML

<ListView ItemsSource="{Binding Departments}" HasUnevenRows="True">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <StackLayout Margin="20,20,20,20">
                        <Label Text="{Binding DepartmentName}" />
                        <Label Text="{Binding DepartmentId}" />

                        <local:ItemsControl ItemsSource="{Binding Employees}">
                            <local:ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <Grid>
                                            <local:WrapLayout>
                                                <Image Source="{Binding ImageUrl}"
                                                       WidthRequest="60"
                                                       HeightRequest="60"/>
                                                <Label
                                                       Text="{Binding FirstName}"
                                                       HorizontalTextAlignment="Center"
                                                       VerticalTextAlignment="Center"
                                                    LineBreakMode="WordWrap"/>
                                            </local:WrapLayout>
                                    </Grid>
                                </DataTemplate>
                            </local:ItemsControl.ItemTemplate>
                        </local:ItemsControl>

                    </StackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>  

更新1:



部门
public class Department
{
    public int DepartmentID { get; set; }
    public string DepartmentName { get; set; }
    // and several other properties

    public List<Employee> Employees { get; set; }
}

Employee.cs
public class Employee
{
    public int EmployeeID { get; set; }
    public string FirstName { get; set; }
    public string ImageUrl{ get; set; }

    // and several other properties
}  

更新2

似乎仅在我的一个测试设备中不会发生滚动。但是仍然需要一种能够包装控件的布局。

更新3
我希望数据显示如下图所示。但是现在,使用上面的代码,WrapLayout中的内容没有被包装,而是调整了大小以适合一行。如果第一行中没有空格,我希望将它们包装起来。

enter image description here

最佳答案

当您提到WrapLayout时,我假设您是将this one定义为here

另外,由于您已经在使用 ItemsControl ,因此建议您对其进行调整以支持WrapLayout而不是StackLayout。例如:

public class ItemsControl : WrapLayout
{
    public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(
        "ItemsSource", typeof(IList), typeof(ItemsControl), propertyChanging: OnItemsSourceChanged);

    /// <summary>
    /// Gets or sets the items source - can be any collection of elements.
    /// </summary>
    /// <value>The items source.</value>
    public IList ItemsSource
    {
        get { return (IList)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }

    public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(
        "ItemTemplate", typeof(DataTemplate), typeof(ItemsControl));

    /// <summary>
    /// Gets or sets the item template used to generate the visuals for a single item.
    /// </summary>
    /// <value>The item template.</value>
    public DataTemplate ItemTemplate
    {
        get { return (DataTemplate)GetValue(ItemTemplateProperty); }
        set { SetValue(ItemTemplateProperty, value); }
    }

    public ItemsControl()
    {
        Padding = new Thickness(0);
        Margin = new Thickness(0);
    }

    static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
    {
        ((ItemsControl)bindable).OnItemsSourceChangedImpl((IList)oldValue, (IList)newValue);
    }

    void OnItemsSourceChangedImpl(IList oldValue, IList newValue)
    {
        // Unsubscribe from the old collection
        if (oldValue != null)
        {
            INotifyCollectionChanged ncc = oldValue as INotifyCollectionChanged;
            if (ncc != null)
                ncc.CollectionChanged -= OnCollectionChanged;
        }

        if (newValue == null)
        {
            Children.Clear();
        }
        else
        {
            FillContainer(newValue);
            INotifyCollectionChanged ncc = newValue as INotifyCollectionChanged;
            if (ncc != null)
                ncc.CollectionChanged += OnCollectionChanged;
        }
    }

    /// <summary>
    /// This method takes our items source and generates visuals for
    /// each item in the collection; it can reuse visuals which were created
    /// previously and simply changes the binding context.
    /// </summary>
    /// <param name="newValue">New items to display</param>
    /// <exception cref="T:System.ArgumentNullException"></exception>
    void FillContainer(IList newValue)
    {
        var template = ItemTemplate;
        var visuals = Children;

        if (template == null)
            throw new NotSupportedException("ItemTemplate must be specified!");

        var newVisuals = new List<View>(Children); //using a list to avoid multiple layout refresh
        Children.Clear();

        for (int i = 0; i < newVisuals.Count; i++)
        {
            newVisuals[i].IsVisible = i < newValue.Count;
        }

        for (int i = 0; i < newValue.Count; i++)
        {
            var dataItem = newValue[i];

            if (visuals.Count > i)
            {
                if (template != null)
                {
                    var visualItem = visuals[i];
                    visualItem.BindingContext = dataItem;
                }
            }
            else
            {
                if (template != null)
                {
                    // Pull real template from selector if necessary.
                    var dSelector = template as DataTemplateSelector;
                    if (dSelector != null)
                        template = dSelector.SelectTemplate(dataItem, this);

                    var view = template.CreateContent() as View;
                    if (view != null)
                    {
                        view.BindingContext = dataItem;
                        newVisuals.Add(view);
                    }
                }
            }
        }

        foreach (var child in newVisuals) //wish they had a nice AddRange method here
            if(child.IsVisible)
                Children.Add(child);
    }

    /// <summary>
    /// This is called when the data source collection implements
    /// collection change notifications and the data has changed.
    /// This is not optimized - it simply replaces all the data.
    /// </summary>
    /// <param name="sender">Sender.</param>
    /// <param name="e">E.</param>
    void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        FillContainer((IList)sender);
    }
}

然后将您的XAML更改为:
<ListView ItemsSource="{Binding Departments}" HasUnevenRows="True">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <StackLayout Margin="20,20,20,20">
                    <Label Text="{Binding DepartmentName}" />
                    <Label Text="{Binding DepartmentId}" />

                    <local:ItemsControl ItemsSource="{Binding Employees}">
                        <local:ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <StackLayout Orientation="Horizontal">
                                    <Image Source="{Binding ImageUrl}"
                                                   WidthRequest="60"
                                                   HeightRequest="60"/>
                                    <Label Text="{Binding FirstName}"
                                           HorizontalTextAlignment="Center"
                                           VerticalTextAlignment="Center" />
                                </StackLayout>
                            </DataTemplate>
                        </local:ItemsControl.ItemTemplate>
                    </local:ItemsControl>
                </StackLayout>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>  

enter image description here

enter image description here

关于xaml - 可以包装Listview内集合的内容的布局,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46153402/

相关文章:

macos - Visual Studio for Mac 无法通过 "Add packages"

c# - 获取 GeometryModel3D 的名称

visual-studio-2008 - 在 VS2008 中加速 XAML 编辑

WPF/MVVM 设计建议

c# - 使用 x :Bind 设置 GridView 项目源的 UWP 问题

android - 部署 Xamarin Forms Android 应用程序时内存不足

Xamarin Forms 导航堆栈

xaml - Xamarin.Forms - 将元素放置在另一个元素之上。

WPF/Silverlight : How to DataTrigger a Storyboard Animation in MVVM?

c# - WPF 加载微调器