CreateGraphics(): Graphics is shown only on re-size - c#

Instantiating the form.
public TwoDPlot()
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US", true);
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US", true);
InitializeComponent();
DrawCircle();
}
Method to draw the circle.
private void DrawCircle()
{
var radius = 0.5 * ClientRectangle.Width;
var height = ClientRectangle.Height - 0.1 * ClientRectangle.Height;
var width = ClientRectangle.Width - 0.1 * ClientRectangle.Width;
var scaledRadius = width > height ? radius * (height / radius) : radius * (width / radius);
var xlocation = ClientRectangle.Width / 2.0 - scaledRadius * 0.5;
var ylocation = ClientRectangle.Height / 2.0 - scaledRadius * 0.5;
m_Graphics = CreateGraphics();
m_Graphics?.Clear(DefaultBackColor);
m_Graphics?.DrawEllipse(new Pen(Color.Red), new Rectangle((int)xlocation, (int)ylocation, (int)scaledRadius, (int)scaledRadius));
m_Graphics.Dispose();
}
On instance, it shows empty form and upon re-sizing it shows the circle. I expect to show during the first instance.

This is the direct correction:
private void TwoDPlot_Paint(object sender, PaintEventArgs e)
{
DrawCircle(e.Graphics);
}
private void DrawCircle(Graphics m_Graphics)
{
var radius = 0.5 * ClientRectangle.Width;
var height = ClientRectangle.Height - 0.1 * ClientRectangle.Height;
var width = ClientRectangle.Width - 0.1 * ClientRectangle.Width;
var scaledRadius = width > height ? radius * (height / radius) : radius * (width / radius);
var xlocation = ClientRectangle.Width / 2.0 - scaledRadius * 0.5;
var ylocation = ClientRectangle.Height / 2.0 - scaledRadius * 0.5;
m_Graphics.Clear(DefaultBackColor);
m_Graphics.DrawEllipse(new Pen(Color.Red), new Rectangle((int)xlocation, (int)ylocation, (int)scaledRadius, (int)scaledRadius));
}
Note that to be more flexible you will want to move the variables maybe to class level variables or to parameters to the DrawCircle function..
When you have done that and changed the variables values you can trigger the Paint event by calling TwoDPlot.Invalidate().
The system will also call it whenever it needs to, e.g. upon many resize, maximize and other events..

Related

How to use AddArc() method in C#

I have this little code to use AddArc() method in a label, but when I execute the code the label disappears. I believe it is the numbers I have used, I followed instructions from the Windows documentation and it had these parameters there too.
GraphicsPath gp = new GraphicsPath();
Rectangle rec = new Rectangle(20, 20, 50, 100);
gp.AddArc(rec, 0 , 180);
label2.Region = new Region(gp);
label2.Invalidate();
I used another code to make the correct way or curve in a text
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var center = new Point(Width / 2, Height / 2);
var radius = Math.Min(Width, Height) / 3;
var text = "Hello";//txtUp.Text;
var font = new Font(FontFamily.GenericSansSerif, 24, FontStyle.Bold);
for (var i = 0; i < text.Length; ++i)
{
var c = new String(text[i], 1);
var size = e.Graphics.MeasureString(c, font);
var charRadius = radius + size.Height;
var angle = (((float)i / text.Length) - 2);
var x = (int)(center.X + Math.Cos(angle) * charRadius);
var y = (int)(center.Y + Math.Sin(angle) * charRadius);
e.Graphics.TranslateTransform(x, y);
e.Graphics.RotateTransform((float)(90 + 360 * angle / (2 * Math.PI)));
e.Graphics.DrawString(c, font, Brushes.Red, 0, 0);
e.Graphics.ResetTransform();
e.Graphics.DrawArc(new Pen(Brushes.Transparent, 2.0f), center.X - radius, center.Y - radius, radius * 2, radius * 2, 0, 360);
}
}
but it wont show in front of a panel is it possible.
This is what it looks like:
Is it possible to move that text in front of the green circle?

Image flickering while zooming while using PinchGestureRecognizer

