我正在创建一个图形控件。我为添加 x 和 y 轴计数标签所做的是我正在向每个计数标记添加一个文本块并显示与该计数标记相关的值。
但是当我需要从数据库加载数据并再次重绘文本块并刷新图形区域时,我无法删除旧文本块,它们仍在图形窗格中。
为了克服这个问题,我想将文本块放在组框旁边,当重新绘制图形窗格以删除组框元素并重新放置它们时。
这种方法正确吗?请告诉我如何在类后面的代码中将元素放入组框?请告诉我他们是否可以解决我的问题。
问候,兰加纳。
在 WPF 中,大多数问题都有许多解决方案。我将讨论您的问题的三种可能的解决方案 - 您描述的一个和其他两个。您可以决定哪个最适合您。
解决方案 1:使用 TextBlock 对象显示标签
听起来您有一个Canvas
,并且您正在TextBlock
为每个刻度线添加一个。如果性能不太重要并且您不能使用数据绑定,这是一个可行的解决方案。
在这种情况下,有两种方法可以删除 TextBlock:
您可以保留一个List<TextBlock>
包含上次创建标签时创建的 TextBlocks 的所有 Textblocks 列表。每当您重新创建标签时,运行此列表并从包含面板(画布)中删除列表中的每个 TextBlock
您可以创建一个新的 Canvas 并将 TextBlocks 放在上面,然后在重新标记时删除整个 Canvas。
这是第二种技术的示例,因为它的效率略高:
class MyGraphBuilder
{
Canvas _labelCanvas;
...
void AddLabels()
{
// Remove old label canvas, if any
if(_labelCanvas!=null)
_graphCanvas.Children.Remove(_labelCanvas);
// Add new label canvas
_labelCanvas = new Canvas();
_graphCanvas.Children.Add(_labelCanvas);
// Create labels
foreach(...)
{
...
_labelCanvas.Add(new TextBlock ...
}
...
}
}
解决方案 2:使用数据绑定
在 WPF 中,您无需编写任何代码即可创建许多图形!WPF 内置的数据绑定足以创建相对复杂的条形图等。
以下是使用数据绑定创建简单条形图的示例:
<ItemsControl ItemsSource="{Binding myData}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DockPanel>
<TextBlock Width="50" Text="{Binding Label}"/>
<Rectangle VerticalAlignment="{Stretch}" Width="{Binding Value}">
<Rectangle.LayoutTransform>
<ScaleTransform ScaleX="10" /> <!-- Scale factor here, can be binding too -->
</Rectangle.LayoutTransform>
</Rectangle>
<TextBlock Text="{Binding Value}" FontSize="8"/>
</DockPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
通过使用水平布局的第二个 ItemsControl 可以将数字标签添加到水平轴,并使用其数据模板固定宽度并显示数字和刻度线。
解决方案 3:使用低级绘图类
DrawingGroup
通过构造一个对象并向其添加GeometryDrawing
和对象来构建您的图形GlyphRunDrawing
,然后将其放入DrawingGroup
内部DrawingVisual
并将其添加到您的 main Panel
。
您应该使用一个GeometryDrawing
或GlyphRunDrawing
用于共享给定画笔和钢笔的每组项目。例如,如果您的坐标轴和刻度线的颜色和宽度都相同,GeometryDrawing
则为它们创建一个,但如果每个刻度线的颜色不同,则创建多个GeometryDrawing
对象。
您将为Geometry
每个GeometryDrawing
. 为了获得最佳效率,它应该是 a StreamGeometry
,但其他 Geometry 类也可以很好地工作,可能更易于使用,并且可以在 XAML 中初始化。创建一个PathGeometry
orEllipseGeometry
可能你已经很熟悉了,所以我将专注于创建一个StreamGeometry
. 为此,您可以Open
在语句中调用该方法using()
,然后写入返回的上下文。这是一个例子:
Geometry BuildAxesAndTicksGeometry()
{
// First create geometry
var geometry = new StreamGeometry();
using(var context = geometry.Open())
{
// Horizontal axis
context.BeginFigure(new Point(0,0), false, false);
context.LineTo(new Point(_width, 0), true, false);
// Vertical axis
context.BeginFigure(new Point(0,0), false, false);
context.LineTo(new Point(0, _height), true, false);
// Horizontal ticks
for(int i=0; i<_nTicksHorizontal; i++)
{
context.BeginFiture(new Point(i * _tickSpacing, -10), false, false);
context.LineTo(new Point(i * _tickSpacing, 10), true, false);
}
// Do same for vertical ticks
}
// Now add it to a drawing
return new GeometryDrawing { Geometry = geometry, Stroke = _axisPen };
}
Drawing BuildDrawing()
{
var mainDrawing = new DrawingGroup();
mainDrawing.Add(BuildAxesAndTicksGeometry());
... // Add other drawings, including one or more for the data
return mainDrawing;
}
void UpdateDrawing()
{
myDrawingVisual.Drawing = BuildDrawing(); // where myDrawingVisual is defined in the XAML
}
解决方案比较
对于大多数情况,我会推荐解决方案 2 或 3,原因如下:
在您的情况下,如果您已经在解决方案 1 中投入了大量工作,您可能希望坚持使用它,即使它可能不是最好的。