need to add textBlocks to a group box and clear when data is loaded in a graph control
I'm creating a graph control. what i'm doing for adding x and y axis tally labels is i'm adding a text block to each tally mark and show the value related to that tally mark.
but when i need to load data form开发者_开发知识库 the database and redraw the textbolcks again and refresh the graph area i can't remove the older textblocks they are still on the graph pane.
to overcome this problem i thought to put the text blocks in side a group box and when graph pane is redrawn to delete the group box elements and put them again..
is this approach correct? please tell me how to put elements to groupbox in code behind class? and please tell me if their is any other solution to my problem.
regards, rangana.
In WPF there are many solutions to most problems. I will discuss three possible solutions to your problem - the one you describe and two others. You can decide which will work best for you.
Solution 1: Using TextBlock objects to disply the labels
It sounds like you have a Canvas
and you're adding a TextBlock
to it for each tick mark. This is a viable solution if performance isn't too critical and you can't use data binding.
There are two ways to remove the TextBlocks in this case:
You can keep a
List<TextBlock>
containing all the Textblocks list of the TextBlocks you created the last time you created the labels. Whenever you recreate the labels, run through this list and remove each TextBlock on the list from the containing panel (the Canvas)You can create a new Canvas and put the TextBlocks on it, then delete the whole Canvas when you relabel.
Here is an example of the second technique, since it is slightly more efficient:
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 ...
}
...
}
}
Solution 2: Using data binding
In WPF you can create many graphs without writing a single line of code! WPF's built in data binding is sufficient to create relatively complex bar charts, etc.
Here is an example of using data binding to create a simple bar chart:
<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>
Numeric labels can be added to the horizontal axis by using a second ItemsControl laid out horizontally, and with its data template a fixed width and showing numbers and tick marks.
Solution 3: Using low level Drawing classes
Build your graph by constructing a DrawingGroup
object and adding GeometryDrawing
and GlyphRunDrawing
objects to it, then putting the DrawingGroup
inside DrawingVisual
and add that to your main Panel
.
You should use one GeometryDrawing
or GlyphRunDrawing
for each set of items sharing a given brush and pen. For example if your axes and tick marks are all the same color and width, create a single GeometryDrawing
for all of them, but if each tick mark is a differnet color, create multiple GeometryDrawing
objects.
You will create a Geometry
object for each GeometryDrawing
. For the best efficiency it should be a StreamGeometry
, but the other Geometry classes also work well, may be easier to use, and may be initialized in XAML. Creating a PathGeometry
or EllipseGeometry
is probably already familar to you so I'll focus on creating a StreamGeometry
. You do this by calling the Open
method in a using()
statement, then writing to the returned context. Here is an example:
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
}
Comparison of solutions
For most cases I would recommend solution 2 or 3, for these reasons:
- If the graph is simple enough to use data binding it will save you a lot of time. Go with solution 2.
- If the graph cannot be done with data binding, using Drawing objects is approximately as simple as any other technique, and can perform better. Go with solution 3.
In your case if you've already invested significant work into your Solution 1, you may want to stick with it even though it probably isn't the best.
精彩评论