Why are my textblocks being inconsistently placed on my WPF Canvas? - c#

I have a DataTable of points and I'm attempting to place two diagonal lines to display the point and then have the point's name as a textblock displayed beside it. The two lines place perfectly in the correct positions, whereas the text is not in the correct position, although I'm using the exact same coordinates.
Here's my code:
public void DrawPoints(List<double> extents, double canvasWidth, double canvasHeight)
{
DataRow[] points = Globals.DT.Select();
foreach (DataRow row in points)
{
string name = row["Name"].ToString();
List<double> coords = new List<double> {(double)row["Easting"], (double)row["Northing"]};
double elevation = (double)row["Elevation"];
DXFToCanvas dxfToCanvas = new DXFToCanvas();
List<double> canvasCoords = dxfToCanvas.DXFCoordToCanvas(coords, extents, canvasWidth, canvasHeight);
// Create two diagonal lines for point
double sizeFactor = 2;
Line line1 = new Line();
line1.X1 = canvasCoords[0] - sizeFactor;
line1.Y1 = canvasCoords[1] - sizeFactor;
line1.X2 = canvasCoords[0] + sizeFactor;
line1.Y2 = canvasCoords[1] + sizeFactor;
Line line2 = new Line();
line2.X1 = canvasCoords[0] - sizeFactor;
line2.Y1 = canvasCoords[1] + sizeFactor;
line2.X2 = canvasCoords[0] + sizeFactor;
line2.Y2 = canvasCoords[1] - sizeFactor;
SolidColorBrush redBrush = new SolidColorBrush();
redBrush.Color = Colors.Red;
line1.StrokeThickness = 0.5;
line1.Stroke = redBrush;
line2.StrokeThickness = 0.5;
line2.Stroke = redBrush;
map_page_canvas.Children.Add(line1);
map_page_canvas.Children.Add(line2);
// Create point labels
TextBlock textBlock = new TextBlock();
textBlock.Text = name;
textBlock.FontSize = 4;
textBlock.Foreground = redBrush;
ScaleTransform scaleTransform = new ScaleTransform();
scaleTransform.ScaleY = -1;
textBlock.RenderTransform = scaleTransform;
Canvas.SetLeft(textBlock, canvasCoords[0]);
Canvas.SetBottom(textBlock, canvasCoords[1]);
map_page_canvas.Children.Add(textBlock);
}
}
How is it possible that using the exact same coordinates to set both the lines and the textblock can result in the textblock being at different positions than the lines? I'm using the scaletransform because my canvas needed to be on a y increasing upwards coordinate system.

Related

WPF Ellipse wont resize correctly on MouseEnter Event

