I am trying to crop my image using ellipse shape.I'll able to do that with rectangle but for ellipse not able to do.
void ClipImage()
{
EllipseGeometry geo = new EllipseGeometry();
r = (Ellipse)(from c in LayoutRoot.Children where c.Opacity == .5 select c).First();
GeneralTransform gt = r.TransformToVisual(LayoutRoot);
Point p = gt.Transform(new Point(0, 0));
geo.Rect = new Rect(p.X, p.Y, r.Width, r.Height);
image1.Clip = geo;
r.Visibility = System.Windows.Visibility.Collapsed;
TranslateTransform t = new TranslateTransform();
t.X = -p.X;
t.Y = -p.Y;
image1.RenderTransform = t;
}
r is ellipse and p is
GeneralTransform gt = ((Ellipse)sender).TransformToVisual(LayoutRoot);
Point p = gt.Transform(new Point(0, 0));
It might be worth using an ImageBrush instead. You don't need to clip your image.
<Ellipse ... >
<Ellipse.Fill>
<ImageBrush ImageSource="..."/>
</Ellipse.Fill>
</Ellipse>
Related
I post this before and it was remove for being a duplicate. It is not. My problem is different then what that other people is doing. He is not doing zoom nor pan, and does not have a boarder.
I am using Stretch="Fill" to place my entire picture in the borders of an Image box. I am using a Border so that I can do Zoom and Pan. I am using the Canvas to draw rectangles around giving click areas. I want to map the left mouse click coordinates of the Canvas with zoom and pan back to the original image. here is my XAML code :
`
<Border x:Name="VideoPlayerBorder" ClipToBounds="True" Background="Gray" >
<Canvas x:Name="CanvasGridScreen" MouseLeftButtonDown="VideoPlayerSource_OnMouseLeftButtonDown" >
<Image x:Name="VideoPlayerSource" Opacity="1" RenderTransformOrigin="0.5,0.5" MouseLeftButtonUp="VideoPlayerSource_OnMouseLeftButtonUp" MouseWheel="VideoPlayerSource_OnMouseWheel" MouseMove="VideoPlayerSource_OnMouseMove" Width="{Binding Path=ActualWidth, ElementName=CanvasGridScreen}" Height="{Binding Path=ActualHeight, ElementName=CanvasGridScreen}" Stretch="Fill" >
</Image>
</Canvas>
`
here is my C# code:
`private void VideoPlayerSource_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
VideoPlayerSource.CaptureMouse();
var tt = (TranslateTransform)((TransformGroup)VideoPlayerSource.RenderTransform).Children.First(tr => tr is TranslateTransform);
start = e.GetPosition(VideoPlayerBorder);
origin = new Point(tt.X, tt.Y);
_stIR = start;
_stIR2 = start;
addRemoveItems(sender, e);
}
private void addRemoveItems(object sender, MouseButtonEventArgs e)
{
// this is the event that will check if we clicked on a rectangle or if we clicked on the canvas
// if we clicked on a rectangle then it will do the following
if (e.OriginalSource is Rectangle)
{
// if the click source is a rectangle then we will create a new rectangle
// and link it to the rectangle that sent the click event
Rectangle activeRec = (Rectangle)e.OriginalSource; // create the link between the sender rectangle
CanvasGridScreen.Children.Remove(activeRec); // find the rectangle and remove it from the canvas
}
// if we clicked on the canvas then we do the following
else
{
// generate a random colour and save it inside the custom brush variable
Custombrush = new SolidColorBrush(Color.FromRgb((byte)r.Next(1, 255),
(byte)r.Next(1, 255), (byte)r.Next(1, 233)));
// create a re rectangle and give it the following properties
// height and width 50 pixels
// border thickness 3 pixels, fill colour set to the custom brush created above
// border colour set to black
Rectangle newRec = new Rectangle
{
Width = 50,
Height = 50,
StrokeThickness = 3,
Fill = Custombrush,
Stroke = Brushes.Black
};
// once the rectangle is set we need to give a X and Y position for the new object
// we will calculate the mouse click location and add it there
Canvas.SetLeft(newRec, Mouse.GetPosition(CanvasGridScreen).X); // set the left position of rectangle to mouse X
Canvas.SetTop(newRec, Mouse.GetPosition(CanvasGridScreen).Y); // set the top position of rectangle to mouse Y
CanvasGridScreen.Children.Add(newRec); // add the new rectangle to the canvas
}
}
private void VideoPlayerSource_OnMouseWheel(object sender, MouseWheelEventArgs e)
{
TransformGroup transformGroup = (TransformGroup)VideoPlayerSource.RenderTransform;
ScaleTransform transform = (ScaleTransform)transformGroup.Children[0];
double zoom = e.Delta > 0 ? .2 : -.2;
double transformScaleX = Math.Round((transform.ScaleX + zoom), 2);
double transformScaleY = Math.Round((transform.ScaleY + zoom), 2);
if (transformScaleX <= 8.2 && transformScaleX >= 1)
{
transform.ScaleX = Math.Round(transform.ScaleX + zoom, 2);
transform.ScaleY = Math.Round(transform.ScaleY + zoom, 2);
zoomFactor2 = zoomFactor2 + zoom;
zoomFactor = zoomFactor2;
}
}
void PanMethod(MouseEventArgs e)
{
var tt = (TranslateTransform)((TransformGroup)VideoPlayerSource.RenderTransform).Children.First(tr => tr is TranslateTransform);
Vector v = start - e.GetPosition(VideoPlayerBorder);
if (zoomFactor > 1.0)
{
tt.X = origin.X - v.X;
tt.Y = origin.Y - v.Y;
}
}
is there a function that would give me this information ? is there a way of using TransformGroup or ScaleTransform to return the actual location in the picture that was clicked? again the Image with possible zoom and/or pan
Check out: https://learn.microsoft.com/en-us/dotnet/api/system.windows.media.visual.transformtovisual
The right way to translate coordinates back to the original pre-transforms control is to use the TransformToVisual helper. It's probably a good idea to do that regardless since transforms could be applied higher up in the stack.
In your case you want to call:
GeneralTransform transform = CanvasGridScreen.TransformToVisual(VideoPlayerSource);
Point normalizedPoint = transform.Transform(new Point(0, 0));
I'm currently trying to insert random images onto a canvas using mouse click coordinates. However, I am unsure where the X & Y coordinates would be placed in the code. Any pointers would be great thanks!
private void canvas1_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Point p = Mouse.GetPosition(canvas1);
double x = p.X;
double y = p.Y;
Image myImage = new Image();
string[] imageNames = { "greenslime.png", "blueslime.png", "redslime.png", "yellowslime.png" };
var rand = new Random();
string imageName = imageNames[rand.Next(imageNames.Length)];
string imageSlime = string.Concat("", imageName);
myImage.Source = new BitmapImage(new Uri(imageSlime, UriKind.Relative));
myImage.Width = 200;
myImage.Height = 200;
canvas1.Children.Add(myImage);
}
You should use the Canvas.Top/Canvas.Left attached dependency properties.
In code behind you should use:
myImage.Source = new BitmapImage(new Uri(imageSlime, UriKind.Relative));
myImage.Width = 200;
myImage.Height = 200;
Canvas.SetLeft(myImage, x);
Canvas.SetTop(myImage, y);
canvas1.Children.Add(myImage);
Because the image is placed in the canvas, the canvas will use these properties.
In XAML it would be:
<Canvas x:Name="canvas1">
<Image Canvas.Top="10" Canvas.Left="20" Width="200" Height="200" />
</Canvas>
I am practicing C# basic WPF/XAML drawing for an assignment and right off the bat I cannot figure out why my polygons are being drawn in the wrong place.
My window is of 1280x720 fixed, non-resizeable. I am trying to programmatically create my polygons by:
Creating points in the coordinates I want them to be:
`
[0,0]
[max height, 0],
[max height, max width],
[0, max width],
[max height/2, max width/2]
`
Creating polygons that consists of three points each, [0,0] and two edges. My screen is supposed to be split into four triangles.
I tried breaking down the code to something really explicit to see if I could figure out where the issue is, so this is what I have:
private void CreatePolygons()
{
List<Point> PointList = new List<Point>
{
new Point(MainUI.Height / 2, MainUI.Width / 2),
new Point(0, 0),
new Point(0, MainUI.Height),
new Point(MainUI.Width, MainUI.Height),
new Point(MainUI.Width, 0)
};
Polygon p1 = new Polygon();
Polygon p2 = new Polygon();
Polygon p3 = new Polygon();
Polygon p4 = new Polygon();
p1.Points.Add(PointList[0]);
p1.Points.Add(PointList[1]);
p1.Points.Add(PointList[2]);
p2.Points.Add(PointList[0]);
p2.Points.Add(PointList[2]);
p2.Points.Add(PointList[3]);
p3.Points.Add(PointList[0]);
p3.Points.Add(PointList[3]);
p3.Points.Add(PointList[4]);
p4.Points.Add(PointList[0]);
p4.Points.Add(PointList[4]);
p4.Points.Add(PointList[1]);
p1.Stroke = System.Windows.Media.Brushes.LightSkyBlue;
p2.Stroke = System.Windows.Media.Brushes.LightSkyBlue;
p3.Stroke = System.Windows.Media.Brushes.LightSkyBlue;
p4.Stroke = System.Windows.Media.Brushes.LightSkyBlue;
p1.StrokeThickness = 1;
p2.StrokeThickness = 1;
p3.StrokeThickness = 1;
p4.StrokeThickness = 1;
MainGrid.Children.Add(p1);
MainGrid.Children.Add(p2);
MainGrid.Children.Add(p3);
MainGrid.Children.Add(p4);
}
The end result is a completely misplaced grid and I can't understand what the coordinates it ended up creating refer to:
What am I missing?
You have accidentally swapped the Width and Height in the first point:
new Point(MainUI.Height / 2, MainUI.Width / 2),
Should be:
new Point(MainUI.Width / 2, MainUI.Height / 2),
Further, assuming MainUI is the app window itself, the points will still be a bit off, because the Height of the window includes its title bar height. You should better use MainGrid.ActualWidth and MainGrid.ActualHeight:
List<Point> PointList = new List<Point>
{
new Point(MainGrid.ActualWidth / 2, MainGrid.ActualHeight / 2),
new Point(0, 0),
new Point(0, MainGrid.ActualHeight),
new Point(MainGrid.ActualWidth, MainGrid.ActualHeight),
new Point(MainGrid.ActualWidth, 0)
};
As an alternative to all the Polygon point calcuations, you may use this simple Path element, which produces the same output and stretches automatically:
<Grid>
<Path Stretch="Fill" Stroke="LightSkyBlue" StrokeThickness="1"
Data="M0,0 L1,0 1,1 0,1Z M0,0 L1,1 M0,1 L1,0"/>
</Grid>
Besides that you have confused Width and Height of the first point, I'd suggest not to create UI elements like Polygons in code behind. Better use an ItemsControl like this:
<Grid SizeChanged="MainUISizeChanged">
<ItemsControl x:Name="polygons">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Polygon Stroke="LightSkyBlue" StrokeThickness="1"
Points="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
and assign its ItemsSource property to a collection of PointCollections, e.g. whenever the size of your MainUI element changes:
private void MainUISizeChanged(object sender, SizeChangedEventArgs e)
{
var points = new List<Point>
{
new Point(e.NewSize.Width / 2, e.NewSize.Height / 2),
new Point(0, 0),
new Point(0, e.NewSize.Height),
new Point(e.NewSize.Width, e.NewSize.Height),
new Point(e.NewSize.Width, 0)
};
polygons.ItemsSource = new List<PointCollection>
{
new PointCollection(new Point[] { points[0], points[1], points[2] }),
new PointCollection(new Point[] { points[0], points[2], points[3] }),
new PointCollection(new Point[] { points[0], points[3], points[4] }),
new PointCollection(new Point[] { points[0], points[4], points[1] }),
};
}
I have an app that will draw many shapes (rectangle, line and circle) on a panel.
the panel can zoom in and out this shapes.
What I'm trying to do is when the application is fired I need to have the shapes zoomed to fit the window.
How I can do that, I read a lot about defined Images but not shapes.
here is my snap shot
private void panel1_Paint(object sender, PaintEventArgs e)
{
SolidBrush brushs = new SolidBrush(Color.White);
e.Graphics.Clip = new Region(new Rectangle(0, 0, Viewer.Width, Viewer.Height));
e.Graphics.FillRegion(brushs, e.Graphics.Clip);
Graphics g = e.Graphics;
g.TranslateTransform(_ImgX, _ImgY);
g.ScaleTransform(_Zoom, _Zoom);
g.SmoothingMode = SmoothingMode.AntiAlias;
SolidBrush myBrush = new SolidBrush(Color.Black);
Pen p = new Pen(Color.Red);
foreach (CircuitData.ResistorRow resistorRow in ResistorData.Resistor)
{
RectangleF rec = new RectangleF((float)(resistorRow.CenterX - resistorRow.Length/ 2), (float)(resistorRow.CenterY - resistorRow.Width/ 2), (float)resistorRow.Length, (float)resistorRow.Width);
float orientation = 360 - (float)resistorRow.Orientation;
PointF center = new PointF((float)resistorRow.CenterX, (float)resistorRow.CenterY);
PointF[] points = CreatePolygon(rec, center, orientation);
if (!Double.IsNaN(resistorRow.HiX) && !Double.IsNaN(resistorRow.HiY))
{
g.FillEllipse(myBrush, (float)resistorRow.HiX - 2 , (float)resistorRow.HiY - 2, 4, 4);
g.DrawLine(p, new PointF((float)resistorRow.HiX , (float)resistorRow.HiY ), center);
}
g.FillPolygon(myBrush, points);
}
}
Thanks
Draw your shapes onto a Metafile. Its dimensions will be calculated automatically after it is created. At the end you can zoom it safely when you draw it onto the panel.
// the reference Graphics can be taken from your form, its size does not matter
Graphics refGraph = this.CreateGraphics();
IntPtr hdc = refGraph.GetHdc();
Metafile result = new Metafile(hdc, EmfType.EmfOnly, "Shapes");
using (var g = Graphics.FromImage(result))
{
// draw your shapes here (zooming is not necessary)
DrawShapes(g);
}
refGraph.ReleaseHdc(hdc);
refGraph.Dispose();
// use this metafile on the panel
return result;
I'm using the MS asp.net charting controls. And I'm using the radar chart to draw some values, but for some reason, the lines of the X-axis doesn't really meet in the middle.
I have set the LineWidth = 1, but the line still takes like 2 pixels and some of the markers are totally off, or maybe it's the line that's totally off.
Maybe my text is a little bit off as well, so please see picture and hopefully you'll understand my problem. =)
Code that generates the chart:
// Populate series data
Chart chart1 = new Chart();
chart1.ChartAreas.Add(new ChartArea("ChartArea1"));
chart1.Height = new Unit(380);
chart1.Width = new Unit(880);
//chart1.AntiAliasing = AntiAliasingStyles.Graphics;
//chart1.BackColor = Color.Transparent;
chart1.Customize += new EventHandler(Chart_Customize);
// Show as 3D
chart1.ChartAreas["ChartArea1"].Area3DStyle.Enable3D = false;
chart1.ChartAreas["ChartArea1"].AxisY.IntervalAutoMode
= IntervalAutoMode.FixedCount;
chart1.ChartAreas["ChartArea1"].AxisY.Interval = 10;
chart1.ChartAreas["ChartArea1"].AxisY.Maximum = 100;
chart1.ChartAreas["ChartArea1"].AxisY.IsReversed = true;
chart1.ChartAreas[0].AxisY.LineWidth = 1;
chart1.ChartAreas[0].AxisY.MajorGrid.LineColor = Color.Gray;
chart1.ChartAreas[0].AxisY.LineColor = Color.Gray;
chart1.ChartAreas[0].AxisY.MajorTickMark.Enabled = false;
List<string> names = new List<string>();
int namecounter = 1;
foreach (var p in Model.Participants)
{
if (SessionHandle.ShowNamesInDiagrams)
names.Add(p.Person.Name);
else
names.Add(namecounter.ToString());
namecounter++;
}
#region firstresult
if (SessionHandle.ShowFirstResult)
{
chart1.Series.Add(new Series("FirstResult"));
List<double> firstresult = new List<double>();
foreach (var p in Model.Participants)
{
var resultSummary = from r in Model.ResultSummary
where r.userID == p.ParentID && Model
.Modules
.Where(x => x.hasResult)
.ToList()
.Exists(x => x.ID == r.moduleID)
select r;
firstresult.Add(resultSummary.Sum(x => x.scorePercent)
/ Model.Modules.Where(x => x.hasResult).Count());
}
chart1.Series["FirstResult"].Points.DataBindXY(names, firstresult);
// Set radar chart type
chart1.Series["FirstResult"].ChartType = SeriesChartType.Radar;
// Set radar chart style (Area, Line or Marker)
chart1.Series["FirstResult"]["RadarDrawingStyle"] = "Marker";
chart1.Series["FirstResult"].Color = Color.DarkBlue;
chart1.Series["FirstResult"].MarkerImage
= Url.Content("~/Content/Images/firstresult.png");
// Set circular area drawing style (Circle or Polygon)
chart1.Series["FirstResult"]["AreaDrawingStyle"] = "Circle";
// Set labels style (Auto, Horizontal, Circular or Radial)
chart1.Series["FirstResult"]["CircularLabelsStyle"] = "Horizontal";
}
#endregion
WPF coordinates refer to the center of the pixel, not the corners, so try adding 0.5 to all your coordinates. To show this is the case consider the following xaml:
<Canvas>
<Line X1="50" Y1="50" X2="100" Y2="50" Stroke="Black" StrokeThickness="1" />
<Line X1="50" Y1="50" X2="50" Y2="100" Stroke="Black" StrokeThickness="1" />
<Line X1="50" Y1="50" X2="100" Y2="100" Stroke="Black" StrokeThickness="1" />
</Canvas>
Here it is rendered normally and then with a 0.5 pixel offset applied to each coordinate: