Show completely MonthCalendar control inside a GroupBox - c#

I'm trying to show a month calendar completely inside a groupbox, I read, you can use a Panel and change the poperty AutoScroll into true, but it does not work.
Is there another way to show it or I am wrong?

The GroupBox control can be goofy to work with, because the Client space will overlap the GroupBox's title, but the DisplayRectangle will adjust correctly the upper left position of the "control space".
So try adjusting the size of the GroupBox using code (I don't think the designer is good at this):
groupBox1.AutoSize = false;
monthCalendar1.Location = groupBox1.DisplayRectangle.Location;
int w = monthCalendar1.Size.Width + groupBox1.Padding.Left +
groupBox1.Padding.Right;
int h = monthCalendar1.Size.Height + groupBox1.DisplayRectangle.Top +
groupBox1.Padding.Bottom;
groupBox1.ClientSize = new Size(w, h);

Related

C#: Docking label at middle bottom of panel and have it grow with content on run time.

I need a label to appear in the middle bottom (like 30px above the bottom line) of a panel, that also auto sizes to wrap the text depending on how long the text is.
So far I can only have the label auto size to wrap the text and have it docked at the bottom. But it is at bottom left and not middle.
As is on this example Long Text Image and Image of short text I'd like to center
(sorry the text is not clear but its at the bottom in white)
I was able to achieve the auto size using the ClientSizeChanged event as below.
private void Panel1_ClientSizeChanged(object sender, EventArgs e)
{
label8.MaximumSize = new Size((sender as Control).ClientSize.Width - label8.Left, 10000);
}
How do I have the text at the middle? It should be able to maintain the bottom middle (dock) position as I resize the panel.
Try following:
To your panel add TableLayoutPanel with following Properties:
tableLayoutPanel1.AutoSize = true; // This can be set at the end if you use designer
tableLayoutPanel1.ColumnCount = 1;
tableLayoutPanel1.RowCount = 1;
tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
panel1.Controls.Add(tableLayoutPanel1); // add TableLayoutPanel to your panel
tableLayoutPanel1.Controls.Add(label1, 0, 0); // Add your label to TableLayout
And set properties on Label:
label1.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
label1.AutoSize = true;
label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
Now you can set
tableLayoutPanel1.AutoSize = true;
It is kind of tricky but should give you what you need.

Textblock margin causes out of bounds text

I'm currently trying to create a visual component to have scrolling text (left to right and right to left) - pretty much an html marquee.
I have a grid divided in several columns & rows, and I want to place my component inside one of the grid slots.
The grid (named UIGrid) is generated like this :
for (int i = 0; i < xDivisions; i++)
{
ColumnDefinition newColumn = new ColumnDefinition();
UIGrid.ColumnDefinitions.Add(newColumn);
}
for (int i = 0; i < yDivisions; i++)
{
RowDefinition newRow = new RowDefinition();
UIGrid.RowDefinitions.Add(newRow);
}
The component I'm adding is just a border with a textblock as a child. I place the border inside the Grid like this :
border = new Border();
Grid.SetColumn(border, xPosition);
Grid.SetRow(border, yPosition);
textBlock = new TextBlock();
border.Child = textBlock;
textBlock.Text = "Scrolling text from left to right";
UIGrid.Children.Add(border);
I'm using a timer to increment the textblock margin, here's the timer callback simplified body :
textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
double textWidth = textBlock.DesiredSize.Width;
double visibleWidth = componentBase.ActualWidth;
double targetMargin = textWidth < visibleWidth ? visibleWidth : textWidth;
if (margin.Left == targetMargin)
{
margin.Left = -textWidth;
} else
{
margin.Left++;
}
When the text slides from left to right, it behaves nicely :
https://s10.postimg.org/p0nt7vl09/text_good.png
Text "leaving" the grid slot is hidden.
However, when I set the textblock's margin as negative so it may come back inside the viewable area from the left, the text is visible even though it's outside its allocated slot :
https://s10.postimg.org/pownqtjq1/text_bad.png
I've tried using padding instead, but I can't set a negative padding. I've tried a few other things, but I feel like I've encountered a roadblock.
What could I do to get a nicely scrolling text ?
If you want nicely scrolling text ListView might be a better option. It is dynamic and you can bind it to your object. It would take a lot of this guess work out.
Ed Plunkett led me in the right direction with the Clip property. The idea is to do this :
border.Clip = new RectangleGeometry
{
Rect = new Rect(0, 0, border.ActualWidth, border.ActualHeight)
};
Of course, that doesn't work if the border hasn't been rendered yet (and of course it isn't when my code is running). You can force the measurement to take place using 'Measure' as I did to measure the text length in pixels, but it behaved strangely on my border. I wouldn't get the correct size at all.
In the end, I simply subscribed to the border's SizeChanged event :
border.SizeChanged += OnSizeComputed;
When that event is fired, I create the RectangleGeometry using ActualWidth & ActualHeight.

WinForms Button: Autosize Maximumsize

I want to add Buttons to a FlowLayoutPanel. The Buttons might contain longer texts with spaces between the words. The Buttons are Autosize=true and AutoSizeMode = AutoSizeMode.GrowAndShrink. Further more I set the MaximumSize property to (maxwidth,0). maxwidth is the width of the panel. So the button does not grow too wide.
What I see is, that the widht of the Button is limited by the MaximumSize property, but when text wrapping occurs, the Button's height doesn't autosize to the height of the wrapped text. Is there a solution to that problem?
I also tried this manually sizing the button like this:
using (Graphics cg = this.CreateGraphics()) {
SizeF size = cg.MeasureString(button.Text, button.Font, 200);
button.Width = (int)size.Width+20;
button.Height = (int)size.Height+20;
button.Text = someLongTextWithSpaces;
}
But please note that I added 20 to the calculated size. It's working, but is there a proper way to determin this additional size? Maybe 2x Padding + ?????
A few hours later...
I came to this version which seems to work quite fine.
using (Graphics cg = this.CreateGraphics()) {
var fmt = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.WordBreak;
var prop = new Size(tableLayoutPanel1.Width - 20, 0);
var size = TextRenderer.MeasureText(button.Text, button.Font, prop, fmt);
int border = button.Height - button.Font.Height;
button.Width = (int)size.Width + border;
button.Height = (int)size.Height + border;
button.Text = someLongTextWithSpaces;
}
It seems that the initial button height is borders + the height the font. So I calculated the border subtracting button.Height-button.font.Height.
According to Hans, I now use the TextRenderer.MeasureText. I tested it without enabling VisualStyles and it worked fine. Any comments on that?
There is a proper way, but it isn't exactly very subtle. Reverse-engineering it from the ButtonRenderer class source code, the Winforms class that draws the button text, you must use the TextRenderer class to measure the text. And you must use the VisualStyleRenderer.GetBackgroundContentRectangle() method to obtain the effective drawing bounds. Note that it is smaller than the button's Size because of the border and a margin that depends on the selected visual style.
Non-trivial problems are mapping a calculated content rectangle back to the outer button size and dealing with old machines that don't have visual styles enabled. Sample code that appeared to arrive at the correct size:
private static void SetButtonSize(Graphics gr, Button button) {
VisualStyleElement ButtonElement = VisualStyleElement.Button.PushButton.Normal;
var visualStyleRenderer = new VisualStyleRenderer(ButtonElement.ClassName, ButtonElement.Part, 0);
var bounds = visualStyleRenderer.GetBackgroundContentRectangle(gr, button.Bounds);
var margin = button.Height - bounds.Height;
var fmt = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.WordBreak;
var prop = new Size(bounds.Width, 0);
var size = TextRenderer.MeasureText(button.Text, button.Font, prop, fmt);
button.ClientSize = new Size(button.ClientSize.Width, size.Height - margin);
}
protected override void OnLoad(EventArgs e) {
using (var gr = this.CreateGraphics()) {
SetButtonSize(gr, this.button1);
}
base.OnLoad(e);
}
Not extensively tested for corner cases, can't say I recommend this.
It seems that the initial button height is borders + the height the font. So I calculated the border subtracting button.Height-button.font.Height. (See the last block of my original post)
This also works with VisualStyles enabled/disabled.
You should control the line breaks by adding newline characters in the text. Automatic text wrapping won't work with spaces alone:
button1.Text = "123232131232\r\nfgfdgfdgdfgdfgdf\r\nASDSADSDASD";
Or :
button1.Text = "123232131232" + Environment.NewLine +
"fgfdgfdgdfgdfgdf" + Environment.NewLine + "ASDSADSDASD";
If you'd rather get the automatic wrapping you could try to use TextMeasure to determine the height needed for the text and then set the button's height accordingly but that may need some extra attention..
But I suggest to consider using Labels instead. For a Label the wrapping works out of the box.. Huge Buttons with varying sizes are non-standard UI elements.

Create Objects in panel and arrange objects the new in first not in end

I want create manually PictureBox and Label on horizontal Panel, The count of PictureBox and Label on panel Unknown maybe 200 or more or less, I use the below code to do that but i face two troubles first one:
I want add the new object created in the first not in the end for example if i created items "A B C D E" want it add on Panel "E D C B A" want always the new come to first.
Note: Panel width "230" Height "710"
Second trouble:
Currently when i use Panel scroll bar to go down than add new objects find happen big free space between the last object created and the new and if i used scroll again to go down and created new object on panel happen more big free space.
int Right = 50, Top = 0;
// Create Image + Text
PictureBox pbox = new PictureBox();
pbox.Size = new Size(140, 80);
pbox.Location = new Point(Right, Top);
pbox.Image = Image.FromFile("");
Panel1.Controls.Add(pbox);
// Create label
Label lblPlateNOBAR = new System.Windows.Forms.Label();
lblPlateNOBAR.Text = lblPlateNO.Text;
lblPlateNOBAR.Location = new Point(Right + 20, Top + 80);
Panel1.Controls.Add(lblPlateNOBAR);
Top = Top + 150;
In order to make the objects insert into the panel, you'd need to move the controls which already exist in the panel:
int right = 50;
// Create Image + Text
PictureBox pbox = new PictureBox();
pbox.Size = new Size(140, 80);
pbox.Location = new Point(right, 0);
pbox.Image = Image.FromFile("");
// Create label
Label lblPlateNOBAR = new System.Windows.Forms.Label();
lblPlateNOBAR.Text = lblPlateNO.Text;
lblPlateNOBAR.Location = new Point(right + 20, 80);
foreach(var control in Panel1.Controls)
{
control.Top = control.Top + 150;
}
Panel1.Controls.Add(pbox);
Panel1.Controls.Add(lblPlateNOBAR);
I know it might seem that I'm not answering your question, but you can take little time to know my suggestion too. I don't know what you wanna achieve, but when you want to make such a sophisticated program, it's a better practice that you do all the work needed yourself, not relying on Windows Forms Controls. Trying to add, remove, change location of lots of controls will reduce the application performance very much. I suggest that you draw, for example your pictures, yourself, using Graphics and Image objects and Paint event. Also handle things like clicking and selecting pictures by MouseEvents. It might seem a little hard at first, but after you've done this you have far better performance and flexibility. This becomes more important considering you mentioned you wanna place 200 pictures in the panel. This also prevents trouble of flickering that appears when you change position of many controls. It's good to mention that to do scrolling in this case, you can place a Panel inside of a Parent Panel and use AutoScroll feature only for the parent panel to handle scrolling. This way you don't have to care about the scrolling anymore.

how to get a labels width before it is rendered in WPF

Tricky one to explain this. I have a custom built properties grid. The left hand column has the property labels. Sometimes depending on the property, I want a little elipsis button to show the user that they can launch a dialog. I want the buttons to be inline vertically to make the UI look neat. The labels vary in width depending on the name of the property "onEnterPressed" or "upLink" for example.
If I add the elipses button alone and use a margin like so ...
elipsisButton.Margin = new Thickness(135, 0, 0, 0);
135 from the left is exactly where I want to place the button.
I was hoping to be able to do something like
Label newLabel = new System.Windows.Controls.Label();
newLabel.Content = anInfo;
aPanel.Children.Add(newLabel);
elipsisButton.Margin = new Thickness(135 - newLabel.Width, 0, 0, 0);
It would appear however, that the label doesn't get a width until it's been rendered on screen so I can't find out what size margin to add to my elipsis button. Any ideas?
You can call the Measure() method in order to ask the control the size it needs to be displayed:
var l = new Label() { Content = "Hello" };
l.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
Size s = l.DesiredSize;
And then use the value of the DesiredSize property.

Categories