I'm developing an app that creates a timeline in run time. So I created a Grid (not a datagrid) and I wanna put some time stamps in equal spaces (60 pixels/hour - 1 pixel/minute).
I already know how to create the columns programmatically:
public void ColumnCreator(double totalInterval, double divPattern)
{
int divisionPattern = Convert.ToInt16(divPattern);
if (divisionPattern < totalInterval)
{
for (int i = 0; i < totalInterval; i += divisionPattern)
{
internalGrid.ColumnDefinitions.Add(new ColumnDefinition() {
Width = new GridLength(divisionPattern, GridUnitType.Pixel) });
}
}
}
but these lines are not synchronized. Does anybody have a solution for me?
Thanks in advance 8-)
As was pointed out especially for real time visualization Grid may be the worse choice you can made. Grid is responsible for containing controls layout management and has a heavy impact on performance.
Use a Canvas to draw a stuff on it. Use Shape and Path for drawing. To achive a reasonable performance on real time UI you need to read at least this link: http://msdn.microsoft.com/en-us/library/ms747393.aspx
Do not use Pixels but provided by WPF Units, which are device independent way to declaring the dimension of something in WPF. So your drawing will maintain a proportion on different monitors screen sizes and dpi.
Related
I'm not sure if I titled the question correctly so it would be better if I explained what I'm trying to do. I want to add some images on chart control and
around their to draw graphics.
I want to display the layout of the sensors on the coordinate plane defined by coordinates, while noting the location of geographic objects (forest, river, etc.). These objects will be images which I want to add to the chart/
How can I do it? It is possible?
If you show us an example we may be able to help to find the best way.
There are several options.:
You can place image controls like PictureBox or Panel on the Chart by adding them to the chart's Controls collection
You can draw them in the Pre- or PostPaint event
You can assemble a BackImage that contains all the Images you want to place around the chart.
You can add ImageAnnotations to the chart. (Recommended)
The latter obviously is the one that is best integrated.
Here is an example:
We start by adding the images we want to use to the Chart's Images collection:
List<string> imgFiles = new List<string>()
{ yourImageFileName1, ...};
for (int i = 0; i < imgFiles.Count; i++)
{
Image img = Bitmap.FromFile(imgFiles[i]);
chart1.Images.Add(new NamedImage("Image" + i, img));
}
Note the NamedImage class used here. It allows you to refer to the images by a string; pick better names! Maybe Path.GetFileNameWithoutExtension(imgFiles[i]) - Also note that you must not Dispose of the Images or else they will disappear!
Next make make a little room at the right side of the chart by reducing the ChartArea's size:
ChartArea ca = chart1.ChartAreas[0];
ca.Position = new ElementPosition(5,5,70,90);
Note the values are percentages of the Chart's ClientSize, so they will grow and shrink when resizing the Chart!
Finally we can add them all. You will want to add them at specific positions. I add them at some space to the right and also make them moveable:
foreach (NamedImage img in chart1.Images)
{
ImageAnnotation ia = new ImageAnnotation();
ia.Image = img.Name;
ia.AllowMoving = true;
ia.X = 77;
ia.Y = 15 * chart1.Images.IndexOf(img) + 5;
chart1.Annotations.Add(ia);
}
Now you should see the Annotions. And if you add this event:
private void chart1_AnnotationPositionChanging(object sender,
AnnotationPositionChangingEventArgs e)
{
testLabel.Text = e.Annotation.X + " " + e.Annotation.Y;
}
..you will see just what the numbers for the best position are. Eventually you will not keep them moveable, of course..
Note the the Annotations' position is also in percentages, so they will move along nicely, when the chart get resized! You may also scale the Images by setting the Width and Height; this is a little tricky, as it will also be in percent (and not as the docs falsely state in pixels). You would probably want to loop over the ImageAnnotations and rescale them in the Resize event..: ia.Height = ia.Width * chart1.Width / chart1.Height;
Also note that there are other ways to position annotations, like anchored to datapoints, but this seem the best for a static adornment.
I've write a program that construct a 2D matrix from txt file, and build a winforms panel with X*Y labels wich contains a char, coordinates, color and border (if select it).
it is my DrawGrid routine:
Container.SuspendLayout();
for (int y = 0; y < template.Matrix.GetLength(1); y++)
{
for (int x = 0; x < template.Matrix.GetLength(0); x++)
{
var curLabel = new LabelTemplate(template.Matrix[x, y].Content, x, y, spacing);
_templateCells.Add(curLabel);
Container.Controls.Add(curLabel);
}
}
Container.ResumeLayout();
whit it I view a txt file in my form, and select a row or columns or area with mouse, manipulate and save new text files from it getting content and coordinates from my LabelTemplate object (extends Label).
I've always test my program with a little txt files in input.
Today i've tested with a big txt file (9000 rows * 50 columns) and i've reached a maximun handles of a windows form application.
(an Win32 Exception is launched during Container.Controls.Add(curLabel)).
Googling i've found that the limit of controls in winforms application is 10000 handles.
Also view a lot of label on my form (if 10000 is a modificable value), performance are very bad (if i scroll container panel, i wait a lot of time to view results)!
There is a way or control that help me?
I think also to GDI+, but what is the right way for you? Any suggestions?
Thanks in advance
I think you should use DataGridView control.
If your amount of data is too big, you can limit the number of items and add some control to select the start of the region that you are viewing (like a NumericUpDdown or a TrackBar). Every time you change the start index, you reload your data to the DataGridView.
Sample of how to fill a DataGridView from an array: "How do I show the contents of this array using DataGridView?".
Another solution would be to use WPF, which has built-in UI Virtualization, therefore supports much bigger datasets without having any performance impact.
It is not practical to use labels for cells of a grid-like control. As agent5566 suggested, you can use DataGridView control for a fast approach or if you want full control and better performance, you can use a single UserControl and paint everything on it, handle keystrokes, simulate focus on cells (if needed) and so on.
I have WinForms application. I made an user control, which draws a map from coordinates of ca 10k lines. Actualy, not all lines are straight ones, but when the map is zoomed out fully - Bezier curves are irrelevant and are replaced with straight lines.
When the map is zoomed, I have smaller number of lines and curves, so the drawing is fast enough (below 15ms). But when it's zoomed out fully - I need to draw all lines (because all fit into viewport). This is painfully slow. On my very fast machine it takes about 1000ms, so on slower machines it would be an overkill.
Is there a simple way to speed up the drawing?
I use Graphics object for drawing and I set Graphics.Scale property to my map fit into my control.
Does this slow things down?
I use Graphics.TranslateTransform() to ensure the whole map is visible.
Both scale and translate is set only once in OnPaint() event handler.
Then there is a loop which draws ca 10k lines. And I just see them drawing on the screen.
Maybe WPF container would help?
Well, I could probably simplify the map to merge some lines, but I wonder if it's worth the effort. It would complicate the code greatly, would introduce much more calculations, use extra memory and I don't know if at the end of the day it would be considerably faster.
BTW, I tested that processing of all lines (converting from one structure to another with some aditional calculations) takes ca 10ms on my machine. So - the drawing alone costs 100x more time.
EDIT:
Now here's the new problem. I've turned double buffering on with:
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer, true);
Here's my messy OnPaint() handler:
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
if (Splines == null) return;
var pens = new[] {
new Pen(TrackColor),
new Pen(TrackColor),
new Pen(RoadColor),
new Pen(RiverColor),
new Pen(CrossColor)
};
var b = Splines.Bounds;
Graphics g = e.Graphics;
g.PageScale = _CurrentScale;
g.TranslateTransform(-b.Left, -b.Top);
int i = 0;
foreach (var s in Splines) {
g.DrawLine(pens[s.T], s.A, s.D);
if (++i > 100) break;
//if (s.L) g.DrawLine(pens[s.T], s.A, s.D);
//else g.DrawBezier(pens[s.T], s.A, s.B, s.C, s.D);
}
foreach (var p in pens) p.Dispose();
}
Take my word the code works, if I only remove OptimizedDoubleBuffer from styles. When double buffering is on the handler executes properly, each DrawLine is executed with correct params. But the graphics is not displayed. CPU usage during resizing is next to zero. Like all DrawLine calls were ignored. What's happening here?
In a related post I've seen recently but can't find, the OP claimed to have seen a large speed-up when switching his control to use double-buffering. Apparently there's a substantial hit for drawing stuff to the screen.
Another thing you could try is decimating the point lists in the lines you draw when zoomed out. Instead of doing the decimation each frame, you could do it only once each time the zoom is changed.
Try double buffering as a possible solution or try to reduce the number of lines. Only testing will give you an answer for your application.
Winforms Double Buffering
Double buffering with Panel
The feasibility of this really depends on if you're using anti-aliasing, if the thing can rotate, if the thickness has to be very accurate, etc.
However you can always draw all the lines into a bitmap, then simply redraw the bitmap unless the map data itself has actually changed. Of course then you get into having different bitmaps for different zoom levels, hiding and showing them, multiple bitmaps in a grid for the high details etc.
It's definitely not ideal, but if you really do need to draw thousands of lines on a 20ms refresh though.. it might be your only real option.
Or you could use lower level of drawing, outside GDI+. one such example is SlimDX. This wrapper allows you to create a directX device write from your windows controls and forms. Once DirectX is in action, the speed can increase up to several times.
2ndly, when drawing on win panel even with DoubleBuffered enabled, you always have to Invalidate the panel which asks the Environment to call the OnPaint event which actual draws using the system provided Graphics object. This invalidation usually requires a timer with fire rate more than 30 to five you a feeling of smooth playback. Now, when the load increases, the subsequent timer event is delayed since everything is happening under a single thread. And the timer must Yield the thread for around 25ms after every fire (windows OS limitation). Cross Thread access ia not allowed, using which a System.Threading.Timer could have prevent this jitter.
See this link for an example where I have tried to transfer my existing GDI code to DirectX. The code uses a lot of graphics attributes which i have incorporated in the wrapper which can draw on both GDI and DirectX.
https://drive.google.com/file/d/1DsoQl62x2YeZIKFxf252OTH4HCyEorsO/view?usp=drivesdk
I'm doing some programming in WPF. My goal is creat a TRPG(Text-RPG), so a map is definitely needed.
I got a PNG picture that can be separated in 5×5.
I want to separate this PNG, the add some Code on these little squares, for example, the picture's code in left bottom corner is (0, 0).
(I'll add functions to make sure I'll be able to travel on this MAP later.)
Now I want to figure out how can I separate the PNG?
Then add it in my code?
Creat a new .cs? .edmx?
I'd recommend taking a step back and concisely identify exactly how you plan to represent geographic area in your application.
If you are planning to have an NxM grid to display geographic area of the "map" in your app, you might want to instead just draw the map's "image" on the background of your window. You can then overlay transparent controls on top of the actual map (such as labels for the individual grid cells).
Fortunately, WPF provides a very applicable layout mechanism for you to do this, a Grid.
See http://wpftutorial.net/GridLayout.html.
You can create a dynamic bitmap array. and define all of the bitmaps and just run a foreach loop to draw them.
Bitmap[] bitmapArray = new Bitmap[width * height]; //the size of your map
for(int i = 0; i <= bitmapArray.Length - 1; i++)
{
//Define the bitmaps here
}
foreach(Bitmap b in bitmapArray)
{
//Drawing Code
}
I'm writing an interface that features a large (~50000px width) "canvas"-type area that is used to display a lot of data in a fairly novel way. This involves lots of lines, rectangles, and text. The user can scroll around to explore the entire canvas.
At the moment I'm just using a standard Canvas panel with various Shapes placed on it. This is nice and easy to do: construct a shape, assign some coordinates, and attach it to the Canvas. Unfortunately, it's pretty slow (to construct the children, not to do the actual rendering).
I've looked into some alternatives, it's a bit intimidating. I don't need anything fancy - just the ability to efficiently construct and place objects in a coordinate plane. If all I get are lines, colored rectangles, and text, I'll be happy.
Do I need Geometry instances inside of Geometry Groups inside of GeometryDrawings inside of some Panel container?
Note: I'd like to include text and graphics (i.e. colored rectangles) in the same space, if possible.
Shapes are fairly heavy-weight. You should probably look into using graphics paths. Those are much more efficient when the user doesn't need to interact with individual parts of the drawing - and sometimes even then.
Try not to creating shapes that you do not need, and recycle ones that you already have. Basically no user will see the whole screen, so do NOT have the shapes that are out of sight. Don't create new ones f you can avoid - basically keep shapes falling out in a "ready" list, so you can reuse them.
If you have a large number of Shape instances, you could perhaps construct them asynchronously (on a worker thread) and queue up the actual Add operations via the Dispatcher. The idea here is that the UI won't be complete right away, but the user can start interacting right away, while elements continue loading.
EDIT: The above is incorrect. WPF does require that Visual elements be created on the UI thread. You can still accomplish this sort of 'lazy' visual loading using a pattern like this:
private Random _random = new Random();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Thread testThread = new Thread(TestThread);
testThread.Start();
}
private void TestThread()
{
for (int i = 0; i < 1000; i++)
{
Dispatcher.BeginInvoke((Action)CreateShape);
}
}
private void CreateShape()
{
var shape = new Rectangle();
shape.Width = _random.Next(10, 50);
shape.Height = _random.Next(10, 50);
shape.Fill = new SolidColorBrush(Colors.Red);
Canvas.SetLeft(shape, _random.Next(0, 400));
Canvas.SetTop(shape, _random.Next(0, 200));
LayoutRoot.Children.Add(shape);
}
This basically queues up tasks to be run 'asynchronously' on the UI thread (i.e. whenever the message pump is being serviced), so you can maintain responsiveness while performing the 'long' UI update.