I'm creating a simple WPF app in which one of the core features would be that wherever the user clicks on the grid inside the main window, a number of buttons should appear around the position of the click.
Now, I try to achieve this with only 1 button. I know that I have to capture the current position of the mouse and then modify the 4 arguments of the Margin of the button (left, top, right, bottom) by creating new instances of Thickness-es.
I managed to create new Thickness-es to the Margins, with the left and top argument set to the mouse X and Y cordinates respectively, but I don't know how to calculate or what to use as the right, and bottom arguments of the newly created Margins.
Here is the relevant function from the xaml.cs (the values in question are indicated as 0-s and grid is intented to refer to the grid):
private void Grid_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
var mouseLocation = PointToScreen(Mouse.GetPosition(grid));
RandomButton.Margin = new Thickness(mouseLocation.X, mouseLocation.Y, 0, 0);
}
Here is the relevant part of the xaml:
<StackPanel>
<Button
Name="RandomButton"
Height="30"
Width="30"
Background="#FF130889"
Click="RandomButton_Click"
Content="RandomContent" />
</StackPanel>
It is also worth mentioning that when the button's HorizontalAlignment is set to Left and the VerticalAlignment is set to top, the button seem to do what I want with this setup, but only, when the windowsize is full.
I think I have to use the actual height of the window or the grid, but I don't know how. I know it is something simple, but I just started working with WPF, so I apreciate any kind of help!
As far as I understood the problem is in determining the click relative position.
In this case you can use Mouse.GetPosition method.
Here is the example with Canvas:
private void SetPos()
{
var relativePosition = Mouse.GetPosition(this.MainCanvas);
Canvas.SetLeft(this.btn1, relativePosition.X);
Canvas.SetTop(this.btn1, relativePosition.Y);
btn1.Visibility = Visibility.Visible;
}
Related
hello I have the following problem: I want to draw a rectangle on the canvas with methods Canvas.SetLeft() and Canvas.SetTop().
I use the method UserControl_Loaded() and everything works.
the problem is that having ActualWidth when resizing the window and therefore the grid, the value does not change and I left with the values no longer accurate.
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Rectangle rett = new Rectangle();
rett.Height = grid1.ActualHeight-10;
rett.Width = grid1.ActualWidth -10;
rett.Fill = new SolidColorBrush(Colors.LightBlue);
canv.Children.Add(rett);
Canvas.SetLeft(rett, 10);
Canvas.SetTop(rett, 10);
}
this is the xaml:
<Grid x:Name="grid1">
<Canvas x:Name="canv" Height="auto" Width="auto"></Canvas>
</Grid>
in the first picture it is fine when not resize the window.
the second when I resize the grid remains the previous width.
I want the width of the rectangle was updated when changing the width of the grid.
Thank you.
Without a good, minimal, complete code example that clearly illustrates your question, along with a detailed explanation of what you're actually trying to accomplish (especially in a broader sense), it is impossible to know for sure what the best answer in your case would be.
Taking your question literally, it seems one possible approach would be to bind the Rectangle dimensions to the Grid's dimensions, so that they are updated as the Grid changes size. You can use IValueConverter to subtract the appropriate amount from the actual dimensions.
But that's a fairly complicated solution for what would otherwise be a reasonably simple problem, and especially so given that you seem to be doing this in code-behind for some reason (not ideal in the first place, and setting up bindings in code-behind is particularly tedious).
Idiomatically, what you should probably be doing is not putting the Rectangle in the Canvas at all, but rather making it a child of the Grid directly. Then you can set its alignments to Stretch so that it will fill the grid cell it's in. Finally, you can set its margins so that you have the 10 pixel gap on the top and left, and no gap on the right and bottom.
For example:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Rectangle rett = new Rectangle();
rett.Fill = new SolidColorBrush(Colors.LightBlue);
// NOTE: technically don't need to set these, as Stretch is the default value!
rett.HorizontalAlignment = HorizontalAlignment.Stretch;
rett.VerticalAlignment = VerticalAlignment .Stretch;
// 10 pixels of margin on top and left, none on right and bottom
rett.Margin = new Thickness(10, 10, 0, 0);
grid1.Children.Add(rett);
}
Doing it as above allows the XAML layout engine to automatically handle the resizing behavior you are looking for.
All that said, I would definitely encourage you to implement this in XAML instead of code-behind. There are a lot of things code-behind is good at, but frankly XAML is much better at any of the things directly related to the configuration of your GUI object graph.
I want to draw a GUI Button on top of a GUI window that I have in my game, but no matter what I try the button always appears behind.
This is my Code:
GUI.depth = -30;
Rect navBackButton = new Rect(10, 10, 10, 10);
// DRAW NAVIGATION BUTTONS
if (GUI.Button(navBackButton, navBackButtonTexture))
{
// DO LOGIC HERE
}
GUI.depth = 10;
topScrollRect = new Rect(0, 0, Screen.width, topScrollHeight);
topScrollListSize = new Vector2(topScrollRect.width - 2*listMargin.x, topScrollRect.height - 2*listMargin.y);
GUI.skin.window = topStyle;
GUI.Window(0, topScrollRect, (GUI.WindowFunction)DoTopScrollWindow, "");
I have tried to draw the buttons before drawing the window, and the other way round, but both have the same result.
In this forum post (http://forum.unity3d.com/threads/setting-depth-of-a-window.12554/), it says that any GUI control with a depth of less than 1 will appear in front of GUI Windows, but I have set my depth to -30 and still the button appears behind the Window.
Any suggestions would be greatly appreciated!
The way to do this is to draw the button in DoTopScrollWindow().
You shouldn't try to place the buttons "on top" of the window, you should put them "in" the window, as the window is your navigation button's parent, conceptually. Additionally, drawing them in the window means that you can take advantage of relative positioning and window resizing without having to write a lot of extra code.
According to the docs, the parameter func in GUI.Window() is a function designed to display content inside the window.
In this case you want your buttons "inside" that window, so draw them in the function you pass in as func.
The General Problem
The application is C# WinForms .Net 4.0.
I have a SplitContainer that takes up most of the form, it is set to Anchor in all directions so it re-sizes along with the form. The left panel (Panel1) has a simple menu, no problems here. The right panel (Panel2) is more complex and contains a number of nested tab controls (with lots of controls) - it is painfully complex, but it's not changing.
The problem is that re-sizing the form doesn't work so well. In fact, if you resize by dragging the edges slowly then it works ok, but drag quickly or use the "restore" button (top-right of form) then the issue occurs.
My Control Hierarchy
The following is a simple example of my control hierarchy, its definitely a cut down version but does highlight the nested tab control which may help with replication:
Form
Split Container (anchor: top, left, bottom, right)
SC Panel1 (min width: 300)
TreeViewControl (forget what it is called)
SC Panel2
Panel (anchor: top, left, bottom, right)
Tab Control (anchor: top, left, bottom, right)
Tab Control w/ lots of pages that overflow screen and require the navigation buttons to show in top right corner (anchor: top, left, bottom, right)
Debug Details
After some debugging it appears that it is in fact Panel2 (a child of the split container) that doesn't resize properly, and the actual SplitContainer itself resizes fine.
Here are the debug values that show this...
Full width form, before resize:
splitContainerMain.Width: 1479
splitContainerMain.Panel2.Width: 1206
panelCenter.Width: 1203
tabControlMain.Width: 1215
All as expected, splitContainerMain.Panel2.Width is smaller than splitContainerMain.Width.
After resize where the issue occurs:
splitContainerMain.Width: 815
splitContainerMain.Panel2.Width: 1206
panelCenter.Width: 1203
tabControlMain.Width: 1215
As can be seen, the splitContainerMain.Width has resized as desired, but the splitContainerMain.Panel2.Width and subsequently its children have not.
NOTE: Please remember, the width updates correctly if I manually resize the form slowly - this is not a problem with me not correctly setting any anchors.
My Efforts So Far
What I have tried to do is use various Form resize events and try to set the widths manually, but to no avail. I think what I would like to try is to set the Panel2.Width value from within an event of some sort.
What I Am Looking For
Is there anyway to force splitContainerMain.Panel2.Width to resize correctly when the splitContainerMain size changes?
Alternatively, how can I calculate what the Panel2.Width should be? And how can I set that value from the Form.Resize event? (or another event?)
Though the question is about 6 years old, I opted to answer this because I was in the same situation as the opening post. Unfortunately, the orientation was not specified. So, my answer would address the ones with Horizontal orientation.
Please translate to C# as this code is in VB.
Private Sub splitContainerMain_Resize(sender As Object, e As EventArgs) Handles splitContainerMain.Resize
'/* This is a work around about panels being left out when SplitContainer is resized */
Dim pnl1Height As Single = splitContainerMain.SplitterDistance '/* Get upper panel height */
Dim pnl2Height As Single = splitContainerMain.Height - splitContainerMain.SplitterDistance '/* Get lower panel height */
splitContainerMain.Panel1.SetBounds(0, 0, splitContainerMain.Width, pnl1Height) '/* Set Upper panel bounds */
'/* Set lower panel bounds, with a top of upper panel height plus splitter width */
splitContainerMain.Panel2.SetBounds(0, pnl1Height + splitContainerMain.SplitterWidth, splitContainerMain.Width, pnl2Height)
End Sub
From what I see u should set anchor to none for controls that are creating problem including splitcontainer pannels.
Also I would suggest to use dock fill property to best use the splitcontainers.
If need further help please provide designer file so can have a better look.
So on each Change event you are creating a new thread, that thread will then wait 100 ms and then do the recize??? thats stupid. You can have a thread created at the constructor, then calling Start() on your thread which could have the following:
private void resizeMe()
{
this.BeginInvoke((Action)() => {
splitContainer.Height = tableBorder.Height;
splitContainer.Width = tableBorder.Width;
}
}
Exactly the same problem, below code worked for me:
Surround splitContainer in a panel "tableBorder"
On tableBorder
Dock = DockStyle.Fill;
On split Container, (no anchoring)
Dock = DockStyle.None;
On tableBorder SizeChanged event
private void tableBorder_SizeChanged(object sender, EventArgs e)
{
new Thread(() => { resizeMe(); }).Start();
}
private void resizeMe()
{
Thread.Sleep(100);
this.BeginInvoke((Action)(() => {
doIt();
}));
}
private void doIt()
{
splitContainer.Height = tableBorder.Height;
splitContainer.Width = tableBorder.Width;
}
There is a small lag, but works
I'm asking a question here cause I'm not sure how to search it in google, I don't know the word how to call this.
What I mean is a "zero point" which basically located at upper-left corner of the element, pictureBox for an example. So when I set new width and height properties for my pictureBox it works fine but it's relative to top left corner.
What I mean is you've got pictureBox, let's say 100 x 100 and you desire decrease it to 50 x 100, so you will get "empty space" under your picture box, not upper. PictureBox counts it's properties from this one top left corner zero point.
And what I need is to change this point to the another corner.
So when I change my height it count up, not down. Help me please.
I really hope you can understand me.
Wow, thank you guys for your advices! I've tested anchor property and Top += 50; now, doesn't solve my problem. Let me try to describe it another way. You have an image (100px) with grass 50px at bottom and sky 50px at the top. So you have picturebox with height 100. If you set pictureBox.height = 50; you will see only sky. But I need to leave only grass. The main problem I see here is because this zero point at top left corner.
Here is an example:
Button1 click event:
private void button1_Click(object sender, EventArgs e)
{
pictureBox1.Height = 150;
}
The result which you will get after you press the button:
But I need another result:
As I understand it happens because height property counts from the top left corner. So if I change it to bottom left or right corner it will works the way I need. Help me please to change this point...
MSDN reference: http://msdn.microsoft.com/en-us/library/system.windows.forms.picturebox.sizemode(v=vs.71).aspx
Valid values for this property are taken from the PictureBoxSizeMode enumeration. By default, in PictureBoxSizeMode.Normal mode, the Image is placed in the upper left corner of the PictureBox, and any part of the image too big for the PictureBox is clipped. Using the PictureBoxSizeMode.StretchImage value causes the image to stretch to fit the PictureBox.
Unless you are using a container which handles control layout automatically, you will have to move it yourself. I assume you are resizing the control in code with a line like:
myPictureBox.Height = 50;
You will have to also move the control's location down by the same amount:
myPictureBox.Top += 50;
Generalizing, something like this ought to do the trick for resizing height:
void ResizeHeightFromBottom(this Control c, int newHeight)
{
var oldBottom = c.Bottom;
c.Height = newHeight;
c.Top = oldBottom - newHeight;
}
I will leave resizing the width and both simultaneously as an exercise for the reader, as well as specifying an arbitrary reference point.
I am responding to MouseLeftButtonDown events on elements added to a WPF canvas. It all works fine when clicked (i.e. the eventhandler fires off correctly), but it requires too much precision from the mouse pointer. You have to be perfectly on top of the circle to make it work. I need it to be a little more forgiving; maybe at least 1 or 2 pixles forgiving. The elements on the canvas are nice big circles (about the size of a quarter on the screen), so the circles themselves are not too small, but the StrokeWidth of each one is 1, so it is a thin line.
You can see a screenshot here: http://twitpic.com/1f2ci/full
Most graphics app aren't this picky about the mouse picking, so I want to give the user a familiar experience.
How can I make it a little more forgiving.
You can hook up to the MouseLeftButtonDown event of your root layout object instead, and check which elements is in range of a click by doing this:
List<UIElement> hits = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(Point, yourLayoutRootElement) as List<UIElement>;
http://msdn.microsoft.com/en-us/library/cc838402(VS.95).aspx
For the Point parameter, you can use the MouseEventArgs parameter e, and call its GetPosition method like this:
Point p = e.GetPosition(null)
I can't remember whether to use HitTest instead of the FindElementsInHostCoordinates. Try both.
http://msdn.microsoft.com/en-us/library/ms608752.aspx
You could create 4 Point objects from the mouse position to create a fake tolerence effect, and call either FindElementsInHostCoordinates or HitTest for all 4 points.
You might want to try to fill the circle with the Transparent colour to make the whole circle clickable...
If that fails, you can also draw helper circles on the same location as the other circles. Make the circle foreground colour Transparent, and make the thickness of the brush a few pixels wider for a more acceptable clickable region around the circle..
Hope this helps!
I think I've done it (with you help to get me started)...
First, I've moved the move event handling to the Canvas instead of each Ellipse. That's good and bad, from an OOP standpoint. At least when the mouse event handling is a responsibility of the HolePattern to set it on up each Hole (the ellipse that is the visual of the Hole), it is abstracted away so that any consumer of my HolePattern will get this functioanality automactically. However, by moving it to the main UI code, I now am dealing with my canvas mouse event at a higher level. But that's not all bad either. We could discuss this part for days.
The point is, I have designed a way to create a "margin of error" when picking something on a canvas with a mouse, and then reading the Hole that the selected Ellipse belongs to, and then I can read the HolePattern that the Hole belongs to, and my entire UI (ListView, textboxes, gridview fo coordinates) are ALL updated by the existing XAML binding, and the Canvas is updated with one call to an existing method to regenerate the canvas.
To be honest, I can't believe I've figured all this out (with your help and others too, of course). It is such a cool feeling to have the vision of this this and see it come to be.
Check out the main code here:
void canvas1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
int ClickMargin = 2;
Point ClickedPoint = e.GetPosition(canvas1);
Point p1 = new Point(ClickedPoint.X - ClickMargin, ClickedPoint.Y - ClickMargin);
Point p2 = new Point(ClickedPoint.X - ClickMargin, ClickedPoint.Y + ClickMargin);
Point p3 = new Point(ClickedPoint.X + ClickMargin, ClickedPoint.Y + ClickMargin);
Point p4 = new Point(ClickedPoint.X + ClickMargin, ClickedPoint.Y - ClickMargin);
var PointPickList = new Collection<Point>();
PointPickList.Add(ClickedPoint);
PointPickList.Add(p1);
PointPickList.Add(p2);
PointPickList.Add(p3);
PointPickList.Add(p4);
foreach (Point p in PointPickList)
{
HitTestResult SelectedCanvasItem = System.Windows.Media.VisualTreeHelper.HitTest(canvas1, p);
if (SelectedCanvasItem.VisualHit.GetType() == typeof(Ellipse))
{
var SelectedEllipseTag = SelectedCanvasItem.VisualHit.GetValue(Ellipse.TagProperty);
if (SelectedEllipseTag!=null && SelectedEllipseTag.GetType().BaseType == typeof(Hole))
{
Hole SelectedHole = (Hole)SelectedEllipseTag;
SetActivePattern(SelectedHole.ParentPattern);
SelectedHole.ParentPattern.CurrentHole = SelectedHole;
}
}
}
}
Just Increase Stroke ThickNess of the Ellipse so that it is adjustable
thus the MouseLeftButtonDown event works
Example:
In Ellipse tag:
Ellipse
Canvas.Left="10" Canvas.Top="133" Height="24" Name="ellipse1" Width="23" Stroke="Red" MouseLeftButtonDown="ellipse1_MouseLeftButtonDown" ToolTip="Temp Close" StrokeEndLineCap="Flat" StrokeThickness="12"
private void ellipse1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Application curApp = Application.Current;
curApp.Shutdown();
}