Resize a shape in WPF after an event - c#

I use a WPF to display a shape on an Office panel.
I can size the shape when application is loading. But I would like to resize this shape if the user resize the panel containing the shape.
My problem is margin defined at the beginning is not changed after loading, so the size keep its initial margin.
I have a UserControl containing the WPF Shape and the resize event handler:
private void UserControlA_Resize(object sender, EventArgs e)
{
myWPF.SetSizeOfShape(sizeOfPanel); // I collect sizeOfPanel, and it is OK, it is changed when panel is resized
}
xaml of the WPF is:
<Border Name="myShape" Background="blue" CornerRadius="8" Margin="10,126,139,199" />
the WPF .cs is:
public void SetSizeOfShape(int widthOfPanel)
{
myShape.Margin = new Thickness(widthOfPanel/3, 100, widthOfPanel/6, 100);
SetSizeOfShape is called when the application is loading and the size is correctly set - but if size is changed, it is called again, but doesn't change the margin displayed.
Do you know what is wrong and how to correct it ?
----- EDIT -----
There is probably a problem with my event handler. Indeed, if I put the event with a button click, it works - but if I use the Resize (or sizeChanged) event it doesn't : the event is called, but there is no effect on the shape. Do you know how to solve it ?

I don't see a Resize event for the Border Control. It can size to its contents. Maybe give us more detail about what you're trying to do.
public void MyMouseOver()
{
Ellipse myShape = new Ellipse() { Width = 200, Height = 100, Stroke = Brushes.Yellow, };
MyCanvas.Children.Add(myShape);
Canvas.SetTop(myShape,10);
Canvas.SetLeft(myShape,10);
myShape.MouseEnter += MyShape_MouseEnter;
myShape.MouseLeave += MyShape_MouseLeave;
}
private void MyShape_MouseLeave(object sender, MouseEventArgs e)
{
((Ellipse)sender).RenderTransform = new ScaleTransform(1, 1); // return scale to normal
}
private void MyShape_MouseEnter(object sender, MouseEventArgs e)
{
((Ellipse)sender).RenderTransform = new ScaleTransform(1.1, 1.1, Width / 2, Height / 2);
}

Related

How to make the Drawer of RadSideDrawer cover the full page

I am using a telerik SizeDrawer control for UWP. and in it I want the drawer content to cover full page when it opens. So I tried to bind to Actual-Width of the page but that didn't work and then I tried to register it with sizeChanged event but width of drawer content still remains 0. unless I explicitly define it to some value like 300 then it remains 300 but I want it to cover full screen.
CODE
<telerik:RadSideDrawer Name="MainDrawer" SizeChanged="MainDrawer_SizeChanged"
Loaded="MainDrawer_Loaded">
<telerik:RadSideDrawer.MainContent>
<Grid />
</telerik:RadSideDrawer.MainContent>
<telerik:RadSideDrawer.DrawerContent>
<Grid Name="DrawerGrid" Background="Yellow">
</Grid>
</telerik:RadSideDrawer.DrawerContent>
</telerik:RadSideDrawer>
C#
private void MainDrawer_SizeChanged(object sender, SizeChangedEventArgs e)
{
DrawerGrid.Width = e.NewSize.Width;
}
private void MainDrawer_Loaded(object sender, RoutedEventArgs e)
{
DrawerGrid.Width = MainDrawer.Width;
}
Note that height of drawer grid is alright and stretches to full screen but width remains 0 and if width is untouched totally then it is 240 fixed.
This is how you do it -
First, define a double property that gets the width of the current Window.
public double WindowWidth => Window.Current.Bounds.Width;
Then use it as a one-time binding source for the DrawerLength of your RadSideDrawer.
<primitives:RadSideDrawer x:Name="MainDrawer"
DrawerLength="{x:Bind WindowWidth}" ... />
Finally, update the DrawerLength whenever the size of the MainDrawer gets updated.
private void MainDrawer_SizeChanged(object sender, SizeChangedEventArgs e) =>
MainDrawer.DrawerLength = e.NewSize.Width;
Also note if you don't feel like doing bindings, you can replace the first two steps with one line of code, right after InitializeComponent();
MainDrawer.DrawerLength = Window.Current.Bounds.Width;
Update
Looks like when the drawer is collapsed, the control itself resets its Opacity back to 1, and then when you constantly update the DrawerLength, the edge of the drawer could come out and give a bad user experience.
You can try to throttle page size changes with Reactive Extensions, or you can simply set the Opacity of the drawer back to 0 when it's collapsed.
To do this, you just need to monitor the DrawerState property and when it's Closed, set the DrawerGrid.Opacity to 0.
But remember you will also need to set it back to 1 before it gets expanded. Unfortunately there isn't an Opening state and Opened fires too late, so you have to locate the menu button and do it inside its Click event.
public MainPage()
{
InitializeComponent();
MainDrawer.DrawerLength = Window.Current.Bounds.Width;
Loaded += (s, e) =>
{
// GetChildByName: https://github.com/JustinXinLiu/Continuity/blob/master/Continuity/Extensions/UtilExtensions.cs#L44
var menuButton = MainDrawer.GetChildByName<Button>("PART_SideDrawerButton");
menuButton.Click += (s1, e1) => DrawerGrid.Opacity = 1;
};
MainDrawer.RegisterPropertyChangedCallback(RadSideDrawer.DrawerStateProperty, (s, e) =>
{
var state = MainDrawer.DrawerState;
if (state == DrawerState.Closed)
{
DrawerGrid.Opacity = 0;
}
});
}

FlowLayoutPanel shows horizontal scroll bar with inner panel's width being the same as client size width

I'm experiencing a problem with FlowLayoutPanel while its children being resized on FlowLayoutPanel's ClientSizeChanged event.
I'm trying to make the children resize horizontally when the FlowLayoutPanel resizes. The problem is that even though the margin of the child is 0 and the padding of FlowLayoutPanel is also 0, after the execution of ClientSizeChanged event handler, the FlowLayoutPanel shows its horizontal scroll bar while the width of the child is exactly the same as FlowLayoutPanel.ClientSize.Width.
I've tried to move the code to Resize event, but I still get the same result.
This is an example to demonstrate the problem, there's one FlowLayoutPanel with the following properties changed from default:
AutoScroll = true
FlowDirection = TopDown
Margin = 0,0,0,0
Name = flow1
BackColor = Red
There's also a regular panel inside the FlowLayoutPanel:
Margin = 0,0,0,0
Name = panel1
BackColor: Blue
And finally, a timer with interval 1 which changes the width of the FlowLayoutPanel and disables itself when the HorizontalScroll.Visibile property of the FlowLayoutPanel is true and shows a message box that announces the width of panel1 and ClientSize.Width of the flow1.
Here's the code:
private void timer1_Tick(object sender, EventArgs e)
{
flow1.Width -= 1;
if (flow1.HorizontalScroll.Visible)
{
timer1.Enabled = false;
MessageBox.Show("Panel.Width = " + panel1.Width.ToString() +
", FlowPanel.ClientWidth = " + flow1.ClientSize.Width.ToString());
}
}
private void flow1_ClientSizeChanged(object sender, EventArgs e)
{
panel1.Width = flow1.ClientSize.Width;
}
What's the logic behind the horizontal scroll bar being shown while no children overflows the client size? And most importantly, how to prevent it from happening?
It is an event order problem, the layout gets calculated too soon. Automatic layout has several nasty corner-cases, it can also be bi-stable with the layout flipping back-and-forth between two solutions. You can see this in your test app, add flow1.PerformLayout(); before the MessageBox.Show() call and you'll see that the scrollbar is hidden again.
This is why the SuspendLayout() method exists. Winforms programmers don't use it enough. For a good reason, it is pretty hard to judge when you need it. And they really don't want to need it. Basic rule is that you should use it if layout must deal with more than one size changing.
Which is the real fix in your test program:
private void timer1_Tick(object sender, EventArgs e) {
flow1.SuspendLayout();
flow1.Width -= 1;
flow1.ResumeLayout(true);
// etc..
}
And you'll see that it works just fine now, you never see the message box.

'Nice' options style winforms dialog control

Here and there I've seen nicely styled dialog controls, similar to this one which appears in Beyond Compare v4:
My own implementation of this gets vaguely close, and consists of a listbox on the left and usercontrols which change when the listbox selected item changes. However no amount of putting lipstick on that pig will get it looking like the above. I can picture how I might undertake this with custom painting of things and so forth, but my real intent is to generate the left hand entries at runtime not design time (there will be one for each column in a datafile).
I was wondering if anyone had any ideas on how to reasonably easily implement such a thing, either with a component (commercial is fine) or some other ingenious method.
Thanks!
Here is an example of mimicking your design. I have one large panel1 to house both sides and in it one panel2 to house the left side. Inside panel2 there are the search controls and the listview.
The search controls are a Label1 containing a TextBox and another Label2. The Label.Images are aligned and the Textbox has no Border. The Labels are AutoSize=false and Label1 has a 3D-border.
Panel1 has a singleLine border, panel2 and the ListView have no borders. The ListView has View=Details and one column, HeaderStyle=None. It also has OwnerDraw=true.
I have added a Paint event for the ListView but have to call it in code.
Please note that I haven't taken the time to create nice images. Also note: Their height will determine the Items' Height (!) so leave a little transparent border above and below; their good looks will be key to the overall looks!
They are contained in an Imagelist with appropriate Size and BitDepth. You may need to adapt the DrawIamge numbers..
The stuff on the right side is pretty much standard; for the horizontal bar I use a Panel with height=1 and Border=Single. If you have more than one group of RadioButtons make sure to put each group in a separate Panel, transparent and no Borders, of course..
public Form1()
{
InitializeComponent();
listView1.Width = panel2.ClientSize.Width;
listView1.Columns[0].Width = listView1.ClientSize.Width;
listView1.Paint += listView1_Paint;
listView1.BackColor = panel2.BackColor;
leftBrush = new SolidBrush(panel2.BackColor);
rightBrush = new SolidBrush(panel1.BackColor);
}
Pen borderPen = new Pen(SystemColors.ActiveBorder);
SolidBrush leftBrush, rightBrush;
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
if (e.ItemIndex == 0) listView1_Paint(
null, new PaintEventArgs(e.Graphics, listView1.ClientRectangle));
if (!e.Item.Selected)
{
e.Graphics.FillRectangle(leftBrush, e.Bounds);
e.Graphics.DrawLine(borderPen,
listView1.Width-1, e.Bounds.Y, listView1.Width-1, e.Bounds.Bottom);
}
else
{
e.Graphics.FillRectangle(rightBrush , e.Bounds);
e.Graphics.DrawLine(borderPen, 0, e.Bounds.Top, e.Bounds.Width, e.Bounds.Top);
e.Graphics.DrawLine(borderPen,
0, e.Bounds.Bottom-1, e.Bounds.Width, e.Bounds.Bottom-1);
}
e.Graphics.DrawString( e.Item.Text, listView1.Font,
Brushes.Black, 35, e.Bounds.Y + 5 );
e.Graphics.DrawImage(imageList1.Images[e.Item.ImageIndex], 2, e.Bounds.Y );
}
void listView1_Paint(object sender, PaintEventArgs e)
{
int hh = listView1.Items.Count * imageList1.ImageSize.Height;
e.Graphics.DrawLine(borderPen,
listView1.Width - 1, hh, listView1.Width - 1, listView1.Height);
}
private void panel2_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawLine(borderPen, panel2.Width-1, 0, panel2.Width-1, listView1.Top);
}
}
Here is a screenshot of my Q&D version:

