ItemsControl 中子元素的加权分布

3

我希望创建一个与对象列表绑定的水平布局,其中包含一些要显示的信息。我希望此控件中的控件大小可以自动伸缩填充整个控件,就像UniformGrid一样。

问题在于,我不想控件的宽度是均匀分配的,而是基于对象本身的属性进行加权分配。

我认为使用自定义的ItemsPanelTemplate的ItemsControl将是一个不错的策略,因为它与UniformGrid很好地配合使用:

<ItemsControl x:Name="itemsControl" ItemsSource="{Binding MyResults}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Rows="1" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Border BorderBrush="Black" BorderThickness="1">
                <TextBlock Width="{Binding Weight}" Text="{Binding Weight}" />
            </Border>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

绑定到视图模型:

public class MainViewModel : PropertyChangedBase
{
    public List<MyTestClass> MyResults { get; set; } = new List<MyTestClass>()
    {
        new MyTestClass() { Weight = 25 },
        new MyTestClass() { Weight = 50 },
        new MyTestClass() { Weight = 25 }
    };
}

public class MyTestClass
{
    public int Weight { get; set; }
}

一个想法是切换到普通的网格(Grid),并使用列定义 *-宽度行为(例如 (25*,50*,25*) 但绑定为 Weight*),但我无法弄清楚如何做到这一点。

2个回答

2

使用自定义 Panel 实现会更容易。我建议采用以下方式:

public class WeightedPanel : Panel
{
    public static double GetWeight(DependencyObject obj)
    {
        return (double)obj.GetValue(WeightProperty);
    }

    public static void SetWeight(DependencyObject obj, double value)
    {
        obj.SetValue(WeightProperty, value);
    }

    public static readonly DependencyProperty WeightProperty =
        DependencyProperty.RegisterAttached("Weight", typeof(double), typeof(WeightedPanel), new PropertyMetadata(1.0));

    private double totalWeight_;
    protected override Size MeasureOverride(Size availableSize)
    {
        totalWeight_ = 0;
        foreach (UIElement child in InternalChildren)
        {
            totalWeight_ += WeightedPanel.GetWeight(child);
            child.Measure(availableSize);
        }

        return new Size(0, 0);
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        double offset = 0;
        foreach (UIElement child in InternalChildren)
        {
            var weight = WeightedPanel.GetWeight(child);
            var width = finalSize.Width * weight / totalWeight_;

            child.Arrange(new Rect(new Point(offset, 0), new Size(width, finalSize.Height)));

            offset += width;
        }

        return finalSize;
    }
}

用法

    <local:WeightedPanel>
        <Border local:WeightedPanel.Weight="25" Background="Green"></Border>
        <Border local:WeightedPanel.Weight="50" Background="Red"></Border>
        <Border local:WeightedPanel.Weight="25" Background="Blue"></Border>
    </local:WeightedPanel>

这看起来是一个非常好的解决方案。但我需要水平流动。我将尝试调整它以使用水平方向。 - FishySwede
我将ArrangeOverride更改为以下内容:{ var width = finalSize.Width * weight / totalWeight_; child.Arrange(new Rect(new Point(offset, 0), new Size(width, finalSize.Height))); offset += width;}。如果您使用此更新后的答案,我将非常乐意接受它作为我的问题的答案。 - FishySwede

-1

UniformGrid 提供了一种在网格中排列内容的方法,其中网格中的所有单元格都具有相同的大小。 我已经尝试过您使用 WrapPanel 的示例,我认为结果就是您所需要的。 因此,只需将 UniformGrid 更改为 WrapPanel 或 StackPanel(其中 Orientation="Horizontal"),请参见屏幕截图 WrapPanel


这非常接近我想要的,但我希望项目填充父容器并随窗口大小调整而缩放等。StackPanel和WrapPanel仅允许固定宽度。 - FishySwede

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接