WPFには、ほとんどの問題に対する多くの解決策があります。私はあなたの問題に対する3つの可能な解決策について議論します-あなたが説明するものと他の2つ。どちらが最適かを決めることができます。
解決策1:TextBlockオブジェクトを使用してラベルを表示する
があり、目盛りごとにをCanvas
追加しているようです。TextBlock
これは、パフォーマンスがそれほど重要ではなく、データバインディングを使用できない場合に実行可能なソリューションです。
この場合、TextBlockを削除する方法は2つあります。
List<TextBlock>
ラベルを最後に作成したときに作成したTextBlockのすべてのTextblockリストを含めることができます。ラベルを再作成するときはいつでも、このリストを実行して、リスト上の各TextBlockを包含パネル(キャンバス)から削除してください。
新しいCanvasを作成してTextBlockを配置し、ラベルを変更するときにCanvas全体を削除できます。
少し効率的であるため、2番目の手法の例を次に示します。
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では、コードを1行も記述せずに、多くのグラフを作成できます。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>
水平方向に配置された2番目のItemsControlを使用し、そのデータテンプレートを使用して固定幅で数字と目盛りを表示することにより、水平軸に数値ラベルを追加できます。
解決策3:低レベルの描画クラスを使用する
DrawingGroup
オブジェクトを作成し、それにオブジェクトを追加GeometryDrawing
してグラフを作成しGlyphRunDrawing
、DrawingGroup
内部に配置DrawingVisual
してメインに追加しますPanel
。
特定のブラシとペンを共有するアイテムのセットごとに1つGeometryDrawing
または使用する必要があります。GlyphRunDrawing
たとえば、軸と目盛りがすべて同じ色と幅であるGeometryDrawing
場合は、それらすべてに対して1つを作成しますが、各目盛りが異なる色である場合は、複数のGeometryDrawing
オブジェクトを作成します。
Geometry
ごとにオブジェクトを作成しますGeometryDrawing
。最高の効率を得るには、を使用する必要がありますStreamGeometry
が、他のGeometryクラスも適切に機能し、使いやすく、XAMLで初期化できます。PathGeometry
またはを作成することEllipseGeometry
はおそらくすでにあなたに馴染みがあるので、私はの作成に焦点を合わせます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をお勧めします。
- グラフがデータバインディングを使用できるほど単純な場合は、時間を大幅に節約できます。ソリューション2を使用します。
- データバインディングを使用してグラフを作成できない場合、Drawingオブジェクトの使用は他の手法とほぼ同じくらい簡単であり、パフォーマンスを向上させることができます。ソリューション3を使用します。
あなたの場合、すでにソリューション1に多大な労力を費やしている場合は、おそらく最善ではないにしても、それを使い続けることをお勧めします。