Drawing a chart in WPF C# design questions
I had a project a month ago where I drew a stock chart in an application using Windows Forms. I did this by creating a bitmap that would stretch to the dimensions of the window. This would allow my chart to resize with the window.
I am now expanding the project using WPF. I have been trying to work on my design for the project, but I cant seem to get开发者_运维技巧 any idea on the best way to do the same chart. I have looked at canvases, grids, and a few other controls. I thought I was on the right track with the canvas, but when I would resize the window, my drawing would stay in the same spot. I guess the point of my post tonight is to get some ideas to help me brainstorm a design for my project.
All advice and questions are appreciated.
Thanks, Joseph
(Realizing this addresses at best a subset of this fairly old question, since it's only one chart type...)
Just fwiw, creating a bar graph in a Grid
as Ed suggests is pretty straightforward. Here's a quick and dirty version:
Add a Grid
to your Window
's XAML. Just for testing, here's one that fills the Window
entirely.
<Grid>
<Grid
Name="myGrid"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Width="auto"
Height="auto"
Margin="10,10,10,10"
/>
</Grid>
Now insert these two utility functions somewhere in your project. These provide simple, single-color columns and unstyled, but centered, label text for the x-axis.
I think the only nasty kludge is the maxHeight
in the _placeSingleColorColumn
call.
Worth mentioning: I don't have labels for the y-axis in this quick & dirty version.
private void _placeSingleColorColumn(Grid grid, Color color, int height, int colNum, int maxHeight)
{
Brush brush = new SolidColorBrush(color);
Rectangle rect = new Rectangle();
rect.Fill = brush;
Grid.SetColumn(rect, colNum);
Grid.SetRow(rect, maxHeight - height);
Grid.SetRowSpan(rect, height);
grid.Children.Add(rect);
}
private void _createLabels(Grid grid, string[] labels)
{
RowDefinition rowDefnLabels = new RowDefinition();
grid.RowDefinitions.Add(rowDefnLabels);
for (int i = 0; i < labels.Length; i++)
{
TextBlock block = new TextBlock();
block.Text = labels[i];
block.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
Grid.SetColumn(block, i);
Grid.SetRow(block, grid.RowDefinitions.Count);
grid.Children.Add(block);
}
}
That's really it. Here's some insanely quickly and dirty example code to create a 10 by 10 grid with some sample data.
public void createGrid10x10()
{
Random random = new Random();
for (int i=0; i<10; i++)
{
ColumnDefinition colDef = new ColumnDefinition();
myGrid.ColumnDefinitions.Add(colDef);
RowDefinition rowDef = new RowDefinition();
myGrid.RowDefinitions.Add(rowDef);
Color color = i % 2 == 0 ? Colors.Red : Colors.Blue;
_placeSingleColorColumn(this.myGrid, color, random.Next(1,11), i, 10);
}
string[] aLabels = "Dogs,Cats,Birds,Snakes,Rabbits,Hamsters,Horses,Rats,Bats,Unicorns".Split(',');
_createLabels(this.myGrid, aLabels);
}
Add one line to your MainWindow
constructor, and you're done, afaict.
public MainWindow()
{
InitializeComponent();
this.createGrid10x10();
}
Now you've got a bar graph that'll resize and stay proportional as the window is resized, etc.
Adding more labels (bar values on top, y-axis labels, etc) should be pretty straightforward if you understand the above. Just throw in another column and/or row, create your TextBlock
s, and place them in the right locations.
Try using a DockPanel and set LastChildFill to true. Then make your control the last child of the DockPanel.
Funny: I am just making the same thing!
I already developed a chart control, plenty of features. Now I need to renew and extend it with some other function. However, my deal is manage even 100k of points on a single chart, but keeping a good performance on a normal pc. By resizing the window will scale the chart, but not the text eventually placed on it. Also consider that I need a real-time rendering of the data incoming at least 0.5 sec.
All that has been resolved using the old-style bitmap creation, then placing it as a normal image on any wpf control. There are several limitation because there's no "living" objects as in the wpf you have, but the rendering performance is really huge compared to the wpf primitives.
So, unless you have charts with max 100 points to manage, I strongly recommend the hybrid approach. Anyway, it's a very hard task!
Cheers
I am assuming you want to draw your own chart rather than using WPF charts.
Canvas is not usually a good thing to use in WPF because it fixes objects at a specific location and size, exactly as you've seen (though I suppose you could use a Canvas with a ScaleTransform). Grid will take the size of its container, so putting a Grid into a window will make the Grid resize with the window (unless you specify a fixed Width and Height for the Grid). StackPanel will stack things and will attempt to take the minimum size of its content, so that's probably not what you want to use here.
Creating a chart layout inside a panel like a Grid isn't completely simple. If you are doing a bar chart, you could create a Column in the Grid for each bar, assign a percentage width such as star; and the columns would get larger as your Grid expanded with the window. You can use a similar trick by making each Bar a Grid, setting two columns in the Grid, and setting a third level of Grid inside the lowest column, then using percentages for the column heights (e.g., 90star and 10star for 90%, 10% heights). The bars would then grow taller as the window grows taller. You could reserve a Grid row below the bars for labels, and center them under the bars.
Line charts are trickier. You would probably want to create a GeometryDrawing of line segments, and then use a ScaleTransform bound to the window size to make it shrink and grow.
There are a lot of possibilities with WPF, but you'll need to do a bit of leaning and studying first. A book such as Adam Nathan's "Windows Presentation Foundation Unleashed" would quickly give you a lot of knowledge of WPF layout and how to proceed.
Edit: You could also use an empty panel and use its DrawingContext to draw lines, rectangles, text, ellipses, etc. at points you calculated from the current window size.
精彩评论