How to hide and show panels on a form and have it resize to take up slack?

I have a form with three panels.
I want the top two to be a fixed height, and bottom one to fill the rest of the space.
The dialog is resizable, so all should change width on resixze, and bottom one should change height.
This is important, the user must be able to stretch the form, as well as the program through code.
If I set a panel to visible = false, I want the form height to shrink so the others stay the same height.
If I set a panel to visible = true, I want the form height to grow by the height of the panel.
I will control the hiding/showing of the panels with buttons. The idea is I show certain panels for "advanced" mode in my form, and hide them for "simple" mode. I cannot have a bunch of blank space if I hide a panel, and I want the form to shrink a bunch for simple mode.
I tried doing this with panels docked to top, but a form resize by the user will not change a panel height. So that is the main trick I am asking for help on.
Anyone in this kind of situation can use the below styling:
Use 2-3 Panel to Group different Controls and place them in a single parent control say a GroupBox.
Make all the child panels Dock to the same side say "Top".
if any of the panel is jumping over another due to similar docking. In Visual Studio, go to menu View > Other Windows > Document Outline and Set the display order. (https://msdn.microsoft.com/en-us/library/46xf4h0w%28v=vs.80%29.aspx)
Set the Panel property as follows:
AutoSize: true
AutoSizeMode: GrowAndShrink
Once the panel is not visible, other panel will take away the empty space.
Hope this helps!!!
the other posts are close, there is a detail missing that I included in the solution I came up with. Its a flag to tell if the sized event was caused by a user form resize, or the program doing it when a panel is shown or hidden.
For this solution, make a form with 4 panels.
Set dock to top for all panels. Do not set any autosizing for panels or form.
Also make two buttons and place on top panel, or any panel that you will not be hiding.
The code below shows how to handle the resized event, and the showing hiding buttons.
I made them hide/show panel 2, but the code should work for any panel.
namespace ProgTesting {
public partial class Form5 : Form {
private bool doNothing = false;
public Form5() {
InitializeComponent();
cmdAdvanced.Visible = false;
}
private void cmdSimple_Click(object sender, EventArgs e) {
if (panel2.Visible) {
panel2.Visible = false;
doNothing = true;
this.MinimumSize = new Size(this.Width, this.Height - panel2.Height);
this.Height = this.Height - panel2.Height;
doNothing = false;
cmdSimple.Visible = false;
cmdAdvanced.Visible = true;
}
}
private void cmdAdvanced_Click(object sender, EventArgs e) {
if (!panel2.Visible) {
panel2.Visible = true;
doNothing = true;
this.Height = this.Height + panel2.Height;
this.MinimumSize = new Size(this.Width, this.Height);
doNothing = false;
cmdAdvanced.Visible = false;
cmdSimple.Visible = true;
}
}
private void Form5_SizeChanged(object sender, EventArgs e) {
if (!doNothing)
if (panel2.Visible)
panel3.Height = this.ClientSize.Height - panel1.Height - panel2.Height - panel4.Height;
else
panel3.Height = this.ClientSize.Height - panel1.Height - panel4.Height;
}
}
}
You do have to manage the heights going on, which is a pain but gives you control. Some shots of it working:
It sounds like what you are looking for is an Expander. Essentially a control that consists of a header and a content area, where clicking on the header toggles the content area between visible and collapsed.
I'm not 100% certain if an Expander control will automatically adjust the height of a form when it expands/collapses though. You may need to hook up an event handler to the Expander to manually adjust the height of your form when the Expander collapses.
Try looking at the Stack Overflow Question Add an expander (collapse/expand) to a Panel WinForm. There are a number of links from that question about implementing an Expander in Windows Forms, including some that provide full source code.
Update:
I've knocked up a quick demo that will accomplish what you want, in a very primitive way, not using any of the various Expander controls out there. I think that while you will find the Expanders useful, I doubt they will adjust the size of the container they are inside, unless you write some adjustment code like I have below.
Create a form that looks like this:
The two text boxes are anchored Left, Top, Right.
The toggle button is anchored Left, Top, named btnTogglePanel.
The magenta panel is anchored Left, Top, Right, Bottom, named pnlToggled.
The bottom button is anchored Right, Bottom.
Then in the code behind for the form:
public partial class ToggleableExpanderForm : Form
{
public ToggleableExpanderForm()
{
InitializeComponent();
SizeChanged += (s, args) =>
{
if (_LastSize.HasValue)
{
var diff = Size - _LastSize.Value;
if (_LastHeight.HasValue
&& !IgnoreNextResizeForLastHeightAdjustment)
{
_LastHeight += diff.Height;
}
}
_LastSize = Size;
};
}
private Size? _LastSize;
private bool IgnoreNextResizeForLastHeightAdjustment = true;
private int? _LastHeight;
private void btnTogglePanel_Click(object sender, EventArgs e)
{
var newVisibility = !pnlToggled.Visible;
int heightAdjustment = 0;
if (newVisibility)
{
if (_LastHeight.HasValue)
{
heightAdjustment = _LastHeight.Value;
_LastHeight = null;
}
}
else
{
_LastHeight = pnlToggled.Height;
heightAdjustment = -pnlToggled.Height;
}
pnlToggled.Visible = newVisibility;
IgnoreNextResizeForLastHeightAdjustment = true;
Height += heightAdjustment;
}
}
Essentially you adjust the height of the form manually when the toggle button is clicked, based on whether or not the panel is hiding/showing. You also need to take into account of what will happen when the user resizes the form when the panel is invisible, which is where the cruft around listening to the SizeChanged event comes in. You need the answer to "how MUCH has the size changed" and then you adjust your previously stored panel height as appropriate.
I don't imagine the above code is perfect, and I haven't tested it in every use case, but it gets the basic job done.
One way to go about it is to use a TableLayoutPanel for the top two panels.
The steps below will give you the resizing desired when the user resizes the form.
Start by created a TableLayoutPanel (TLP) and shrink it down from the default 2 columns, 2 rows to just 2 columns, 1 row.
Anchor it to Top, Left, and Right
Now size the TLP to fit your top two panels and place each panel in a cell of the TLP.
Anchor these two panels to all sides.
Position the 3rd panel (Panel3) below the TLP to your liking and anchor it to all sides.
To handle the hiding of Panel3 some logic will need to be added to the appropriate button_Click event. Determine what size you would like the "minimized" height to be and then just store the form size when Panel3 is visible and restore the height when it's clicked again. It should look something like this.
private void button1_Click(object sender, EventArgs e)
{
if (panel3.Visible)
{
// make invisible
panel3.Visible = false;
// storedHeight is a private member of the Form
storedHeight = Form1.ActiveForm.Height;
Form1.ActiveForm.Height = minimumHeight; // Set to predetermined minimum height
}
else
{
// make visible
panel3.Visible = true;
Form1.ActiveForm.Height = storedHeight;
}
}
Now when you hide Panel3 the form will shrink to the size of the TLP, then grow again when Panel3 is visible. Also the top panels will expand in width, but not height. You will need to change the MinSize properties of the form and its value will have to be dynamically adjusted depending on whether Panel3 is visible or not.

Resizing Panel Containing UserControl

I have a UserControl that contains invisible controls, to make them visible, the UserControl resizes.
I need to resize the Panel that contains the UserControl, but I don't know how.
This behavior is handled well by the Panel and Form classes without explicit sizing (and without the layout bugs introduced when the user has a high-DPI monitor or uses the large or extra-large font settings.
1) Create a Form with a docked FlowLayoutPanel.
2) Set the Form and FlowLayoutPanel's AutoSize to true and AutoSizeMode to GrowAndShrink
3) Add your panels and content.
4) Programmatically set the desired panel's Visible property to hidden
hiddenPanel.Visible = false;
5) or true
hiddenPanel.Visible = true;
Put this code in the usercontrol:
Size last = new Size(0, 0);
private void Me_Resize(object sender, System.EventArgs e)
{
if (last != new Size(0, 0)) {
this.Parent.Size = Size.Add(this.Parent.Size, Size.Subtract(this.Size, last));
}
last = this.Size;
}
Will also retain margins (e.g., if the panel is larger than your usercontrol or has other controls beside your usercontrol.)
If you'd like to resize it to a particular size, you can do it on the code behind:
Size panelSize = new Size(500, 500);
usercontrol1.Parent.Size = panelSize;
You could add this code to the usercontrol if that is where you wish to resize from.
To resize the control , call scale of to the control.
// To zoom in controls.
foreach (Control c in MyFlowLayoutPanel.Controls)
{
PictureBox ptc = c as PictureBox;
if (null != ptc)
{
Point pt = new Point(2, 2);
SizeF sf = new SizeF(pt);
c.Scale(sf);
}
}
// To zoom out controls.
foreach (Control c in MyFlowLayoutPanel.Controls)
{
PictureBox ptc = c as PictureBox;
if (null != ptc)
{
SizeF sf = new SizeF(0.5F, 0.5F);
c.Scale(sf);
}
}
I know that this topic is pretty old but I want to add my method, as well...
If you have a Panel containing a UserControl you can easially resize the panel.Controls by firing Form1_Resize event.
private void Form1_Resize(object sender, EventArgs e)
{
foreach (Control control in MasterPanel.Controls)
{
control.Size = MasterPanel.Size;
}
}
Just make sure that you anchor its content properly.

Categories