I am using ChartView Telerik WPF Library. I want the points to get bigger when the user hovers over them. But for some reason it is not working as expected. The Ellipse gets bigger but it does not resize correctly. But I don't understand why. The other properties as border color and thickness are working correctly.
Can someone tell me what am I missing here ?
This is how it looks currently when I try to resize the point :
Here is the Source Code:
private FrameworkElementFactory AddPointsToSeries(KeyValuePair<ChartSerie, List<ChartDataPoint>> chartSeries, int colorPaletteIndex)
{
var seriesPredefinedColor = this.ChartBase.Palette.GlobalEntries[colorPaletteIndex].Fill;
FrameworkElementFactory frameworkElement = new FrameworkElementFactory(typeof(Ellipse));
frameworkElement.SetValue(Ellipse.FillProperty, ColorService.BrushFromHex(chartSeries.Key.ColorHex) ?? seriesPredefinedColor);
frameworkElement.SetValue(Ellipse.HeightProperty, 9.0D);
frameworkElement.SetValue(Ellipse.WidthProperty, 9.0D);
frameworkElement.AddHandler(Ellipse.MouseEnterEvent, new MouseEventHandler((sender, args) =>
{
Ellipse ellipse = (Ellipse)sender;
ellipse.Stroke = ColorService.BrushFromHex(ColorService.BlendHex((chartSeries.Key.ColorHex ?? ColorService.BrushToHex(seriesPredefinedColor)), "#000000", 0.4));
// this is not correctly applied!
ellipse.Width = 15;
ellipse.Height = 15;
ellipse.StrokeThickness = 2;
}));
frameworkElement.AddHandler(Ellipse.MouseLeaveEvent, new MouseEventHandler((sender, args) =>
{
Ellipse ellipse = (Ellipse)sender;
ellipse.Height = 8;
ellipse.Width = 8;
ellipse.Stroke = null;
}));
return frameworkElement;
}
// Here I create the Line Series and here I use the AddPointsToSeries Method
private LineSeries CreateLineSeries(KeyValuePair<ChartSerie, List<ChartDataPoint>> chartSeries, ChartLegendSettings legendSettings,
int colorPaletteIndex)
{
FrameworkElementFactory addPoints = AddPointsToSeries(chartSeries, colorPaletteIndex);
var lineSerie = new LineSeries()
{
VerticalAxis = CreateMultipleVerticalAxis(chartSeries, colorPaletteIndex, out var multipleVerticalAxis) ? multipleVerticalAxis : null,
ZIndex = 150, // the line series should always be displayed on top of other series.
StrokeThickness = 3.5,
LegendSettings = (SeriesLegendSettings)legendSettings,
Opacity = 0.8,
StackGroupKey = chartSeries.Key.Group,
CombineMode = string.IsNullOrEmpty(chartSeries.Key.Group) ? ChartSeriesCombineMode.None : ChartSeriesCombineMode.Stack,
PointTemplate = new DataTemplate()
{
VisualTree = addPoints,
},
};
// this is the color of line series
if (chartSeries.Key.ColorHex != null)
{
lineSerie.Stroke = (SolidColorBrush)(new BrushConverter().ConvertFrom(chartSeries.Key.ColorHex));
}
foreach (ChartDataPoint serie in chartSeries.Value)
{
lineSerie.DataPoints.Add(new CategoricalDataPoint()
{
Category = serie.XPoint.Label,
Value = (double?)serie.Value,
});
}
return lineSerie;
}
My question was answered my Martin Ivanov:
This behavior comes from the size caching mechanism of the chart. Basically, the control is setting the size of the visual when initially loaded and then it doesn't change it unless something on the chart's API changes. In order to achieve your requirement you can wrap the ellipse in a Grid panel and its Width and Height properties to be the same (or little bigger) then the size of the bigger ellipse.
This is the solution:
private FrameworkElementFactory AddPointsToSeries(KeyValuePair<ChartSerie, List<ChartDataPoint>> chartSeries, int colorPaletteIndex)
{
var seriesPredefinedColor = this.ChartBase.Palette.GlobalEntries[colorPaletteIndex].Fill;
Brush brush = chartSeries.Key.ColorHex == null ? (seriesPredefinedColor) : ColorService.HexToBrush(chartSeries.Key.ColorHex);
Brush mouseOnEnterColor = new SolidColorBrush(ColorService.ChangeColorLightness(ColorService.BrushToColor(brush), 0.8));
double ellipseMouseOverStrokeThickness = 2;
double ellipseMouseOverHeightWidth = 13;
double ellipseStrokeThickness = 1;
double ellipseHeightWidth = 9;
FrameworkElementFactory frameworkElement = new FrameworkElementFactory(typeof(Ellipse));
frameworkElement.SetValue(Ellipse.FillProperty, brush);
frameworkElement.SetValue(Ellipse.MarginProperty, new Thickness(-4.5));
frameworkElement.SetValue(Ellipse.HeightProperty, ellipseHeightWidth);
frameworkElement.SetValue(Ellipse.WidthProperty, ellipseHeightWidth);
frameworkElement.SetValue(Ellipse.StrokeProperty, new SolidColorBrush(Colors.White));
frameworkElement.SetValue(Ellipse.StrokeThicknessProperty, ellipseStrokeThickness);
frameworkElement.AddHandler(Ellipse.MouseEnterEvent, new MouseEventHandler((sender, args) =>
{
Ellipse ellipse = (Ellipse)sender;
ellipse.Fill = new SolidColorBrush(Colors.White);
ellipse.Stroke = mouseOnEnterColor;
ellipse.StrokeThickness = ellipseMouseOverStrokeThickness;
ellipse.Width = ellipseMouseOverHeightWidth;
ellipse.Height = ellipseMouseOverHeightWidth;
}));
frameworkElement.AddHandler(Ellipse.MouseLeaveEvent, new MouseEventHandler((sender, args) =>
{
Ellipse ellipse = (Ellipse)sender;
ellipse.Stroke = new SolidColorBrush(Colors.White);
ellipse.Fill = brush;
ellipse.StrokeThickness = ellipseStrokeThickness;
ellipse.Height = ellipseHeightWidth;
ellipse.Width = ellipseHeightWidth;
}));
FrameworkElementFactory stackPanelFactory = new FrameworkElementFactory(typeof(Grid));
stackPanelFactory.SetValue(Grid.HeightProperty, ellipseMouseOverHeightWidth + ellipseMouseOverStrokeThickness);
stackPanelFactory.SetValue(Grid.WidthProperty, ellipseMouseOverHeightWidth + ellipseMouseOverStrokeThickness);
stackPanelFactory.AppendChild(frameworkElement);
return stackPanelFactory;
}
Now it looks like this:

Usage of a Picture instead of Rectangle Shape in C#

Background: I am currently busy with showing position of a Vehicle on a Zoomable Canvas based on the Position (X,Y) and Orientation (for Rotation). I use Rectangle for visualizing the vehicle. Everything works well but I got a bit greedy and now I want to replace the Rectangle with Top View Picture of the Vehicle, so it looks that the vehicle itself is moving instead a Rectangle.
Code Below:
private void PaintLocationVehicle(VehicleClass vc)
{
IEnumerable<Rectangle> collection = vc.ZoomableCanvas.Children.OfType<Rectangle>().Where(x => x.Name == _vehicleobjectname);
List<Rectangle> listE = collection.ToList<Rectangle>();
for (int e = 0; e < listE.Count; e++)
vc.ZoomableCanvas.Children.Remove(listE[e]);
// Assign X and Y Position from Vehicle
double drawingX = vc.gCurrentX * GlobalVar.DrawingQ;
double drawingY = vc.gCurrentY * GlobalVar.DrawingQ;
// Scale Length and Width of Vehicle
double tractorWidthScaled = vc.tractorWidth * GlobalVar.DrawingQ;
double tractorLengthScaled = vc.tractorLength * GlobalVar.DrawingQ;
// Get Drawing Location
double _locationX = drawingX - (tractorLengthScaled / 2);
double _locationY = drawingY - ((tractorWidthScaled / 2));
RotateTransform rotation = new RotateTransform();
// Angle in 10th of a Degree
rotation.Angle = vc.gCurrentTheeta/10 ;
double i = 0;
//paint the node
Rectangle _rectangle = new Rectangle();
_rectangle.Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString(vc.VehicleColor == "" ? "Black" : vc.VehicleColor));
_rectangle.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString(vc.VehicleColor == "" ? "Black" : vc.VehicleColor));
i += 0;
_rectangle.Width = tractorLengthScaled ;
_rectangle.Height = tractorWidthScaled;
rotation.CenterX = _rectangle.Width / 2;
rotation.CenterY = _rectangle.Height / 2;
_rectangle.RenderTransform = rotation;
Canvas.SetTop(_rectangle, _locationY + i);
Canvas.SetLeft(_rectangle, _locationX + i);
_rectangle.SetValue(ZoomableCanvas.ZIndexProperty, 2);
string _tooltipmsg = "Canvas: " + vc.ZoomableCanvas.Name;
// Assign ToolTip Values for User
_tooltipmsg += "\nX: " + vc.gCurrentX;
_tooltipmsg += "\nY: " + vc.gCurrentY;
_rectangle.ToolTip = _tooltipmsg;
_rectangle.Name = _vehicleobjectname;
//add to the canvas
vc.ZoomableCanvas.Children.Add(_rectangle);
}
Note: VehicleClass holds all the Values for a certain Vehicle. DrawingQ holds the transformation scale from Reality to Zoomable Canvas.
So the issues I forsee:
How to append the Size of a Jpeg file to get the size same as
Rectangle?
What kind of Shape object shall I use? Please
suggest.
If i undrestand you correctly. you wanted to show an image of the vechicle inside the rectangle. in order to do that you can use
ImageBrush and assign to the Rectangle Fill property
something like this
Rectangle rect = new Rectangle();
rect.Width = 100;
rect.Height = 100;
ImageBrush img = new ImageBrush();
BitmapImage bmp = new BitmapImage();
bmp.BeginInit();
bmp.UriSource = new Uri("vehicle image path");
bmp.EndInit();
img.ImageSource = bmp;
rect.Fill = img;
I hope that helps

Add Image to DataGridview

I have read the article at Show Image In dataGrid bu t am un sure of how to add the image. I am following the Windows 7 touch screen development kit example for images and would like to place the images in a data grid so they can scroll (the example has them in a circle on the canvas).
So, when adding an image in the image paths are just placed in a string array:
private string[] GetPictureLocations()
{
string[] pictures = Directory.GetFiles(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "*.jpg");
// If there are no pictures in MyPictures
if (pictures.Length == 0)
pictures = new string[]
{
#"images\Pic1.jpg",
#"images\Pic2.jpg",
#"images\Pic3.jpg",
#"images\Pic4.jpg"
};
return pictures;
}
//Load pictures to the canvas
private void LoadPictures()
{
string[] pictureLocations = GetPictureLocations();
double angle = 0;
double angleStep = 360 / pictureLocations.Length;
foreach (string filePath in pictureLocations)
{
try
{
Picture p = new Picture();
p.ImagePath = filePath;
p.Width = 300;
p.Angle = 180 - angle;
double angleRad = angle * Math.PI / 180.0;
p.X = Math.Sin(angleRad) * 300 + (_canvas.ActualWidth - 300) / 2.0;
p.Y = Math.Cos(angleRad) * 300 + (_canvas.ActualHeight - 300) / 2.0;
_canvas.Children.Add(p);
angle += angleStep;
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine("Error:" + ex.Message);
}
}
}
The example from the stack overflow article is:
DataGridTemplateColumn col1 = new DataGridTemplateColumn();
col1.Header = "MyHeader";
FrameworkElementFactory factory1 = new FrameworkElementFactory(typeof(Image));
Binding b1 = new Binding("Picture");
b1.Mode = BindingMode.TwoWay;
factory1.SetValue(Image.SourceProperty, b1);
DataTemplate cellTemplate1 = new DataTemplate();
cellTemplate1.VisualTree = factory1;
col1.CellTemplate = cellTemplate1;
datagrid.Columns.Add(col1);
I am unsure how to consolidate the two so I can show the loaded images (p) in the datagrid. Or is there an easier way?

How to draw vertical line on mschart that fills graph but isn't infinite?

I am trying to draw a vertical line that is anchored to a point. I tried to use the height of my Y axis which is fixed to draw the line, but it wasn't centered correctly. So right now I have an infinite line, but that I want is the line to just fill the graph like so
VerticalLineAnnotation lineannot = new VerticalLineAnnotation();
lineannot.AnchorDataPoint = chart.Series[item].Points.Last();
lineannot.LineColor = Color.Red;
lineannot.Width = 3;
lineannot.Visible = true;
lineannot.IsInfinitive = true;
chart.Annotations.Add(lineannot);
IsInfinitive is complemented by ClipToChartArea; you can set the line to be clipped to a ChartArea like this:
lineannot.ClipToChartArea = chart.ChartAreas[item].Name;
assuming item is the right area name or index..
Note that ClipToChartArea takes the name of the chart area!
This is the simplest way to do it.
It is also possible to control an annotation's position and size directly:
// Note that directly after adding points this will return NaN:
double maxDataPoint = chart1.ChartAreas[0].AxisY.Maximum;
double minDataPoint = chart1.ChartAreas[0].AxisY.Minimum;
LineAnnotation annotation2 = new LineAnnotation();
annotation2.IsSizeAlwaysRelative = false;
annotation2.AxisX = chart1.ChartAreas[0].AxisX;
annotation2.AxisY = chart1.ChartAreas[0].AxisY;
annotation2.AnchorY = minDataPoint;
annotation2.Height = maxDataPoint - minDataPoint;;
annotation2.Width = 0;
annotation2.LineWidth = 2;
annotation2.StartCap = LineAnchorCapStyle.None;
annotation2.EndCap = LineAnchorCapStyle.None;
annotation2.AnchorX = 21; // <- your point
annotation2.LineColor = Color.Pink; // <- your color
chart1.Annotations.Add(annotation2);

Drawing Multiple Rectangles on a Canvas in C#

I'm trying to draw a row of rectangles across my Canvas. When I run the following code, I only get one rectangle, even though my canvas element says it has 12 children.
Dimensions is a class with 2 integer properties, Height and Width. The canvas I am drawing this is on 400px by 600px.
Dimensions windowDimensions = new Dimensions()
{
Width = (int)cvsGameWindow.Width,
Height = (int)cvsGameWindow.Height
};
//init rectangles
for (int i = 0; i < windowDimensions.Width; i+=50)
{
Rectangle rect = new Rectangle(); //create the rectangle
rect.StrokeThickness = 1; //border to 1 stroke thick
rect.Stroke = _blackBrush; //border color to black
rect.Width = 50;
rect.Height = 50;
rect.Name = "box" + i.ToString();
Canvas.SetLeft(rect,i * 50);
_rectangles.Add(rect);
}
foreach (var rect in _rectangles)
{
cvsGameWindow.Children.Add(rect);
}
and the private members declared at the top of my code:
private SolidColorBrush _blackBrush = new SolidColorBrush(Colors.Black);
private SolidColorBrush _redBrush = new SolidColorBrush(Colors.Red);
private SolidColorBrush _greenBrush = new SolidColorBrush(Colors.Green);
private SolidColorBrush _blueBrush = new SolidColorBrush(Colors.Blue);
private List<Rectangle> _rectangles = new List<Rectangle>();
This is the culprit:
Canvas.SetLeft(rect,i * 50);
On the first loop, with i=0, you're setting Canvas.Left = 0; Since your for loop is doing i+=50, on the second loop i will be 50, so you'll be setting Canvas.Left = 2500. You said your Canvas is 400x600, so your rectangles are off-screen.
The simplest fix: use Canvas.SetLeft(rect, i) - since i is increasing in increments of 50.

Categories

Resources