例如,实施TwoColumnStackPanel
。顾名思义,通用StackPanel
只能在一列中堆叠元素,而我的TwoColumnStackPanel可以在两列中堆叠元素。
TwoColumnStackPanel应该在两列中均匀分配元素。如果4个元素,左边2和右边2; 5个元素,左2和右3。
我认为TwoColumnStackPanel实际上是两个并排的StackPanels,可以使用现有的StackPanel 实现吗?
class TwoColumnStackPanel : Panel
{
private readonly StackPanel leftPanel;
private readonly StackPanel rightPanel;
public TwoColumnStackPanel()
{
leftPanel = new StackPanel();
rightPanel = new StackPanel();
}
protected override Size MeasureOverride(Size availableSize)
{
int size = InternalChildren.Count;
int leftCount = size / 2;
int rightCount = size - leftCount;
//Load elements to left stackpanel.
int index = 0;
leftPanel.Children.Clear();
for (int s = 0; s < leftCount; s++)
{
leftPanel.Children.Add(InternalChildren[index + s]);
}
//Load elements to right stackpanel.
index += leftCount;
rightPanel.Children.Clear();
for (int s = 0; s < rightCount; s++)
{
rightPanel.Children.Add(InternalChildren[index + s]);//error
}
//Measure the two stackpanel and the sum is my desired size.
double columnWidth = availableSize.Width / 2;
leftPanel.Measure(new Size(columnWidth, availableSize.Height));
rightPanel.Measure(new Size(columnWidth, availableSize.Height));
return new Size(leftPanel.DesiredSize.Width + rightPanel.DesiredSize.Width, Math.Max(leftPanel.DesiredSize.Height, rightPanel.DesiredSize.Height));
}
protected override Size ArrangeOverride(Size finalSize)
{
leftPanel.Arrange(new Rect(0,0,leftPanel.DesiredSize.Width,leftPanel.DesiredSize.Height));
rightPanel.Arrange(new Rect(leftPanel.DesiredSize.Width,0,rightPanel.DesiredSize.Width,rightPanel.DesiredSize.Height));
return finalSize;
}
}
上面的代码在标签行引发异常。怎么解决?我是以正确的方式实施的吗?
答案 0 :(得分:1)
你不应该使用面板来实现面板
更好的方式(不完美,但它会给你的想法):
class TwoColumnStackPanel : Panel
{
protected override Size MeasureOverride(Size availableSize)
{ //split the size
Size halfPanelSize = new Size(availableSize.Width / 2, availableSize.Height / 2);
Size secondHalfPanelSize = new Size(availableSize.Width - halfPanelSize.Width, availableSize.Height - halfPanelSize.Height);
int firstHalf = InternalChildren.Count / 2;
for (int i = 0; i < firstHalf; i++) //measure the first column
{
InternalChildren[i].Measure(halfPanelSize);
Debug.WriteLine(InternalChildren[i].DesiredSize);
}
for (int i = firstHalf; i < InternalChildren.Count; i++)//measure the second column
{
InternalChildren[i].Measure(secondHalfPanelSize);
Debug.WriteLine(InternalChildren[i].DesiredSize);
}
return availableSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
Size halfPanelSize = new Size(finalSize.Width / 2, finalSize.Height / 2);
Size secondHalfPanelSize = new Size(finalSize.Width - halfPanelSize.Width, finalSize.Height - halfPanelSize.Height);
int firstHalf = InternalChildren.Count / 2;
Point location = new Point();
for (int i = 0; i < firstHalf; i++)
{// arrange from (0,0) and add the height
InternalChildren[i].Arrange(new Rect(location.X, location.Y, halfPanelSize.Width, InternalChildren[i].DesiredSize.Height));
location.Y += InternalChildren[i].DesiredSize.Height;
}
location.X = halfPanelSize.Width; // move to the next column
location.Y = 0;
for (int i = firstHalf; i < InternalChildren.Count ; i++)
{// arrange from (firts column width,0) and add the height
InternalChildren[i].Arrange(new Rect(location.X, location.Y, secondHalfPanelSize.Width, InternalChildren[i].DesiredSize.Height));
location.Y += InternalChildren[i].DesiredSize.Height;
}
return finalSize;
}
}
答案 1 :(得分:1)
我知道这是旧的,但实际上有一种更简单的方法可以使用两个(或更多)现有面板的现有功能来获得所需的行为。
首先,子类UniformGrid,而不是普通的Panel。当你想要进行安排时,您只需要对一个面板进行子类化。你没有。您希望内部堆栈面板能够做到这一点。您只是将它们分发到内部面板。 (您还提到了一个停靠面板,但这意味着您还必须指定停靠的附加属性,但无论哪种方式,此代码都完全相同。
*注意:您可以随意使用任何面板作为根。我刚刚选择了UniformGrid,因此StackPanels将是并排的。但是您可以将任何面板嵌套在任何其他面板中。完全取决于你。
接下来,在新子类的构造函数中,将两个内部StackPanel添加到UniformGrid,一个在左边,一个在右边。请注意,这意味着您的'Children'集合实际上将返回两个StackPanel,而不是您添加的控件。这没关系,因为我们不再使用Children属性了。您将创建自己的属性以供您提供的孩子使用。
现在就这样做。创建一个名为StackPanelChildren的新属性,类型为ObservableCollection,然后在您的类上添加属性[ContentProperty(“StackPanelChildren”)],告诉XAML处理器控件的开始和结束标记插入到中> property,而不是普通的Children属性。接下来,在类的构造函数中添加Collection Changed处理程序,以便您知道何时添加或删除子项。然后,只需在该处理程序中根据需要从两个内部StackPanel中添加或删除项目。
要真正完成,您可能希望覆盖CreateUIElementCollection(它支持实际的Children属性),因此您可以返回它的只读版本,以便人们不会弄乱您的内部StackPanel。我会把它留给你来弄清楚那部分,因为它实际上并不需要。
这是一些伪代码(从我的头部输入,所以它可能无法编译,但你明白了......
[ContentProperty("StackPanelChildren")]
public class TwoColumnStackPanel : UniformGrid
{
private readonly StackPanel leftStackPanel = new StackPanel();
private readonly StackPanel rightStackPanel = new StackPanel();
public TwoColumnStackPanel()
{
this.Rows = 1;
this.Columns = 2;
this.Children.Add(leftStackPanel);
this.Children.Add(rightStackPanel);
StackPanelChildren.CollectionChanged += StackPanelChildren_CollectionChanged;
}
// Note: You should make this a read-only Dependency property
// I'm just doing it this way for brevity in typing
private readonly ObservableCollection<UIElement> _stackPanelChildren = new ObservableCollection<UIElement>();
public ObservableCollection<UIElement> StackPanelChildren
{
get{ return _stackPanelChildren; }
}
private void StackPanelChildren_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Add your child item distribution logic here. Make sure to account for
// all new items, all removed items, and the Reset action which
// clears all items but doesn't actually provide them on the argument
// since you just assume 'everything must go!'
}
}
其他信息:
您可以添加类似于新StackPanelChildren属性的其他属性。但是只有一个可以用作默认内容。但是,如果明确地将它们标记出来,您可以在XAML中指定它们中的任何一个。例如,出于某种原因,你想要第二个项目集合,也许它们只会进入左侧面板。该属性称为AdditionalLeftPanelChildren。您只需在代码(ObservableCollection)中以完全相同的方式定义它并像这样访问...
<MyCustomPanel>
<TextBlock Text="I'm in the normal content for the StackPanels." />
<TextBlock Text="So am I!" />
<MyCustomPanel.AdditionalLeftPanelChildren>
<TextBlock Text="I go to the other property! Woot!" />
</MyCustomPanel.AdditionalLeftPanelChildren>
</MyCustomPanel>