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;
}
});
}
Related
I have a panel1 with AutoScroll = true.I have to make panel1 scroll with btnUp and btnDown. So far I've made what I was asked for
private void btnUpClicked(Object sender, EventArgs e)
{
if (panel1.VerticalScroll.Value - 55 > 0)
panel1.VerticalScroll.Value -= 55;
else panel1.VerticalScroll.Value = 0;
}
private void btnDownClicked(Object sender, EventArgs e)
{
panel1.VerticalScroll.Value += 55;
}
But now I need to hide Scrollbar or make it invisible. I tried
panel1.VerticalScroll.Visible = false;
but it doesn't work. Any ideas guys?
Ok, I've done the working example of this for you. All you have to do is to change the max value depending on the total size of all the items inside your panel.
Form code:
public partial class Form1 : Form
{
private int location = 0;
public Form1()
{
InitializeComponent();
// Set position on top of your panel
pnlPanel.AutoScrollPosition = new Point(0, 0);
// Set maximum position of your panel beyond the point your panel items reach.
// You'll have to change this size depending on the total size of items for your case.
pnlPanel.VerticalScroll.Maximum = 280;
}
private void btnUp_Click(object sender, EventArgs e)
{
if (location - 20 > 0)
{
location -= 20;
pnlPanel.VerticalScroll.Value = location;
}
else
{
// If scroll position is below 0 set the position to 0 (MIN)
location = 0;
pnlPanel.AutoScrollPosition = new Point(0, location);
}
}
private void btnDown_Click(object sender, EventArgs e)
{
if (location + 20 < pnlPanel.VerticalScroll.Maximum)
{
location += 20;
pnlPanel.VerticalScroll.Value = location;
}
else
{
// If scroll position is above 280 set the position to 280 (MAX)
location = pnlPanel.VerticalScroll.Maximum;
pnlPanel.AutoScrollPosition = new Point(0, location);
}
}
}
Picture example:
You have to set AutoScroll option to False on your panel. I hope you understand what I've done and will get your panel running the way you want. Feel free to ask if you have any questions.
The Panel control takes on the duty you gave it by setting AutoScroll to true pretty serious. This always includes displaying the scrollbar gadget if it is necessary. So what you tried cannot work, hiding the vertical scrollbar forces Panel to recalculate layout since doing so altered the client area. It will of course discover that the scrollbar is required and promptly make it visible again.
The code that does this, Panel inherits it from ScrollableControl, is internal and cannot be overridden. This was intentional.
So using AutoScroll isn't going to get you anywhere. As an alternative, do keep in mind what you really want to accomplish. You simply want to move controls up and down. Easy to do, just change their Location property. That in turn is easiest to do if you put the controls on another panel, big enough to contain them. Set its AutoSize property to True. And implement you buttons' Click event handlers by simply changing that panel's Location property:
private const int ScrollIncrement = 10;
private void ScrollUpButton_Click(object sender, EventArgs e) {
int limit = 0;
panel2.Location = new Point(0,
Math.Min(limit, panel2.Location.Y + ScrollIncrement));
}
private void ScrollDownButton_Click(object sender, EventArgs e) {
int limit = panel1.ClientSize.Height - panel2.Height;
panel2.Location = new Point(0,
Math.Max(limit, panel2.Location.Y - ScrollIncrement));
}
Where panel1 is the outer panel and panel2 is the inner one that contains the controls. Be careful when you use the designer to put controls on it, it has a knack for giving them the wrong Parent. Be sure to use the View + Other Windows + Document Layout helper window so you can see this going wrong. After you filled it, set its AutoSizeMode property to GrowAndShrink so it snaps to its minimum size.
Try this:
panel.AutoScroll = true;
panel.VerticalScroll.Enabled = false;
panel.VerticalScroll.Visible = false;
Edit:
Actually when AutoScroll = true; It will take care of hscroll and vscroll automatically and you wont be able to change it. I found this on Panel.AutoScroll Property on MSDN
AutoScroll maintains the visibility of the scrollbars automatically. Therefore, setting the HScroll or VScroll property to true has no effect when AutoScroll is enabled.
You may try this to workaround this problem, I have copied it from this Link.
Behavior Observations 1:
If AutoScroll is set to true, you can't modify anything in VerticalScroll or HorizontalScroll. AutoScroll means AutoScroll; the control decides when scrollbars are visible, what the min/max is, etc. and you can't change a thing.
So if you want to customize the scrolling (e.g. hide scrollbars), you must set AutoScroll to false.
Looking at the source code for the ScrollableControl with Lutz Roeder's .NET Reflecter, you can see that if AutoScroll is set to true, it ignores your attempts to change property values within the VerticalScroll or HorizontalScroll properties such as MinValue, MaxValue, Visible etc.
Behavior Observations 2:
With AutoScroll set to false, you can change VerticalScroll.Minimum, VerticalScroll.Maximum, VerticalScroll.Visible values.
However, you cannot change VerticalScroll.Value!!! Wtf! If you set it to a non-zero value, it resets itself to zero.
Instead, you must set AutoScrollPosition = new Point( 0, desired_vertical_scroll_value );
And finally, SURPRISE, when you assign positive values, it flips them to negative values, so if you check AutoScrollPosition.X, it will be negative! Assign it positive, it comes back negative.
So yeah, if you want custom scrolling, set AutoScroll to false. Then set the VerticalScroll and HorizontalScroll properties (except Value). Then to change the scroll value, you need to set AutoScrollPosition, even though you aren't using auto scrolling! Finally, when you set the AutoScrollPosition, it will take on the opposite (i.e. negative) value that you assign to it, so if you want to retrieve the current AutoScrollPosition later, for example if you want to offset the scroll value by dragging the mouse to pan, then you need to remember to negate the value returned by AutoScrollPosition before reassigning it to AutoScrollPosition with some offset. WOW. Wtf.
One other thing, if you are trying to pan with the mouse, use the values of Cursor.Position rather than any mouse locations returned by the mouse events parameters. Scrolling the control will cause the event parameter values to be offset as well, which will cause it to start firing mouse move events complete with undesired values. Just use Cursor.Position, because it will use mouse screen coordinates as a fixed frame of reference, which is what you want when you're trying to pan/offset the scroll value.
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);
}
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.
I'm having quite a few other problems with layout that require a lot of extra InvalidateLayout() calls, so I'm starting to question if I understand how RelativeLayout works.
Here's a very simple example of a UI that wants a right-aligned label:
public class MainPage : ContentPage {
public MainPage() {
var layout = new RelativeLayout();
var label = new Label() {
Text = "I want to be right-aligned."
};
layout.Children.Add(label,
Constraint.RelativeToParent((rl) => rl.Width - label.Width),
Constraint.Constant(10));
var button = new Button() {
Text = "Invalidate"
};
button.Clicked += (object sender, EventArgs e) => layout.ForceLayout();
layout.Children.Add(button,
Constraint.Constant(10),
Constraint.Constant(10));
Content = layout;
}
}
I expect this to start with the label properly aligned, but it does not align the label correctly until another layout pass is forced. By overriding methods like OnSizeRequest() in my custom control, I've determined this is because the calls to OnSizeRequest() don't happen until after the calls to the RelativeLayout's constraint lambdas. So, when the page is laid out, the label's Width is -1. When ForceLayout() is called later, the Label has had a chance to perform its layout logic and has properly set the Width property, so it gets laid out correctly.
In a larger context, I'm trying to make a button that, when clicked, fades out and a label slides into place where it was. It's to be aligned in the bottom-right corner of my layout, but I'm finding that modifying Opacity or IsVisible only inconsistently updates the layout. The only consistent behavior is RelativeLayout really likes to ask for the control's size before it gets a chance to resize itself.
Am I interpreting how to use a RelativeLayout wrong, or is this a mistake in its logic?
Delving deep into the (current) implementation of RelativeLayout, I found a truth I did not expect: it does not consult a view's GetSizeRequest() method or call its Layout() method before the constraints are calculated, because those constraints might affect the control's final size. The consequence: while the constraints are being calculated, the control's bounds reflect its old position and size.
To "fix" this, call the view's GetSizeRequest() inside constraints that need the most up-to-date size of the control:
public class MainPage : ContentPage {
public MainPage() {
var layout = new RelativeLayout();
var label = new Label() {
Text = "I want to be right-aligned."
};
Func<RelativeLayout, double> getLabelWidth = (parent) => label.GetSizeRequest(parent.Width, parent.Height).Request.Width;
layout.Children.Add(label,
Constraint.RelativeToParent((rl) => rl.Width - getLabelWidth(rl)),
Constraint.Constant(10));
var button = new Button() {
Text = "Invalidate"
};
button.Clicked += (object sender, EventArgs e) => layout.ForceLayout();
layout.Children.Add(button,
Constraint.Constant(10),
Constraint.Constant(10));
Content = layout;
}
}
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.