I use the below code to place an <Image> and <Image.GestureRecognizers> to make the zoom functionality.
The code behind for the event PinchUpdated="PinchGestureRecognizer_PinchUpdated" is also provided.
The zoom function works, but too much flickering while pinching.
XAML :
<Image
Grid.Column="0"
Grid.Row="0"
x:Name="pageIterator"
Grid.ColumnSpan="3">
<Image.GestureRecognizers>
<PinchGestureRecognizer PinchUpdated="PinchGestureRecognizer_PinchUpdated"></PinchGestureRecognizer>
</Image.GestureRecognizers>
</Image>
Code behind :
private void PinchGestureRecognizer_PinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
double currentScale = 1;
double startScale = 1;
double xOffset = 0;
double yOffset = 0;
if (e.Status == GestureStatus.Started)
{
// Store the current scale factor applied to the wrapped user interface element,
// and zero the components for the center point of the translate transform.
startScale = Content.Scale;
Content.AnchorX = 0;
Content.AnchorY = 0;
}
if (e.Status == GestureStatus.Running)
{
// Calculate the scale factor to be applied.
currentScale += (e.Scale - 1) * startScale;
currentScale = Math.Max(1, currentScale);
// The ScaleOrigin is in relative coordinates to the wrapped user interface element,
// so get the X pixel coordinate.
double renderedX = Content.X + xOffset;
double deltaX = renderedX / Width;
double deltaWidth = Width / (Content.Width * startScale);
double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;
// The ScaleOrigin is in relative coordinates to the wrapped user interface element,
// so get the Y pixel coordinate.
double renderedY = Content.Y + yOffset;
double deltaY = renderedY / Height;
double deltaHeight = Height / (Content.Height * startScale);
double originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;
// Calculate the transformed element pixel coordinates.
double targetX = xOffset - (originX * Content.Width) * (currentScale - startScale);
double targetY = yOffset - (originY * Content.Height) * (currentScale - startScale);
// Apply translation based on the change in origin.
Content.TranslationX = targetX.Clamp(-Content.Width * (currentScale - 1), 0);
Content.TranslationY = targetY.Clamp(-Content.Height * (currentScale - 1), 0);
// Apply scale factor.
Content.Scale = currentScale;
}
if (e.Status == GestureStatus.Completed)
{
// Store the translation delta's of the wrapped user interface element.
xOffset = Content.TranslationX;
yOffset = Content.TranslationY;
}
}
You should move the below lines out of PinchGestureRecognizer_PinchUpdated method,otherwise you're essentially initializing these values every time you fire.
double currentScale = 1;
double startScale = 1;
double xOffset = 0;
double yOffset = 0;
private void PinchGestureRecognizer_PinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
if (e.Status == GestureStatus.Started)
{
// Store the current scale factor applied to the wrapped user interface element,
// and zero the components for the center point of the translate transform.
startScale = Content.Scale;
Content.AnchorX = 0;
Content.AnchorY = 0;
}
if (e.Status == GestureStatus.Running)
{
// Calculate the scale factor to be applied.
currentScale += (e.Scale - 1) * startScale;
currentScale = Math.Max(1, currentScale);
// The ScaleOrigin is in relative coordinates to the wrapped user interface element,
// so get the X pixel coordinate.
double renderedX = Content.X + xOffset;
double deltaX = renderedX / Width;
double deltaWidth = Width / (Content.Width * startScale);
double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;
// The ScaleOrigin is in relative coordinates to the wrapped user interface element,
// so get the Y pixel coordinate.
double renderedY = Content.Y + yOffset;
double deltaY = renderedY / Height;
double deltaHeight = Height / (Content.Height * startScale);
double originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;
// Calculate the transformed element pixel coordinates.
double targetX = xOffset - (originX * Content.Width) * (currentScale - startScale);
double targetY = yOffset - (originY * Content.Height) * (currentScale - startScale);
// Apply translation based on the change in origin.
Content.TranslationX = targetX.Clamp(-Content.Width * (currentScale - 1), 0);
Content.TranslationY = targetY.Clamp(-Content.Height * (currentScale - 1), 0);
// Apply scale factor.
Content.Scale = currentScale;
}
if (e.Status == GestureStatus.Completed)
{
// Store the translation delta's of the wrapped user interface element.
xOffset = Content.TranslationX;
yOffset = Content.TranslationY;
}
}

hittest in polar chart with multiple series

