【发布时间】:2023-04-09 23:55:01
【问题描述】:
我正在尝试实现一个与标准 WrapPanel 类似的自定义控件,但它允许您指定页眉和页脚。从视觉上看,这就是我想要完成的:
我创建了一个自定义控件,似乎为页眉和页脚项目留出了空间,但我无法让它们在视觉上出现。这是我第一次尝试任何类型的自定义控件,因此感谢任何帮助或输入!
C#
using System;
using System.Windows;
using System.Windows.Controls;
namespace MyProject.Extras
{
public class HeaderedFooteredPanel : Panel
{
public FrameworkElement Header
{
get { return (FrameworkElement) GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
public FrameworkElement Footer
{
get { return (FrameworkElement)GetValue(FooterProperty); }
set { SetValue(FooterProperty, value); }
}
public static DependencyProperty HeaderProperty = DependencyProperty.Register(
nameof(Header),
typeof(FrameworkElement),
typeof(HeaderedFooteredPanel),
new PropertyMetadata((object)null));
public static DependencyProperty FooterProperty = DependencyProperty.Register(
nameof(Footer),
typeof(FrameworkElement),
typeof(HeaderedFooteredPanel),
new PropertyMetadata((object)null));
protected override Size MeasureOverride(Size constraint)
{
double x = 0.0;
double y = 0.0;
double largestY = 0.0;
double largestX = 0.0;
var measure = new Action<FrameworkElement>(element =>
{
element.Measure(constraint);
if (x > 0 && // Not the first item on this row
(x + element.DesiredSize.Width > constraint.Width) && // We are too wide to fit on this row
((largestY + element.DesiredSize.Height) <= MaxHeight)) // We have enough room for this on the next row
{
y = largestY;
x = element.DesiredSize.Width;
}
else
{
/* 1) Always place the first item on a row even if width doesn't allow it
* otherwise:
* 2) Keep placing on this row until we reach our width constraint
* otherwise:
* 3) Keep placing on this row if the max height is reached */
x += element.DesiredSize.Width;
}
largestY = Math.Max(largestY, y + element.DesiredSize.Height);
largestX = Math.Max(largestX, x);
});
measure(Header);
foreach (FrameworkElement child in InternalChildren)
{
measure(child);
}
measure(Footer);
return new Size(largestX, largestY);
}
protected override Size ArrangeOverride(Size finalSize)
{
double x = 0.0;
double y = 0.0;
double largestY = 0.0;
double largestX = 0.0;
var arrange = new Action<FrameworkElement>(element =>
{
if (x > 0 && // Not the first item on this row
(x + element.DesiredSize.Width > finalSize.Width) && // We are too wide to fit on this row
((largestY + element.DesiredSize.Height) <= MaxHeight)) // We have enough room for this on the next row
{
y = largestY;
element.Arrange(new Rect(new Point(0.0, y), element.DesiredSize));
x = element.DesiredSize.Width;
}
else
{
/* 1) Always place the first item on a row even if width doesn't allow it
* otherwise:
* 2) Keep placing on this row until we reach our width constraint
* otherwise:
* 3) Keep placing on this row if the max height is reached */
element.Arrange(new Rect(new Point(x, y), element.DesiredSize));
x += element.DesiredSize.Width;
}
largestY = Math.Max(largestY, y + element.DesiredSize.Height);
largestX = Math.Max(largestX, x);
});
arrange(Header);
foreach (FrameworkElement child in InternalChildren)
{
arrange(child);
}
arrange(Footer);
return new Size(largestX, largestY);
}
}
}
在 XAML 中的用法:
<ItemsControl ItemsSource="{Binding SomeItems}" ItemTemplate="{StaticResource SomeTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<extras:HeaderedFooteredPanel>
<extras:HeaderedFooteredPanel.Header>
<TextBlock Text="Header" />
</extras:HeaderedFooteredPanel.Header>
<extras:HeaderedFooteredPanel.Footer>
<TextBlock Text="Footer" />
</extras:HeaderedFooteredPanel.Footer>
</extras:HeaderedFooteredPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
【问题讨论】:
-
我在您的代码中看不到任何内容来覆盖
OnRender()方法以实际绘制页眉和页脚项目。请在您上面发布的代码中解释您希望导致这种情况发生的原因是什么? -
我怀疑问题是由于这样一个事实,虽然,是的,您正在为使用 Measure 和 Arrange 覆盖的项目腾出空间,但该列表仍然数据绑定到 {Binding SomeItems}。因此,当 WPF 进入放下项目时,它仍然只处理绑定列表中的项目。我可能错了,但是如果您的初始方法不起作用,您可能会考虑另一种方法。如果您使用包含页眉和页脚的可扩展元素包装您的列表怎么办?换句话说,不要将页眉和页脚作为列表的一部分,而是将其作为控件的一部分。
-
@PeterDuniho 提供给 OnRender() 方法的 DrawingContext 似乎只支持非常基本的渲染命令。当然,您不必为标准 WPF 控件重新编写呈现代码,但我没有看到自己绘制它们的方法。正如我在原来的帖子中所说,这是我第一次尝试创建自定义控件,但在找到有关如何创建这样的自定义控件的有用文档或示例方面并不是很成功。
-
@ryancdotnet 我同意问题可能是 WPF 不知道它需要放置页眉和页脚控件,(也许像 PeterDuniho 建议的那样在 OnRender() 方法中不知何故?)。我不确定您不将页眉和页脚作为列表的一部分但仍将其作为控件的一部分是什么意思。这是我的初衷!
标签: c# wpf xaml custom-controls panel