In a WPF User Control, I am trying to mask an Image control with a circle that can vary in position and size. The user left-drags to change the circle radius and right-drags to change the center point of the ellipse. I'm capturing all the needed values correctly and can properly set the radius of the mask. The problem is, that no matter what point I use for the center of the ellipse, it is drawn from the center of the Image control. Any ideas?
Below is the code that sets the mask:
private void SetMask(double _Radius)
{
EllipseGeometry MaskGeometry = new EllipseGeometry(CenterPos, _Radius, _Radius);
GeometryDrawing MaskDrawing = new GeometryDrawing(Brushes.Black, null, MaskGeometry);
DrawingBrush MaskBrush = new DrawingBrush(MaskDrawing);
MaskBrush.Stretch = Stretch.None;
Img.OpacityMask = MaskBrush; //Img is the Image control
}
You also have to set the ViewboxUnits, AlignmentX, and AlignmentY properties:
private void SetMask(double radius)
{
var maskGeometry = new EllipseGeometry(CenterPos, radius, radius);
var maskDrawing = new GeometryDrawing(Brushes.Black, null, maskGeometry);
var maskBrush = new DrawingBrush
{
Drawing = maskDrawing,
Stretch = Stretch.None,
ViewboxUnits = BrushMappingMode.Absolute,
AlignmentX = AlignmentX.Left,
AlignmentY = AlignmentY.Top
};
Img.OpacityMask = maskBrush;
}
You need to modify the Viewport and ViewportUnits property of your DrawingBrush base tile :
private void button1_Click(object sender, RoutedEventArgs e)
{
EllipseGeometry MaskGeometry = new EllipseGeometry(CenterPos, _RadiusX, _RadiusY);
GeometryDrawing MaskDrawing = new GeometryDrawing(Brushes.Black, null, MaskGeometry);
DrawingBrush MaskBrush = new DrawingBrush(MaskDrawing);
MaskBrush.Stretch = Stretch.None;
MaskBrush.ViewportUnits = BrushMappingMode.Absolute;
MaskBrush.Viewport = MaskGeometry.Bounds;
Img.OpacityMask = MaskBrush; //Img is the Image control
}
MSDN document here gives a good explanation of how controls are painted using TileBrush.
Related
I am creating a project that needs to have a vertical scroll bar with multiple pictures like the server explorer in discord:
For example:
how can I mimic in WinForms in C# (not only having pictures scrolling but also the pictures can have events attached to them?
First you need add a Parent Panel, i'm used the pnServers.
Set property value:
AutoScroll = True;
On Code Behind you can create a List of Rounded Pictures.
private void DiscordServerBarExample_Load(object sender, System.EventArgs e)
{
// Example, in your case this looping is based on return (Database, Api, ...).
for (int i = 1; i <= 10; i++)
{
Panel pnServer = new Panel()
{
Dock = DockStyle.Top,
Height = pnServers.Width,
Padding = new Padding(10)
};
RoundedPictureBox serverImage = new RoundedPictureBox()
{
SizeMode = PictureBoxSizeMode.CenterImage,
Dock = DockStyle.Fill
};
serverImage.Image = Properties.Resources._255352;
pnServer.Controls.Add(serverImage);
pnServers.Controls.Add(pnServer);
}
}
Rounded Picture Box Code:
public class RoundedPictureBox : PictureBox
{
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (GraphicsPath gp = new GraphicsPath())
{
gp.AddEllipse(0, 0, Width - 1, Height - 1);
Region rg = new Region(gp);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.DrawEllipse(new Pen(new SolidBrush(this.BackColor), 1), 0, 0, this.Width - 1, this.Height - 1);
Region = rg;
}
}
}
And this is final result.
I am creating some shapes from code behind dynamically and adding them to a Grid and further add the Grid to Canvas.
So when I double click on a shape I should be able to add some text which works fine. Now lets say I have two shapes on the Canvas and when I try to draw a line between these shapes for some reason the first shape gets pulled away to the bottom and the line starts from the middle of first shape.
I want the shape not to change the position and the line should start from the bottom of first shape. Please see the image for my problem.
Please help with your thoughts. Here is my code. Also I tried numerous posts eg: Getting the top left coordinates of a WPF UIElement.
But none of them seem to help.
private void CvsSurface_OnDrop(object sender, DragEventArgs e) //In this event I am creating a shape dynamically and adding to a grid which is then added to a canvas.
{
Shape result = null;
Object droppedData = e.Data; //This part is not important
/*Translate Drop Point in reference to Stack Panel*/
Point dropPoint = e.GetPosition(this.cvsSurface);
//Console.WriteLine(dropPoint);
//Label lbl = new Label();
//lbl.Content = draggedItem.Content;
UIElement element = draggedItem.Content as UIElement;
Shape s = element as Shape;
if (s is Ellipse)
{
Ellipse ellipse = new Ellipse()
{
Height = s.Height,
Width = s.Width,
Fill = s.Fill
};
result = ellipse;
}
else if (s is Rectangle)
{
Rectangle rectangle = new Rectangle()
{
Height = s.Height,
Width = s.Width,
Fill = s.Fill
};
result = rectangle;
}
Grid sp = new Grid();
sp.Children.Add(result);
sp.MouseLeftButtonDown += Sp_MouseLeftButtonDown;
sp.MouseLeftButtonUp += Sp_MouseLeftButtonUp;
//sp.PreviewMouseLeftButtonUp += Sp_PreviewMouseLeftButtonUp;
//sp.MouseLeftButtonUp += Sp_MouseLeftButtonUp;
cvsSurface.Children.Add(sp);
Canvas.SetLeft(sp, dropPoint.X);
Canvas.SetTop(sp, dropPoint.Y);
}
private void Sp_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) // The purpose of this event lets say when some one clicks on a shape and drags the mouse to the other shape and when mouse up I want to draw a line between the shapes.
{
bool mouserelease = System.Windows.Input.Mouse.LeftButton == MouseButtonState.Pressed;
if (!mouserelease)
{
x2 = e.GetPosition(stackpanel).X;
y2 = e.GetPosition(stackpanel).Y;
Line l = new Line();
l.X1 = x1;
l.Y1 = y1;
l.X2 = x2;
l.Y2 = y2;
l.Margin = new Thickness(0, 19, 0, 0);
l.Stroke = new SolidColorBrush(Colors.Black);
l.StrokeThickness = 2;
stackpanel.Children.Add(l);
}
}
private void Sp_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) //This method lets say if user clicks twice then he wants to add some text or if he single clicks then I am assuming he is trying to a drag and draw a line
{
stackpanel = sender as Grid; //Sorry, the stackpanel is a global variable name of type Grid. Its actually Grid stackpanel;
if (e.ClickCount == 2)
{
dialog = new UserDialog()
{
DataContext = this,
Height = 180,
Width = 400,
MaxHeight = 180,
MaxWidth = 400
};
dialog.ShowDialog();
}
else
{
x1 = e.GetPosition(stackpanel).X + 18;
y1 = e.GetPosition(stackpanel).Y + 18;
//x1 = GetPosition(stackpanel, cvsSurface).X;
//y1 = GetPosition(stackpanel, cvsSurface).Y;
}
}
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.
I'm developing a WinForms application and can't figure out how to resolve an issue.
I need to show an image in a Form. Because the image can be arbitrarily large, I need scrollbars on the picturebox containing the image so the user can see it entirely.
Googling around I found out the best way to achieve this is to add the PictureBox as a Child Control of a Panel, and making the Panel autosizable and autoscrollable.
I did that programatically since using the designer I was unable to insert the picturebox as a child control of the panel.
The problem I'm now facing is that I can't seem to be able to center and scroll the picturebox at the same time.
If I put the anchor of the picturebox to top,left,bottom,right, the scrollbars are not shown and the image displayed is strange, if I put back the anchor to only top-left, the image is not centered.
Is there any way to do both at the same time?
Here's the code for my Panel and Picturebox:
this.panelCapturedImage = new System.Windows.Forms.Panel();
this.panelCapturedImage.SuspendLayout();
this.panelCapturedImage.AutoScroll = true;
this.panelCapturedImage.AutoSize = true;
this.panelCapturedImage.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.panelCapturedImage.Controls.Add(this.pictureBoxCapturedImage);
this.panelCapturedImage.Location = new System.Drawing.Point(0, 49);
this.panelCapturedImage.Name = "panelCapturedImage";
this.panelCapturedImage.Size = new System.Drawing.Size(3, 3);
this.panelCapturedImage.TabIndex = 4;
this.pictureBoxCapturedImage.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.pictureBoxCapturedImage.Location = new System.Drawing.Point(0, 0);
this.pictureBoxCapturedImage.Name = "pictureBoxCapturedImage";
this.pictureBoxCapturedImage.Size = new System.Drawing.Size(0, 0);
this.pictureBoxCapturedImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage;
this.pictureBoxCapturedImage.TabIndex = 0;
this.pictureBoxCapturedImage.TabStop = false;
this.panelCapturedImage.Controls.Add(this.pictureBoxCapturedImage);
And here's where I set the image:
public Image CapturedImage
{
set
{
pictureBoxCapturedImage.Image = value;
pictureBoxCapturedImage.Size = value.Size;
}
}
For the PictureBox, set SizeMode = AutoSize, Anchor it Top, Left, and set its Location to 0, 0.
Set Panel.AutSize to False and Panel.AutoScroll to True.
When you set the PictureBox.Image property, it will auto-size to the size of the image. You can then use that size to set the panel's AutoScrollPosition property:
public Image CapturedImage
{
set
{
pictureBoxCapturedImage.Image = value;
panelCapturedImage.AutoScrollPosition =
new Point {
X = (pictureBoxCapturedImage.Width - panelCapturedImage.Width) / 2,
Y = (pictureBoxCapturedImage.Height - panelCapturedImage.Height) / 2
};
}
}
If the image is smaller then then panel's size, it will remain in the upper left corner. If you want it centered within the panel, you'll have to add logic to set its Location appropriately.
Based on earlier answers I was able to create this full example:
private void testShowPictureBox()
{
/* format form */
Form frmShowPic = new Form();
frmShowPic.Width = 234;
frmShowPic.Height = 332;
frmShowPic.MinimizeBox = false;
frmShowPic.MaximizeBox = false;
frmShowPic.ShowIcon = false;
frmShowPic.StartPosition = FormStartPosition.CenterScreen;
frmShowPic.Text = "Show Picture";
/* add panel */
Panel panPic = new Panel();
panPic.AutoSize = false;
panPic.AutoScroll = true;
panPic.Dock = DockStyle.Fill;
/* add picture box */
PictureBox pbPic = new PictureBox();
pbPic.SizeMode = PictureBoxSizeMode.AutoSize;
pbPic.Location = new Point(0, 0);
panPic.Controls.Add(pbPic);
frmShowPic.Controls.Add(panPic);
/* define image */
pbPic.ImageLocation = #"c:\temp\pic.png";
frmShowPic.ShowDialog();
}
The picturebox has to be set to autosize. anchored at the center (or a border).
You could manage all of this in the designer, don't undertand your problem with that.
The panel has to be set to autoscroll to true.
I'm creating a Windows Forms application. How do I capture the size of the windows form?
Currently I have something that looks like this in my code:
PictureBox display = new PictureBox();
display.Width = 360;
display.Height = 290;
this.Controls.Add(display);
display.Image = bmp;
However, the size of my display is hard-coded to a specific value.
I know that if I want to draw a square that re-sizes I can use something like this:
private Rectangle PlotArea;
private int offset = 30;
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
//// Calculate the location and size of the plot area
//// within which we want to draw the graphics:
Rectangle ChartArea = ClientRectangle;
PlotArea = new Rectangle(ChartArea.Location, ChartArea.Size);
PlotArea.Inflate(-offset, -offset);
Draw PlotArea:
g.DrawRectangle(Pens.Black, PlotArea);
}
Is there a way for me to use method one and grab the size of the form for Height and Width?
I've tried the following code, but it doesn't work...
PictureBox display = new PictureBox();
display.Width = ClientRectangle.Y;
display.Height = ClientRectangle.X;
this.Controls.Add(display);
display.Image = bmp;
For Getting the size of a Windows Form:
int formHeight = this.Height;
int formWidth = this.Width;
PictureBox display = new PictureBox();
display.Width = ClientRectangle.Width;
display.Height = ClientRectangle.Height;
this.Controls.Add(display);
display.Image = bmp;
When you resize the window:
private void Form1_Resize(object sender, System.EventArgs e)
{
Control control = (Control)sender;
// set the PictureBox width and heigh here using control.Size.Height
// and control.Size.Width
}
You want to set Dock = DockStyle.Fill to dock the control to fill its parent.
Set it to ClientRectangle.Width.