Update width of the rectangle when changing the width of the grid - c#

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.

Related

WPF: Stretch rectangle if aspect ratio > 16:9, scale otherwise

I'm trying to put a rectangle in a grid in a window that will change size regularly. I'm not working with absolute values, but with ratios.
So, there are three states the rectangle could have relative to the window/grid:
The default aspect ratio for the window is 16:9. If the window has that size, the rectangle should fit into the window perfectly, filling the window;
If the window's width is bigger than that, the rectangle should stretch with it. (So if the window's aspect ratio > 16/9, the rectangle stretches its width, thus still filling the entire window);
If the window's height is bigger than the 16:9 ratio, the rectangle inside should (1) not stretch vertically, and (2) align to the bottom of the grid.
This image explains it a lot clearer
I'm looking for a solution that doesn't involve changing code other than XAML, (so nothing in the .cs file), unless there is no other way. I did try finding a solution with C# code though:
RectName_OnSizeChanged(object sender, SizeChangedEventArgs) {
RectName.MaxHeight = 9/16 * RectName.Width;
}
but it doesn't seem to be working. (So why that is, is my bonus question)
How about this:
<Grid Background="CornflowerBlue" SizeChanged="ParentSizeChanged">
<Rectangle x:Name="theRect" Fill="Blue" HorizontalAlignment="Left" VerticalAlignment="Bottom" />
</Grid>
And this:
private void ParentSizeChanged(object sender, SizeChangedEventArgs e)
{
var parent = sender as FrameworkElement;
if (parent == null)
return;
theRect.Width = parent.ActualWidth;
theRect.Height = Math.Min(parent.ActualHeight, parent.ActualWidth * 6 / 9);
}

Buttons to appear around click position of mouse

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;
}

PictureBox size coordinates relation

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.

how to get actual height of Grid Row before rendering

In my project there are many nested Grids. And mostly rows and columns width is defined as "*" in XAML.
I am trying to expand a particular row(lets say Row1) by seting other row's height to 0, and i am using the .ActualHeight property to get the width of Row1 then its not giving me actual height.
As per I know that is happening because height and width of Grid rows and columns are set on rendering time.
I searched on net and somebody suggested to use UpdateLayout() method ..but that is also not working for me.
I can not post code snippet because it is very long code.
my project in c#.net wpf.
You need to do a full layout update, that is you need to call Measure, Arrange and UpdateLayout:
//Make the framework (re)calculate the size of the element
_Element.Measure(new Size(Double.MaxValue, Double.MaxValue));
Size visualSize = _Element.DesiredSize;
_Element.Arrange(new Rect(new Point(0, 0), visualSize));
_Element.UpdateLayout();
_Element being a FrameworkElement (Grid is one).
Solution based on Baboon post worked for me. I needed to animate slide in of collapsed FrameworkElement, what is similar problem of not knowing ActualHeight value.
element.Visibility = Visibility.Visible;
element.InvalidateArrange();
element.UpdateLayout();
double elementHeight = element.DesiredSize.Height;
DoubleAnimation animation = new DoubleAnimation(0, elementHeight, TimeSpan.FromMilliseconds(animMilisec));
element.BeginAnimation(Rectangle.HeightProperty, animation);
Now before element is rendered I have access to it's final height;

MouseLeftButtonDown on canvas requires too much precision

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();
}

Categories

Resources