I have a WPF project where i had to plot some lines in the project. But when i resize the window, the lines wouldn't resize because i using the canvas coordinate to plot the line. Anyone show me how to make the line resize vary to the window size?
my code for the line:
public static void drawGridLines(MainWindow main)
{
double axisX = 10;
Line lastLine = new Line();
lastLine.X2 = axisX;
lastLine.Y2 = 15;
double y = 0;
double x = 0;
bool first = true;
int[] point = new int[10] { 1, 3, 8, 9, 9, 0, 7, 5, 4, 1 };
for (int i = 0; i < point.Length; i++) // iterate over your gridview rows
{
Line newline = new Line();
newline.X1 = lastLine.X2;
newline.Y1 = lastLine.Y2;
newline.X2 = axisX + (Point[i] * 5); // calculate X position of the current cell
newline.Y2 = lastLine.Y2 + 10; // calculate Y position of the current cell
x = newline.X2;
y = newline.Y2;
if (!first)
{
// first minimum cell should't be drawn, it is just the start point for next line
drawLine(main, newline);
}
else
{
first = false;
}
lastLine = newline;
}
public static void drawLine(MainWindow main, Line line)
{
line.HorizontalAlignment = HorizontalAlignment.Left;
line.VerticalAlignment = VerticalAlignment.Center;
line.Stroke = System.Windows.Media.Brushes.SteelBlue;
line.StrokeThickness = 1.5;
main.myLineCanvas.Children.Add(line);
}
Place your Canvas in a Viewbox:
<Viewbox>
<Canvas x:Name="myLineCanvas" />
</Viewbox>
You can change its behavior with Stretch and StretchDirection.
You should try using a ViewBox and see if it works well in your case, is as simple as surrounding your canvas with the ViewBox.
<Window ...
...>
<ViewBox>
<Canvas .....>
</Canvas>
</ViewBox>
</Window>
Window has a SizeChanged event. In your handler you can grab the new size of your window and set the endpoints of your lines accordingly.
Related
I'm exploring graphics capabilities of WPF+C#. Drawing a line with XAML makes what expected. ok.
To draw a simple line with C# code makes something "strange". The coordinates appear not where
they should be. Why? The following code should implement a diagonal from (0, 0) to (width, height) of
the container panel of the line.
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
Line line1 = new Line
{
Stroke = Brushes.Black
};
Thickness thickness = new Thickness(10, -10, 36, 250);
line1.Margin = thickness;
line1.Visibility = Visibility.Visible;
line1.StrokeThickness = 4;
line1.X1 = 0;
line1.Y1 = 0;
line1.X2 = MainGrid.ActualWidth;
line1.Y2 = MainGrid.ActualHeight;
line1.HorizontalAlignment = HorizontalAlignment.Left;
MainGrid.Children.Add(line1);
}
It doesn't matter what panel you use: canvas, dockpanel, grid, stackpanel, I observed the same
strange and annoying behavior.
Not showing the diagonal. Not beginning at origin-vertex (0,0): (left,top)
Your problem is Thickness(10, -10, 36, 250). This arbitrary margin is cutting off the rest of the line. Here is a simplified version of your code that correctly draws a line from the top-left corner, to the bottom-right:
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
Line line1 = new Line
{
Stroke = Brushes.Black,
StrokeThickness = 4,
X1 = 0,
Y1 = 0,
X2 = MainGrid.ActualWidth,
Y2 = MainGrid.ActualHeight
};
MainGrid.Children.Add(line1);
}
Note that Visibility = Visibility.Visible is left because Line (like most other elements) are visible by default.
You can do this much easier with a simple path statement - use Stretch="Fill" so there's no need to worry about dimensions
<Path Stroke="Black" StrokeThickness="4" Data="M 0,0 L 1,1" Stretch="Fill" />
This is my first project in c# and I'm trying to create plots from data.
I'm struggling with drawing minor and major grid lines and labels on a logarithmic scale.
I've set the scale to logarithmic, set the base to 10 and both major and minor intervals to 1, and it works great, however, the interval starts with the minimum value on scale, so for example if data starts at 30M (I'm dealing with frequencies) the next major tick is at 300M and 3G, which is not as it should be.
Is there a way to set major grid to 1, 10, 100 etc, independent of what data is displayed? i've tried changing intervals, base and offset but have not achieved much.
area.AxisX.IsLogarithmic = true;
area.AxisX.LogarithmBase = 10;
area.AxisX.Interval = 1;
//area.AxisX.IntervalOffset = 10000;
area.AxisX.IntervalAutoMode = IntervalAutoMode.FixedCount;
area.AxisX.MajorGrid.Enabled = true;
area.AxisX.MajorTickMark.Enabled = true;
area.AxisX.MinorGrid.Enabled = true;
area.AxisX.MinorGrid.Interval = 1;
area.AxisX.MinorTickMark.Enabled = true;
area.AxisX.MinorTickMark.Interval = 1;
area.AxisX.Minimum = minMaxXY[0]; // in this example 30 M
area.AxisX.Maximum = minMaxXY[1]; // in this example 1 G
here's the link to the current grid
https://ibb.co/3WkxLfc
Thank you for your time and answers!
Thanks to TaW replay I managed to get my program working.
Here is my solution using customLabels location to draw the grid lines.
private void Chart1_PostPaint(object sender, ChartPaintEventArgs e)
{
if (e.Chart.ChartAreas.Count > 0) // I don't yet truly understand when this event occurs,
// so I got plenty of null references.
{
Graphics g = e.ChartGraphics.Graphics;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
Color minorGridColor = Color.Gainsboro;
ChartArea area = e.Chart.ChartAreas[0];
double aymin = area.AxisY.Minimum;
double aymax = area.AxisY.Maximum;
int y0 = (int)area.AxisY.ValueToPixelPosition(aymin);
int y1 = (int)area.AxisY.ValueToPixelPosition(aymax);
foreach (var label in chart1.ChartAreas[0].AxisX.CustomLabels)
{
double xposition = area.AxisX.ValueToPixelPosition(Math.Pow(10, label.FromPosition + 0.1));
if (xposition > area.AxisX.ValueToPixelPosition(minMaxXY[0]) && xposition < area.AxisX.ValueToPixelPosition(minMaxXY[1]))
//this prevents drawing of lines outside of the chart area
{
int x = (int)xposition;
using (Pen dashed_pen = new Pen(Color.FromArgb(10, 0, 0, 0), 1))
{
dashed_pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
g.DrawLine(dashed_pen, x, y0, x, y1);
}
}
}
}
}
I also found the CustomLabel.GridTicks Property, but for some reason it did not work.
I'm trying to fill a rectangle with a variable quantity of little rectangles but adjusting the distance between them depending on the number of units (more units->the lesser the distance between).
I'm a newbie programming WPF in C# and i don´t know how to advance from this point.
How can I do it?
The code so far:
int units = 20;
int width = 10;
int height = 20;
int top = 200;
int left = 200;
int rectangleWidth = 300;
int rectangleHeight = 100;
for (int i = 0; i < units; i++)
{
Rectangle rec = new Rectangle()
{
Width = width,
Height = height,
Fill = Brushes.Black,
Stroke = Brushes.White,
StrokeThickness = 1,
RadiusX = 3,
RadiusY = 3,
};
cuadernodibujo.Children.Add(rec);
Canvas.SetTop(rec, top);
Canvas.SetLeft(rec, left + (i*50));
}
I have updated the code, but doesn´t work.
I don´t know what am i doing wrong.
The piece of code so far:
int rectangleWidth = 500;
int rectangleHeight = 100;
int units = 60;
int unitsX = 10;
int unitsY = 6;
var childWidht = (rectangleWidth - 2*Left) / unitsX;
var childHeigth = (rectangleHeight - 2*Top ) / unitsY;
int width = 10;
int height = 20;
double top = 100;
double left = 100;
for (int i = 0; i < units; i++)
{
Rectangle rec = new Rectangle()
{
Width = width,
Height = height,
Fill = Brushes.Black,
Stroke = Brushes.White,
StrokeThickness = 1,
RadiusX = 3,
RadiusY = 3,
};
cuadernodibujo.Children.Add(rec);
for (int j = 0; j < unitsY; j++)
{
Rectangle rec2 = new Rectangle()
{
Width = width,
Height = height,
Fill = Brushes.Black,
Stroke = Brushes.White,
StrokeThickness = 1,
RadiusX = 3,
RadiusY = 3,
};
cuadernodibujo.Children.Add(rec2);
Canvas.SetTop(rec, top + (j * childHeigth));
for (int k = 0; k < unitsX; k++)
{
Rectangle rec3 = new Rectangle()
{
Width = width,
Height = height,
Fill = Brushes.Black,
Stroke = Brushes.White,
StrokeThickness = 1,
RadiusX = 3,
RadiusY = 3,
};
cuadernodibujo.Children.Add(rec3);
Canvas.SetLeft(rec, left + (k * childWidht));
}
}
}
If I understand correctly, you want to spread the little rectangles uniformly over the width of the parent rectangle.
This is less a programming problem, then a maths problem.
Given the parent rectangle's width parentWidht and the number of child rectangles units each child rectangle has a width of:
var childWidht = parentWidht / units;
If you want to add a left and right margin (given your left variable), you need to subtract the margin from parentWidht.
var childWidht = (parentWidht - 2 * left) / units; // 2 times left, to add the margin on both sides.
This gives you the width of each child, you now only have to move each child rectangle according to the previously calculated childWidht.
...
var childWidht = (parentWidht - 2 * left) / units;
for (int i = 0; i < units; i++)
{
...
Canvas.SetLeft(rec, left + (i*childWidht));
}
Update to question in the comments
With that I can fill a single line, but how can I fill the rest of the lines (to fill the parent height as well)?
We can apply the same logic as for the horizontal filling.
First calculate the child rectangles height (parentHeight - 2 * top)
Then wrap the horizontal rectangles into a loop and move each line according to the calculated height.
Here the listing with horizontal and vertical filling.
...
var childWidht = (parentWidht - 2 * left) / unitsX;
var childHeigth = (parentHeigth - 2 * top) / unitsY;
for (int j = 0; j < unitsY; i++) // first loop for vertical filling
{
for (int i = 0; i < unitsX; i++) // second loop for horizontal
{
var rect = new Rectangle { ... } ;
Canvas.Children.Add(rect); // Only add once in the inner loop.
Canvas.SetTop(rec, top + (j * childHeigth)); // here we use j, the vertical index
Canvas.SetLeft(rec, left + (i*childWidht)); // here we use i, the horizontal index
}
}
i am stuck finding the correct mathematical formula to set the correct y position in a canvas based on the lowest and highest price (int32) in a list of prices.
I hope you can help me form a formula which will solve my problem.
I have a canvas control in which i want to draw lines to display the price trend.
What i have is the list of prices to draw,
example: 10 20 30 40 50
the lowest and the highest price in the list and of course the canvas control.
the formular so far is:
double currentPriceLineY = canvas.Height - (canvas.Height * (history.Price / highestPrice));
with this code i set the y position of the line based on the highest price,
but now i have no clue how to combine this with the lowest price to be at the bottom of the canvas while the highest price is at the top of the canvas and the other prices between this both points with relation to the highest and the lowest price.
I am stuck since yesterday and decided to ask you guys for help and hope you can understand my problem.
Thanks in advance
Avoider
First, calculate the difference between the lowest and highest value (I called it range in the code). Then for each value, subtract the minimum value from it and divide by the range. This will give you the percentage into the range for that value. Then draw your lines - keep in mind that 0 is at the top and canvas.Height is at the bottom. In the code, I inverted the percentage to fix that.
I also added a TextBlock sitting on the line so you can see what value went with what line.
Here is the implementation of it:
XAML:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="300">
<Window.Resources>
</Window.Resources>
<Grid>
<Canvas Background="Beige" Name="canvas" Height="200" Width="200" />
</Grid>
</Window>
Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
int highestPrice = 65;
List<int> vals = new List<int>() { 10, 20, 30, 40, 50 };
int min = vals.Min();
int max = vals.Max();
max = highestPrice > max ? highestPrice : max;
double range = max - min;
// Draw max in red
Color c = new Color() { ScA = 1, ScR = 1, ScG = 0, ScB = 0 };
// y = 0 is at the top of the canvas
var line = new Line() { X1 = 0, Y1 = 0, X2 = canvas.Width, Y2 = 0, Stroke = new SolidColorBrush(c), StrokeThickness = 2.0 };
canvas.Children.Add(line);
// Add txt so we can visualize better
var txt = new TextBlock() { Text = max.ToString() };
Canvas.SetLeft(txt, canvas.Width / 2);
Canvas.SetTop(txt, 0 - 9);
canvas.Children.Add(txt);
foreach (int val in vals)
{
double percent = 1.0 - ((val - min)/range); // 0 is at the top, so invert it by doing 1.0 - xxx
double y = percent*canvas.Height;
// Draw line in a shade of blue/green
c = new Color() { ScA = 1, ScR = 0, ScG = 0.5f, ScB = (float)percent };
line = new Line() { X1 = 0, Y1 = y, X2 = canvas.Width, Y2 = y, Stroke = new SolidColorBrush(c), StrokeThickness = 2.0 };
canvas.Children.Add(line);
// Add txt so we can visualize better
txt = new TextBlock() { Text = val.ToString() };
Canvas.SetLeft(txt, canvas.Width / 2);
Canvas.SetTop(txt, y - 9);
canvas.Children.Add(txt);
}
}
}
I am trying to create rectangles and the number of rectangles is depend on data passed from database.
For example, if number = 5, the program will generate 5 rectangles. Also, these rectangles must be able to follow my rectangle property settings, like height, width,color...put them in one line at the end.
Is there a way to do that?
I am using WPF and C#.
Thank you.
To create the rectangle in code dynamically:
int number = 5;
int width = 10;
int height = 10;
int top = 20;
int left = 20;
for (int i = 0; i < number; i++)
{
// Create the rectangle
Rectangle rec = new Rectangle()
{
Width = width,
Height = height,
Fill = Brushes.Green,
Stroke = Brushes.Red,
StrokeThickness = 2,
};
// Add to a canvas for example
canvas.Children.Add(rec);
Canvas.SetTop(rec, top);
Canvas.SetLeft(rec, left);
}