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.
Related
After moving my PictureBox around with the arrows, how would I go about using the center button to reset my PictureBox back the the center of the Form?
You can put this code in the centre button click.
pictureBox.Left = (this.ClientSize.Width - pictureBox.Width) / 2 ;
pictureBox.Top = (this.ClientSize.Height - pictureBox.Height) / 2;
Or you can use pictureBox.CenterToParent();, it will position the control within the centre of the parent.
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.
I have a WP8 app where I display buttons on a wheel. When clicked, I want the wheel to turn and bring the clicked button on the top of it.
I'm placing the buttons with their margin property. I figured I'd use a double while loop to move the buttons along the circle degree by degree, like this (trying to simplify it as much as I can) :
private void Select(Button selectedButton)
{
Button[] SelectedButtons = { button1, button2, button3 };
//Calculates the distance between buttons in degrees
double buttonDistance = 360 / SelectedButtons.Length;
double angleDeg;
double angleRad;
int i = 0;
int j = 0;
//While the selected button hasn't reach top position, move the wheel
while (selectedButton.Margin != new Thickness(<top position of the wheel>)
{
//Moves the buttons of a single degree
while (i < SelectedButtons.Length)
{
//Calculates the position of the current button in the array in degrees and converts it in radians
angleDeg = j + 90 + i * buttonDistance;
angleRad = Math.PI * angleDeg / 180;
//Button positioning
SelectedButtons[i].Margin = new Thickness(<calculation of the margins with the given angles>);
i++;
}
System.Threading.Thread.Sleep(5);
j++;
i = 0;
}
}
However, and even with the Sleep() function, the buttons' positions only update once the loop is over, and don't animate like I want them too.
I tried to use UpdateLayout() in the loop, both on the ContentPanel and on the Buttons themselves, I tried InvalidateArrange() and InvalidateMeasure(), or even adding a canvas in, but nothing does it : the buttons only update when the whiles are over, never before.
Can you guys please help me on this ?
Quick update, for anyone who could stumble upon the same issue :
Indeed, as steveg89 said, you can't update UIElements right from the GUI thread. The link he gave suggests using a background worker, or delegates, to get it to work. Unfortunately, WP8 doesn't allow it either.
The only solution is switching to storyboard animations : http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj206955(v=vs.105).aspx
Not extremely easy for a absolute beginner, but it works fine when you take the time to understand how these work and their different types (simple animations, keyframe animations, or path animations (however path animations are not supported in Windows Phone. Lost quite a bit of time to it.))
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.