Description Created a form in Winform C# app.
Added a panel (PBack) with dock type fill. (Scrollable)
Then added a picturebox(pbDraw) in panel(PBack) that height depends upon image size.
I want to add a control on the bottom left of the current screen view. (assume client scrolled down)
What i tried
Rectangle rect = Screen.GetWorkingArea(pbDraw);
ctrl.Top = rect.Top + rect.Height;
ctrl.Top = Screen.PrimaryScreen.WorkingArea.Top + Screen.PrimaryScreen.WorkingArea.Height;
ctrl.Top = Screen.FromControl(pbDraw).WorkingArea.Top+ Screen.FromControl(pbDraw).WorkingArea.Height;
Issue control is adding at top of pbDraw (0,0) and not on current screen bounds top.
As far as I understand, Control.Top takes the scrolled view into account all by itself.
Gets or sets the distance, in pixels, between the top edge of the control and the top edge of its container's client area.
So, you should be able to assign the coordinates relevant to the pBack and its client view:
ctrl.Top = pBack.Height - ctrl.Height;
Assuming that ctrl is a child of pBack this code should place it at the bottom of the current (scrolled) part of pBack
UPDATE:
As you said in the comments, ctrl is actually a child of pbDraw. In this case, you'll need to take the scrolling into account. For that, you can use Panel.VerticalScroll:
//scroll position + panel height - control height
ctrl.Top = pBack.VerticalScroll.Value + pBack.Height - ctrl.Height;
If I were you, I'd add ctrl to the panel, on top of the picture box. This will make it easier to calculate offsets relative to the panel.
Related
I have a "Screen Space - Camera" Canvas in Unity with several buttons inside. When I click a button, it opens a popup (UI Panel), outside of the button's parent, which I want to align to the specific button I pressed. I can position it over the button by simply matching its "rectTransform.position" to the button's rectTransform.position, but that just centers it over the button. I want its edge to line up with the edge of the button's edge.
I tried to get the width of the button and the width of the popup and then just shift it over by the width / 2, but the problem is the values are way off because of the type of canvas I'm using (I'm assuming that's issue). I can't change the canvas type for my game. I tried using Canvas.scaleFactor too, but it doesn't seem to work or I don't know how to use it.
The other big issue is that the button is deep down in the Canvas's hierarchy, but the popup is a direct child of the Canvas.
Here's what I tried so far:
// Popup position & size
RectTransform popupPOS = popupCanvas.transform.GetComponent<RectTransform>();
float popupRealWidth = popupPOS.rect.width * mainCanvas.scaleFactor;
// Button position & size
RectTransform btnPOS = gameObject.GetComponent<RectTransform>();
float btnRealWidth = btnPOS.rect.width * mainCanvas.scaleFactor;
// Position popup to position of button
popupCanvas.gameObject.GetComponent<RectTransform>().position = new Vector3(btnPOS.position.x, btnPOS.position.y, btnPOS.position.z);
// Move Rect.x by "RealWidth"
popupPOS.localPosition = new Vector3(popupPOS.localPosition.x + popupRealWidth / 2, popupPOS.localPosition.y, popupPOS.localPosition.z);
I found a solution. Here it is in case anyone is trying to figure this out in the future.
There was no need to use "scaleFactor". I feel like I tried this before and it didn't work. Maybe it's because I didn't follow the "PEMDAS" rule...sigh. My high school math teacher would be shaking her head.
popupPos.localPosition = new Vector3(popupPos.localPosition.x + (popupPos.rect.width / 2) + (btnPos.rect.width / 2), popupPos.localPosition.y - (popupPos.rect.height / 2) + (btnPos.rect.height / 2), popupPos.localPosition.z);
This aligns the popup (ui panel) to the top right of the button.
this.Parent = gameObject; //Or whatever your button is named
this.StartPosition = FormStartPosition.CenterParent;
I believe this is what you are looking for.
I want to create a simple Adorner that marks a selected element with a bounding box. I want it to be sharp and be exactly one pixel outside the target content. I found some useful code, but it does not work exactly as I wanted it to.
The OnRender method of my SelectAdorner is:
Rect adornedElementRect = new Rect(AdornedElement.DesiredSize);
SolidColorBrush renderBrush = new SolidColorBrush(Colors.Transparent);
Pen renderPen = new Pen(new SolidColorBrush(Colors.LightBlue), 1);
double halfPenWidth = renderPen.Thickness / 2;
// Create a guidelines set
GuidelineSet guidelines = new GuidelineSet();
guidelines.GuidelinesX.Add(adornedElementRect.Left + halfPenWidth);
guidelines.GuidelinesX.Add(adornedElementRect.Right + halfPenWidth);
guidelines.GuidelinesY.Add(adornedElementRect.Top + halfPenWidth);
guidelines.GuidelinesY.Add(adornedElementRect.Bottom + halfPenWidth);
drawingContext.PushGuidelineSet(guidelines);
drawingContext.DrawRectangle(renderBrush, renderPen, adornedElementRect);
The problem is that the bounding box is not aligned properly around the content (see second and third item). I want the result to be the one from the bottom item in the picture below.
Any ideas how can I achieve what I want?
Also, it would be nice to have the Adorner working in the same way with renderPen.Thickness greater than 1, in case I need that.
Thanks for the help.
The following code needs to be added before creating the GuidelineSet:
adornedElementRect = new Rect(
adornedElementRect.Left - halfPenWidth,
adornedElementRect.Top - halfPenWidth,
adornedElementRect.Width + renderPen.Thickness,
adornedElementRect.Height + renderPen.Thickness
);
The problem comes from the fact that adornedElementRect overlaps AdornedElement when the pen thickness is taken into consideration. The value of AdornedElement.DesiredSize corresponds to the outer edge of the border. That means that the Adorner is drawn half over the border and half outside the border. The GuidelineSet then aligns the right side of vertical edges and the bottom side of horizontal edges to pixel boundaries. This makes the drawing look sharp, however the edges are moved either to the inside, either to the outside of the AdornedElement. This is what creates the artifact.
When adding the specified changes to adornedElementRect, then it is drawn outside and right next to the AdornedElement. Now, the GuidelineSet aligns the Adorner similarly with the Border control. See this link for more information about how WPF draws the content on the screen.
I have a PictureBox docked with Fill inside a larger control. The PictureBox is set to scale my image, but I don't want to scale the image larger than the original. Hence, my PictureBox has a maximum size set. As long as the container is smaller than the picture box, the image is fine. As the container expands beyond the maximum size of the PicutreBox, it is obvious that the picture box is tied to the top left. I would rather have the box centered vertically and horizontally in the parent. How do I make the Dock behavior fill from center rather than top left?
Use the Layout Anchor property of the PictureBox. You need to set it to "Top, Left, Bottom, Right" instead of using Dock.Fill. You can set this in the property window for the PictureBox:
Advantage of anchors against docking: the (in this case PictureBox) container can be positioned everywhere, but still be relative to other components in the parent panel/container. You can do this using Dock.Fill only with Layouts (different panels).
I think what you are looking for is the Anchor style of None, which will make the control "float" in the middle of the control. The catch though is that now you have to "initially" center it yourself:
PictureBox pb = new PictureBox();
pb.SizeMode = PictureBoxSizeMode.AutoSize;
pb.Anchor = AnchorStyles.None;
pb.Image = bmp;
pb.Location = new Point((this.ClientSize.Width / 2) - (pb.Width / 2),
(this.ClientSize.Height / 2) - (pb.Height / 2));
this.Controls.Add(pb);
this.AutoScrollMinSize = pb.Size;
After further thought, I solved the issue with this line of code:
_box.SizeChanged += (sender, args) => _box.SizeMode = _box.Width < _cross.Width || _box.Height < _cross.Height ? PictureBoxSizeMode.Zoom : PictureBoxSizeMode.CenterImage;
I'm working on a WPF project where I have a grid of thumbnails (each thumbnail is a SurfaceButton), and clicking on each thumbnail shows its own popup menu. The popup menu in each case has its 'sibling' photo thumbnail as its placement target. The thumbnail and popup sit alongside each other in a Grid.
I've designed the popup to emulate the kind of popout scrollable menu that you'd find in iOS, complete with a little triangle arrow that points up to the thumbnail the popup relates to (with the popup appearing below the thumbnail).
But, much to my pleasant surprise, if you click on a thumbnail that's at the bottom of the screen, WPF moves the popup above the thumbnail so that it's not off-screen.
This is great, but how can I compare the positions of the two elements and move the arrow accordingly (or hide it and show another), so that the popup above the thumbnail is pointing down to it?
Hope that makes some sense!
I've tried VisualTreeHelper.GetOffset, but that only returns the offset from the element's parent, and these seem to always be 0,0 or 1,1. I've also tried UIElement.PointToScreen, but the numbers I get back from the two elements don't seem to vary when the popup moves.
I'm sure there's a simple solution that I'm missing.
You can get your popup's actual placement like below
// ComboBox for example:
Popup popup = yourcombobox.Template.FindName("PART_Popup", yourcombobox) as Popup;
popup.Opened += (s, a) => {
// structure inside will be different by your templates
Dacorator decorator = (s as popup).Child as Decorator;
Border border = decorator.Child as Border;
Double y0 = yourcombobox.PointToScreen(new Point(0,0)).Y;
Double y1 = border.PointToScreen(new Point(0,0)).Y;
if(y0 > y1) {
// when shows at the top
yourcombobox.Background = Brushes.Red;
} else {
// when shows at the botttom
yourcombobox.Background = Brushes.Blue;
}
};
Popup doesn't have it's own size or position, so you have to catch the child's position only when popup is shown.
https://learn.microsoft.com/en-us/dotnet/framework/wpf/controls/popup-overview
I'm trying to center a fixed size control within a form.
Out of interest, is there a non-idiotic way of doing this? What I really want is something analogous to the text-align css property.
At the moment, I'm setting the padding property of the surrounding form to a suitable size and setting the Dock property of the control to fill.
You could achieve this with the use of anchors. Or more precisely the non use of them.
Controls are anchored by default to the top left of the form which means when the form size will be changed, their distance from the top left side of the form will remain constant. If you change the control anchor to bottom left, then the control will keep the same distance from the bottom and left sides of the form when the form if resized.
Turning off the anchor in a direction will keep a control centered when resizing, if it is already centered. In general, a control not anchored maintains its proportional position to the dialog. E.g. If you put a control at X=75% of the dialog width and turn off left/right anchors, the control will maintain its center at X=75% of the dialog width.
NOTE: Turning off anchoring via the properties window in VS2015 may require entering None (instead of default Top,Left)
myControl.Left = (this.ClientSize.Width - myControl.Width) / 2 ;
myControl.Top = (this.ClientSize.Height - myControl.Height) / 2;
Since you don't state if the form can resize or not there is an easy way if you don't care about resizing (if you do care, go with Mitch Wheats solution):
Select the control -> Format (menu option) -> Center in Window -> Horizontally or Vertically
I found a great way to do this and it will work with multiple controls. Add a TableLayout with 3 columns. Make the center column an absolute size (however much room you need). Set the two outside columns to 100%. Add a Panel to the center column and add any controls you need and place them where you want. That center panel will now remain centered in your form.
To center Button in panel o in other container follow this step:
At design time set the position
Go to properties Anchor of the button and set this value as the follow image
In addition, if you want to align it to the center of another control:
//The "ctrlParent" is the one on which you want to align "ctrlToCenter".
//"ctrlParent" can be your "form name" or any other control such as "grid name" and etc.
ctrlToCenter.Parent = ctrlParent;
ctrlToCenter.Left = (ctrlToCenter.Parent.Width - ctrlToCenter.Width) / 2;
ctrlToCenter.Top = (ctrlToCenter.Parent.Height - ctrlToCenter.Height) / 2;
You can put the control you want to center inside a Panel and set the left and right padding values to something larger than the default. As long as they are equal and your control is anchored to the sides of the Panel, then it will appear centered in that Panel. Then you can anchor the container Panel to its parent as needed.
It involves eyeballing it (well I suppose you could get out a calculator and calculate) but just insert said control on the form and then remove any anchoring (anchor = None).
you can put all your controls to panel and then write a code to move your panel to center of your form.
panelMain.Location =
new Point(ClientSize.Width / 2 - panelMain.Size.Width / 2,
ClientSize.Height / 2 - panelMain.Size.Height / 2);
panelMain.Anchor = AnchorStyles.None;
To keep the control centered even the form or parent control were resize.
Set the following properties of the parent element (you can set it through the property window):
parentControl.AutoSize = true;
parentControl.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
Put this code in the Resize event of the form or the parent control (if the control is inside of another control).
controlToCenter.Left = (parentControl.Width- controlToCenter.Width) / 2;
controlToCenter.Top = (parentControl.Height - controlToCenter.Height) / 2;
If the parent control is docked to the form, add this line of code.
//adjust this based on the layout of your form
parentControl.Height = this.ClientSize.Height;
So, I am currently working on a pagination control and I came up with the following to achieve the below result.
Add a PanelLayout to your container (e.g. form or usercontrol)
Set the PanelLayout properties:
Dock: Bottom (or Top)
AutoSize: False
This will center the PanelLayout horizontally.
Now, in your codebehind, do something like this:
public MyConstructor()
{
InitializeComponent();
for (var i = 0; i<10; i++)
{
AddButton(i);
}
}
void AddButton(int i)
{
var btn = new Button();
btn.Width = 30;
btn.Height = 26;
btn.Text = i.ToString();
this.flowLayoutPanel1.Controls.Add(btn);
btn.Anchor = AnchorStyles.None;
}
There is a ceveat, however. If I make my form too small (horizontally) buttons will "disappear" outside of the viewport. In my case, that's not a problem, but you could take care of this by writing code that listens to the Resize event, and remove elements (buttons) based on the viewport Width.