WPF WrapPanel中具有的高度的一些项目* [英] WPF WrapPanel with some items having a height of *

查看:455
本文介绍了WPF WrapPanel中具有的高度的一些项目*的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一个看似简单的问题,我一直在努力解决的问题。我希望有一个控制(或XAML布局magickry),其行为类似于网格有与*一个高一些的行,但支持列的包装。地狱;称之为WrapGrid。 :)

A deceptively simple question that I have been trying to solve. I want a control (or some XAML layout magickry) that behaves similar to a Grid that has some rows with a Height of *, but supports wrapping of columns. Hell; call it a WrapGrid. :)

下面是一个实体模型可视化这一点。想象一下,一个网格定义为这样的:

Here's a mockup to visualize this. Imagine a grid defined as such:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="400">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Button Grid.Row="0" MinHeight="30">I'm auto-sized.</Button>
        <Button Grid.Row="1" MinHeight="90">I'm star-sized.</Button>
        <Button Grid.Row="2" MinHeight="30">I'm auto-sized.</Button>
        <Button Grid.Row="3" MinHeight="90">I'm star-sized, too!</Button>
        <Button Grid.Row="4" MinHeight="30">I'm auto-sized.</Button>
        <Button Grid.Row="5" MinHeight="30">I'm auto-sized.</Button>
    </Grid>
</Window>

我想这个小组做的是包装一个项目到附加列时,该项目无法获得比了minHeight任何较小。下面是一些样机我做了详细说明这个过程的一个可怕的MSPAINT 。

What I want this panel to do is wrap an item into an additional column when the item can not get any smaller than its minHeight. Here is a horrible MSPaint of some mockups I made detailing this process.

从XAML的汽车大小的按钮有30 minHeights,与星大按钮具有90. minHeights召回

Recall from the XAML that the auto-sized buttons have minHeights of 30, and the star-sized buttons have minHeights of 90.

这样机只是两个网格并排的设计师,我手​​动移动周围的按钮。可以想象,这可以通过编程完成,并作为一个类型的复杂的解决方案这一点。

This mockup is just two grids side by side and I manually moved buttons around in the designer. Conceivably, this could be done programmatically and serve as a sort of convoluted solution to this.

如何才能做到这一点?我会接受任何解决方案,无论是通过XAML或者有code-背后(虽然我会preFER纯XAML,如果可能的,因为XAML code背后,是更严厉的IronPython的实施)。

How can this be done? I will accept any solution whether it's through xaml or has some code-behind (though I would prefer pure XAML if possible since xaml code behind is tougher to implement in IronPython).

更新与赏金

我设法解决如何使用Meleak的解决方案,在我的IPY的应用程序:

I managed to work out how to use Meleak's solution in my IPy app:

1)我编 WrapGridPanel.cs 与一个DLL CSC

1) I compiled WrapGridPanel.cs into a DLL with csc:

C:\Projects\WrapGridTest\WrapGridTest>csc /target:library "WrapGridPanel.cs" /optimize /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\PresentationFramework.dll" /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\PresentationCore.dll" /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\WindowsBase.dll" /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Xaml.dll" 

更新:新增了 /优化开关,这个网一个小的性能提升。

Update: Added the /optimize switch, this nets a small performance increase

2),我将它添加到我的应用程序的XAML与下面一行。

2) I added it to my application's xaml with the following line.

xmlns:local="clr-namespace:WrapGridTest;assembly=WrapGridPanel.dll"

这个运行良好,但它打破了设计师。我真的不能找到一种解决方法是,它看起来是在VS2010中的错误。因此,作为一种变通方法,以便能够使用设计,我只是添加了WrapGridPanel编程方式在运行时:

This runs fine, but it breaks the designer. I can't really find a workaround for this yet, it looks to be a bug in VS2010. So as a workaround, in order to be able to use the designer, I just add the WrapGridPanel programmatically at runtime:

clr.AddReference("WrapGridPanel.dll")
from WrapGridTest import WrapGridPanel
wgp = WrapGridPanel()

在调整大小时性能缓慢

在我的IronPython的应用程序,调整包含本WrapGridPanel窗口是缓慢的,hitchy。请问 RecalcMatrix()算法进行优化?难道也许可以不经常叫什么名字?也许覆盖的MeasureOverride ArrangeOverride ,尼古拉斯建议,将有更好的表现?

In my IronPython application, resizing the window containing this WrapGridPanel is slow and hitchy. Could the RecalcMatrix() algorithm be optimized? Could it perhaps be called less frequently? Maybe overriding MeasureOverride and ArrangeOverride, as Nicholas suggested, would perform better?

更新:根据VS2010仪器探查,在RecalcMatrix花()97%的时间都花在清除(),并添加()。修改就地每个元素将是一个巨大的性能提升。我是在一个失衡的自己,但它总是艰难的修改别人的code ... http://i.stack.imgur.com/tMTWU.png

Update: According to the VS2010 Instrumentation Profiler, 97% of the time spent in RecalcMatrix() is spent on Clear() and Add(). Modifying each element in-place would be a huge performance improvement. I'm taking a whack at it myself but it's always tough modifying someone else's code... http://i.stack.imgur.com/tMTWU.png

下面是一个样机的我的实际应用程序的用户界面的一部分,在XAML中,如果你想尝试一下。 http://pastebin.com/2EWY8NS0

Here is a mockup of part of my actual application's UI, in XAML, if you wish to try it out. http://pastebin.com/2EWY8NS0

推荐答案

更新
优化 RecalcMatrix 这样在需要的时候将UI仅重建。它不碰,除非必要的界面,因此它应该是快很多。

Update
Optimized RecalcMatrix so the UI is only rebuilt when needed. It doesn't touch the UI unless necessary so it should be much faster.

再次更新
在使用修正了保证金

Update Again
Fixed problem when using Margin

时认为你在寻找什么基本上是一个 WrapPanel中与水平方向在哪里它的每一个元素是1列电网。列中的每个元素则有一个相应的 RowDefinition 其中身高属性相匹配的附加属性(WrapHeight)在其子集。该小组将必须在一个电网本身,与身高=* WIDTH =自动,因为儿童应定位在提供身高并不在乎用宽度

Is think what you're looking at is basically a WrapPanel with Horizontal Orientation where every element in it is a 1 Column Grid. Each element in a Column then has a corresponding RowDefinition where the Height Property matches an attached property ("WrapHeight") set on its Child. This Panel would have to be in a Grid itself, with Height="*" and Width="Auto" because the Children should be positioned by the available Height and not care about the available Width.

我做了一个这样的实现,我称之为 WrapGridPanel 。您可以使用它像这样

I made an implementation of this which I called a WrapGridPanel. You can use it like this

<local:WrapGridPanel>
    <Button MinHeight="30" local:WrapGridPanel.WrapHeight="Auto">I'm auto-sized.</Button>
    <Button MinHeight="90" local:WrapGridPanel.WrapHeight="*">I'm star-sized.</Button>
    <Button MinHeight="30" local:WrapGridPanel.WrapHeight="Auto">I'm auto-sized.</Button>
    <Button MinHeight="90" local:WrapGridPanel.WrapHeight="*">I'm star-sized, too!</Button>
    <Button MinHeight="30" local:WrapGridPanel.WrapHeight="Auto">I'm auto-sized.</Button>
    <Button MinHeight="30" local:WrapGridPanel.WrapHeight="Auto">I'm auto-sized.</Button>
</local:WrapGridPanel>

WrapGridPanel.cs