I have a polar chart with multiple series. I want to have a functionality to click on one of the datapoints in any series and perform something. I tried to use the HitTest and it works on a single series. The problem is when I used in on a chart with multiple series and sometimes when I click on a datapoint, it returns a different point. Please help.
This is the snippet that I used.
HitTestResult result = chart1.HitTest(e.X, e.Y, ChartElementType.DataPoint);
if (result.ChartElementType == ChartElementType.DataPoint)
{
var xVal = result.Series.Points[result.PointIndex].XValue;
var yVal = result.Series.Points[result.PointIndex].YValues;
result.Series.Points[result.PointIndex].MarkerColor = Color.Black;
}
update:
Thanks you so much for bearing with me. Anyway, this is the code incorporating what you suggested.
DataPoint dpCurrent = null;
int normalMarkerSize = 10;
int largeMarkerSize = 15;
private void chart1_MouseClick(object sender, MouseEventArgs e)
{
HitTestResult result = chart1.HitTest(e.X, e.Y, ChartElementType.DataPoint);
if (result.ChartElementType == ChartElementType.DataPoint)
{
dpCurrent = result.Series.Points[result.PointIndex];
if (distance(PolarValueToPixelPosition(dpCurrent, chart1, result.ChartArea), e.Location) <= 5)
result.Series.Points[result.PointIndex].MarkerColor = Color.Black;
}
}
However, I noticed that the value of "phi" in the PolarValueToPixelPosition is always returning NaN
Here are two ways to solve the problem; none can actually make the HitTest ignore clicking on the connecting lines.
But they should be fine, espacially when you implement them both.
The first provides feedback to the user so he can see in advance which point the mouse is over and he is about to click:
DataPoint dpCurrent = null;
int normalMarkerSize = 8;
int largeMarkerSize = 12;
private void chart1_MouseMove(object sender, MouseEventArgs e)
{
HitTestResult hit = chart1.HitTest(e.X, e.Y);
if (hit.ChartElementType == ChartElementType.DataPoint)
{
dpCurrent = hit.Series.Points[hit.PointIndex];
dpCurrent.MarkerSize = largeMarkerSize;
}
else
{
if (dpCurrent != null) dpCurrent.MarkerSize = normalMarkerSize;
dpCurrent = null;
}
Unfortunately the HitTest will still trigger a DataPoint-hit even if you only hit the connecting lines, no matter how thin you make them or what color they have..
..enter solution two:
One can write a custom HitTest by calculating the pixel-coordinates of the DataPoints; this is not as simple as calling the Axis.ValueToPixelPosition methods as it involves some modest amount of math..:
Now before processing the hit you would do an additional check, maybe like this:
if (distance(PolarValueToPixelPosition(dpCurrent, chart1,
hit.ChartArea), e.Location) <= markerRadius) ...//do the hit stuff
Here is the coordinate transformation function:
PointF PolarValueToPixelPosition(DataPoint dp, Chart chart, ChartArea ca)
{
RectangleF ipp = InnerPlotPositionClientRectangle(chart, ca);
double crossing = ca.AxisX.Crossing != double.NaN ? ca.AxisX.Crossing : 0;
// for RangeChart change 90 zo 135 !
float phi = (float)(360f / ca.AxisX.Maximum / 180f * Math.PI *
(dp.XValue - 90 + crossing ) );
float yMax = (float)ca.AxisY.Maximum;
float yMin = (float)ca.AxisY.Minimum;
float radius = ipp.Width / 2f;
float len = (float)(dp.YValues[0] - yMin) / (yMax - yMin);
PointF C = new PointF(ipp.X + ipp.Width / 2f, ipp.Y + ipp.Height / 2f);
float xx = (float)(Math.Cos(phi) * radius * len);
float yy = (float)(Math.Sin(phi) * radius * len);
return new PointF(C.X + xx, C.Y + yy);
}
It makes use of a simple distance function..:
float distance(PointF pt1, PointF pt2)
{
float d = (float)Math.Sqrt((pt1.X - pt2.X) * (pt1.X - pt2.X)
+ (pt1.Y - pt2.Y) * (pt1.Y - pt2.Y));
return d;
}
Also of two other useful functions to calculate the pixel size of the InnerPlotPosition, which I have used in quite a few answers now..:
RectangleF ChartAreaClientRectangle(Chart chart, ChartArea CA)
{
RectangleF CAR = CA.Position.ToRectangleF();
float pw = chart.ClientSize.Width / 100f;
float ph = chart.ClientSize.Height / 100f;
return new RectangleF(pw * CAR.X, ph * CAR.Y, pw * CAR.Width, ph * CAR.Height);
}
RectangleF InnerPlotPositionClientRectangle(Chart chart, ChartArea CA)
{
RectangleF IPP = CA.InnerPlotPosition.ToRectangleF();
RectangleF CArp = ChartAreaClientRectangle(chart, CA);
float pw = CArp.Width / 100f;
float ph = CArp.Height / 100f;
return new RectangleF(CArp.X + pw * IPP.X, CArp.Y + ph * IPP.Y,
pw * IPP.Width, ph * IPP.Height);
}

Scale buttons in any resolution

I'm trying to write a method that scales buttons in any resolution, on full screen without remaining space between them. Button parameters come from a web service. How can I resize them to fill all screen no matter what the parameters are. Or at least to scale their actual size to my new size. My form size is good but my buttons intersect.
private void ResizeButtons()
{
pnlDynamicControls.Height = this.ClientSize.Height
- (pnlDynamicControls.Top + pnlButtons.Height);
pnlDynamicControls.Width = pnlButtons.Width;
float widthRatio =
(float)Screen.PrimaryScreen.Bounds.Width / ClientRectangle.Width;
float heightRatio =
(float)Screen.PrimaryScreen.Bounds.Height / ClientRectangle.Height;
SizeF scale = new SizeF(widthRatio,heightRatio);
this.Scale(scale);
//if ((widthRatio - pnlDynamicControls.Width) < (heightRatio - pnlDynamicControls.Height))
// l_zoomFactor = (float)widthRatio / pnlDynamicControls.Width;
//else
// l_zoomFactor = (float)heightRatio / pnlDynamicControls.Height;
foreach (Control c in pnlDynamicControls.Controls)
{
c.Height = Convert.ToInt16(c.Height * heightRatio);
c.Width = Convert.ToInt16(c.Width * widthRatio);
c.Margin = new Padding(1, 1, 1, 1);
//c.Size = new System.Drawing.Size(
// Convert.ToInt16(c.Width * l_zoomFactor)
// , Convert.ToInt16(c.Height * l_zoomFactor)
// );
}
}

Place dynamically generated items in circle around a button

I'm creating a silverlight application where I have to dynamically create buttons. But I need to place them in a circle around the button that I click to generate the other buttons (picture here, the buttons should go on the black line surrounding the 'test project' button)
I don't know how many buttons will be generated each time but I do know the size of each button is static. I'm not quite sure how to do this. Currently my button creation is as follows
foreach (Item a in itemList)
{
Button newButton = new Button();
newButton.Height = 50;
newButton.Width = 50;
newButton.Content = a.getName();
newButton.Click += new RoutedEventHandler(addedClick);
newButton.HorizontalAlignment = HorizontalAlignment.Left;
newButton.VerticalAlignment = VerticalAlignment.Top;
newButton.Margin = new Thickness(0, 0, 0, 0);
newButton.Style = (Style)Application.Current.Resources["RB"];
buttons.Add(newButton);
}
My biggest problem is that I'm not quite sure how to get the center point of the 'test project' button.
EDIT: Okay, now that I have a set of coordinates for each button, how exactly do I go about placing them? I'm not sure how to use a canvas. I tried to set one up but it keeps acting like a stackpanel (no .setLeft/.setTop).
You mean something like the circle equation:
Double distanceFromCenter = 5;
Double angleInDegrees = 90;
Double angleAsRadians = (angleInDegrees* Math.PI) / 180.0;
Double centerX = 100;
Double centerY = 100;
Double x = centerX + Math.Cos(angleAsRadians) * distanceFromCenter;
Double y = centerY + Math.Sin(angleAsRadians) * distanceFromCenter;
that should give you a point that is distanceFromCenter units away from (centerX, center), at an angle of 90-degrees. Note this only works with radians so we have to convert to radians.
var radius = 100;
var angle = 360 / itmeList.Count * Math.PI / 180.0f;
var center = new Point(100, 100);
for (int i = 0; i < itemList.Count; i++)
{
var x = center.X + Math.Cos(angle * i) * radius;
var y = center.Y + Math.Sin(angle * i) * radius;
Button newButton = new Button();
newButton.RenderTransformOrigin = new Point(x, y);
newButton.Height = 50;
newButton.Width = 50;
newButton.Content = a.getName();
newButton.Click += new RoutedEventHandler(addedClick);
newButton.HorizontalAlignment = HorizontalAlignment.Left;
newButton.VerticalAlignment = VerticalAlignment.Top;
newButton.Margin = new Thickness(0, 0, 0, 0);
newButton.Style = (Style)Application.Current.Resources["RB"];
buttons.Add(newButton);
}
Assuming you want your buttons evenly spaced on the circle, you should first generate the list of angles you want them at. E.g.
double eachSection = 2 * Math.PI / count;
var anglesInRadians = Enumerable.Range(0, count).Select(x => x * eachSection);
Then use this formula to find the x/y coordinates of each angle, and use a Canvas or something to position the buttons in those positions
public static Point PointOnCircle(double radius, double angleInRadians, Point origin)
{
double x = radius * Math.Cos(angleInRadians) + origin.X;
double y = radius * Math.Sin(angleInRadians) + origin.Y;
return new Point(x, y);
}

Categories

Resources