我需要编写一个WPF自定义控件,它由4个其他FrameworkElement派生的自定义类组成。通常我会使用Grid来安排它们,但Grid有一些布局问题,我必须编写自己继承自Panel的类。当然,我的自定义控件可以从Panel继承,但是会暴露子和其他面板功能,它不应该。
我需要一个自定义控件,它只显示我添加的一些属性和典型的FrameworkElement属性。我正在考虑我的自定义控件继承自Control。但我不想使用ControlTemplates。我想创建Panel并从代码后面添加FrameworkElements。怎么办呢?
让我总结一下问题: 1)控制是从中派生出来的最佳类吗?
2)如何在不使用ControlTemplate的情况下将FrameworkElements添加到Control派生类?
答案 0 :(得分:1)
是。控制是最好的等级。
您需要覆盖方法GetVisualChild()和属性VisualChildrenCount。我在考虑你想要4个自定义框架元素作为你的孩子,并在构造函数中初始化该集合。然后代码如下,
private List<UIElement> visualChildren;
protected override int VisualChildrenCount
{
get
{
return this.visualChildren.Count;
}
}
protected override Visual GetVisualChild(int index)
{
return this.visualChildren[index];
}
您还可以覆盖MeasureOverride和ArrangeOverride方法,为子项分配大小并分别安排子项。
答案 1 :(得分:0)
这是根据XAML Lover和Marat Khasanov的建议解决我的问题的实现。 TestCustomControl将2个TextBox对角放置。右上角的那个使用尽可能多的宽度和尽可能多的高度。左下角的那个使用尽可能多的高度和尽可能宽的宽度。
+------------+--------+
| | |
| |TextBox1|
| | |
+------------+--------+
| TextBox2 | |
+------------+--------+
这种排列对于Grid来说是一个问题,因为要测量一个TextBox的大小,它需要知道另一个TextBox的大小,这是不可能的。 Grid通过使用不确定空间测量第一个TextBox,然后使用第一个TextBox的大小测量另一个TextBox,然后使用第二个的大小再次测量第一个TextBox来解决这个难题。这会导致各种问题,TestCustomControl会完全阻止这些问题。
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
namespace TestControl {
public class TestCustomControl: Control {
VisualCollection visualCollection;
Rectangle RectangleToLeft;
TextBox TextBoxTopRight;
TextBox TextBoxBottomLeft;
Rectangle RectangleBottomRight;
public TestCustomControl() {
RectangleToLeft = new Rectangle { Fill = Brushes.LightYellow };
TextBoxTopRight = new TextBox {Text = "TR", FontSize = 22, HorizontalAlignment=HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Stretch };
TextBoxBottomLeft = new TextBox {Text = "BL", FontSize = 22, HorizontalAlignment=HorizontalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Center };
RectangleBottomRight = new Rectangle {Fill = Brushes.LightBlue };
//new UIElementCollection(this, logicalParent);
visualCollection = new VisualCollection(this);
visualCollection.Add(RectangleToLeft);
visualCollection.Add(TextBoxTopRight);
visualCollection.Add(TextBoxBottomLeft);
visualCollection.Add(RectangleBottomRight);
}
protected override int VisualChildrenCount {
get {
return visualCollection.Count;
}
}
protected override System.Windows.Media.Visual GetVisualChild(int index) {
return visualCollection[index];
}
protected override System.Windows.Size MeasureOverride(System.Windows.Size constraint) {
TextBoxTopRight.Measure(constraint);
TextBoxBottomLeft.Measure(constraint);
Size returnedSize = constraint;
if (double.IsInfinity(constraint.Height)) {
returnedSize.Height = TextBoxTopRight.DesiredSize.Height + TextBoxBottomLeft.DesiredSize.Height;
}
if (double.IsInfinity(constraint.Width)) {
returnedSize.Width = TextBoxTopRight.DesiredSize.Width + TextBoxBottomLeft.DesiredSize.Width;
}
return constraint;
}
protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeBounds) {
double remainingWidth = Math.Max(0, arrangeBounds.Width - TextBoxTopRight.DesiredSize.Width);
double remainingHeight = Math.Max(0, arrangeBounds.Height - TextBoxBottomLeft.DesiredSize.Height);
RectangleToLeft.Arrange(new Rect(0, 0, remainingWidth, remainingHeight));
TextBoxTopRight.Arrange(new Rect(remainingWidth, 0, TextBoxTopRight.DesiredSize.Width, remainingHeight));
TextBoxBottomLeft.Arrange(new Rect(0, remainingHeight, remainingWidth, TextBoxBottomLeft.DesiredSize.Height));
RectangleBottomRight.Arrange(new Rect(remainingWidth, remainingHeight, TextBoxTopRight.DesiredSize.Width, TextBoxTopRight.DesiredSize.Height));
return arrangeBounds;
}
protected override Geometry GetLayoutClip(Size layoutSlotSize) {
if (ClipToBounds)
return new RectangleGeometry(new Rect(RenderSize));
else
return null;
}
}
}