[ContentProperty("WrapChildren")] 
public class WrapGridPanel : Grid
{
    private WrapPanel m_wrapPanel = new WrapPanel();
    public WrapGridPanel()
    {
        ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1.0, GridUnitType.Auto) } );
        RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0, GridUnitType.Star) } );
        Children.Add(m_wrapPanel);
        WrapChildren = new ObservableCollection<FrameworkElement>();
        WrapChildren.CollectionChanged += WrapChildren_CollectionChanged;
        DependencyPropertyDescriptor actualHeightDescriptor
            = DependencyPropertyDescriptor.FromProperty(WrapGridPanel.ActualHeightProperty,
                                                        typeof(WrapGridPanel));
        if (actualHeightDescriptor != null)
        {
            actualHeightDescriptor.AddValueChanged(this, ActualHeightChanged);
        }
    }

    public static void SetWrapHeight(DependencyObject element, GridLength value)
    {
        element.SetValue(WrapHeightProperty, value);
    }
    public static GridLength GetWrapHeight(DependencyObject element)
    {
        return (GridLength)element.GetValue(WrapHeightProperty);
    }
    public ObservableCollection<FrameworkElement> WrapChildren
    {
        get { return (ObservableCollection<FrameworkElement>)base.GetValue(WrapChildrenProperty); }
        set { base.SetValue(WrapChildrenProperty, value); }
    }

    void ActualHeightChanged(object sender, EventArgs e)
    {
        RecalcMatrix();
    }
    void WrapChildren_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        RecalcMatrix();
    }

    List<List<FrameworkElement>> m_elementList = null;
    private bool SetupMatrix()
    {
        m_elementList = new List<List<FrameworkElement>>();
        double minHeightSum = 0;
        m_elementList.Add(new List<FrameworkElement>());
        int column = 0;
        if (WrapChildren.Count > 0)
        {
            foreach (FrameworkElement child in WrapChildren)
            {
                double tempMinHeight = 0.0;
                if (WrapGridPanel.GetWrapHeight(child).GridUnitType != GridUnitType.Star)
                {
                    tempMinHeight = Math.Max(child.ActualHeight, child.MinHeight) + child.Margin.Top + child.Margin.Bottom;
                }
                else
                {
                    tempMinHeight = child.MinHeight + child.Margin.Top + child.Margin.Bottom;
                }
                minHeightSum += tempMinHeight;
                if (minHeightSum > ActualHeight)
                {
                    minHeightSum = tempMinHeight;
                    m_elementList.Add(new List<FrameworkElement>());
                    column++;
                }
                m_elementList[column].Add(child);
            }
        }
        if (m_elementList.Count != m_wrapPanel.Children.Count)
        {
            return true;
        }
        for (int i = 0; i < m_elementList.Count; i++)
        {
            List<FrameworkElement> columnList = m_elementList[i];
            Grid wrapGrid = m_wrapPanel.Children[i] as Grid;
            if (columnList.Count != wrapGrid.Children.Count)
            {
                return true;
            }
        }
        return false;
    }
    private void RecalcMatrix()
    {
        if (ActualHeight == 0 || SetupMatrix() == false)
        {
            return;
        }

        Binding heightBinding = new Binding("ActualHeight");
        heightBinding.Source = this;
        while (m_elementList.Count > m_wrapPanel.Children.Count)
        {
            Grid wrapGrid = new Grid();
            wrapGrid.SetBinding(Grid.HeightProperty, heightBinding);
            m_wrapPanel.Children.Add(wrapGrid);
        }
        while (m_elementList.Count < m_wrapPanel.Children.Count)
        {
            Grid wrapGrid = m_wrapPanel.Children[m_wrapPanel.Children.Count - 1] as Grid;
            wrapGrid.Children.Clear();
            m_wrapPanel.Children.Remove(wrapGrid);
        }

        for (int i = 0; i < m_elementList.Count; i++)
        {
            List<FrameworkElement> columnList = m_elementList[i];
            Grid wrapGrid = m_wrapPanel.Children[i] as Grid;
            wrapGrid.RowDefinitions.Clear();
            for (int j = 0; j < columnList.Count; j++)
            {
                FrameworkElement child = columnList[j];
                GridLength wrapHeight = WrapGridPanel.GetWrapHeight(child);
                Grid.SetRow(child, j);
                Grid parentGrid = child.Parent as Grid;
                if (parentGrid != wrapGrid)
                {
                    if (parentGrid != null)
                    {
                        parentGrid.Children.Remove(child);
                    }
                    wrapGrid.Children.Add(child);
                }

                RowDefinition rowDefinition = new RowDefinition();
                rowDefinition.Height = new GridLength(Math.Max(1, child.MinHeight), wrapHeight.GridUnitType);
                wrapGrid.RowDefinitions.Add(rowDefinition); 
            }
        }
    }

    public static readonly DependencyProperty WrapHeightProperty =
            DependencyProperty.RegisterAttached("WrapHeight",
                                                typeof(GridLength),
                                                typeof(WrapGridPanel),
                                                new FrameworkPropertyMetadata(new GridLength(1.0, GridUnitType.Auto)));

    public static readonly DependencyProperty WrapChildrenProperty =
            DependencyProperty.Register("WrapChildren",
                                        typeof(ObservableCollection<FrameworkElement>),
                                        typeof(WrapGridPanel),
                                        new UIPropertyMetadata(null));
}

更新
修正了不止一星大小列问题。
这里新的示例应用程序: http://www.mediafire.com/?28z4rbd4pp790t2

Update
Fixed more than one star-sized column problem.
New sample app here: http://www.mediafire.com/?28z4rbd4pp790t2

更新
一张照片,试图解释什么 WrapGridPanel 确实

Update
A picture that tries to explain what WrapGridPanel does

这篇关于WPF WrapPanel中具有的高度的一些项目*的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