Resizing label width depending on the width of its content - c#

I'm working on a code-editor and want to auto-adjust the width of a label as the number increases. For example, for 1-9 (1 digit) there's a specific width. Then when it gets to 10-99 (2 digits), width of label increases. Then again for then 100-999 (3 digits), etc.
The result should be something like this:
Here is my code:
private void timer_countline_Tick(object sender, EventArgs e)
{
updateNumberLabel();
}
private void updateNumberLabel()
{
// we get index of first visible char and number of first visible line
Point pos = new Point(0, 0);
int firstIndex = rtb.GetCharIndexFromPosition(pos);
int firstLine = rtb.GetLineFromCharIndex(firstIndex);
// now we get index of last visible char and number of last visible line
pos.X = ClientRectangle.Width;
pos.Y = ClientRectangle.Height;
int lastIndex = rtb.GetCharIndexFromPosition(pos);
int lastLine = rtb.GetLineFromCharIndex(lastIndex);
// this is point position of last visible char, we'll use its Y value for calculating numberLabel size
pos = rtb.GetPositionFromCharIndex(lastIndex);
// finally, renumber label
numberLabel.Text = "";
for (int i = firstLine; i <= lastLine + 1; i++)
numberLabel.Text += i + 1 + "\n";
}

You can use TextRenderer for doing what you want. Please check the following code lines (You should add the code lines to TextChanged event of your label):
Please remember that the AutoSize property of your controls must set to False.
This is for changing Width of your control to fit with width of its contents.
yourLabelName.Width = TextRenderer.MeasureText(yourLabelName.Text, yourLabelName.Font).Width;
This is for changing Height of your control to fit with height of its contents.
yourLabelName.Height = TextRenderer.MeasureText(yourLabelName.Text, yourLabelName.Font).Height;
Update 1:
For changing your panel Width for showing all contents in it horizontally, you can use the followng code line:
yourPanelName.Width = yourLabelName.Left + yourLabelName.Width;
For changing your panel Height for showing all contents in it vartically, you can use the followng code line:
yourPanelName.Height = yourLabelName.Top + yourLabelName.Height;
Update 2:
If you are used SplitContainer control, you must change the properties of your SplitContainer as follows:
FixedPanel = none
IsSplitterFixed = False
Then you can use the following lines of code for achieve to what you want:
For changing your SplitContainer panel Width for showing all contents in it horizontally, you can use the followng code line:
int yourLabelNameWidth = TextRenderer.MeasureText(yourLabelName.Text, yourLabelName.Font).Width;
yourSplitContainerName.SplitterDistance = yourLabelName.Left + yourLabelNameWidth;
yourLabelName.Width = yourLabelName.Left + yourLabelNameWidth;

Related

Set text width component via script

I am instantiating the leader board on the image (https://imgur.com/a/I0aVSaR), the problem is, I can't align properly the numbers on the center row.
I think that if I set the width of the numbers of the right row, the center row will be aligned. Right now the elements on the right row have different widths.
My problem now is how to set the width via script, I already tried rectransform.deltaSize, SetSizeWithCurrentAnchors and other similar things with no luck.
I've also tried to set a preferred width on the inspector on Layout Element but is ignored (https://imgur.com/a/O3hDeGN).
This is how the elements are created:
for (int i = 0; i < leaderboardEntries.Count; i++)
{
Transform newEntry = Instantiate(entryPrefab);
newEntry.SetParent(leaderboardEntriesPanel);
Text entryName = newEntry.GetChild(0).GetComponent<Text>();
Text entryScore = newEntry.GetChild(1).GetComponent<Text>();
Text entryRank = newEntry.GetChild(2).GetComponent<Text>();
entryName.text = leaderboardEntries[i].getName();
entryScore.text = leaderboardEntries[i].getScore().ToString();
entryRank.text = (i+1).ToString();
entryName.alignment = TextAnchor.UpperLeft;
entryScore.alignment = TextAnchor.UpperRight;
entryRank.alignment = TextAnchor.UpperRight;
}
The center row is the score, the left row the Name and the right row the rank.
Thanks!
if you want to change the width and height of a RectTransform you can try this:
rectTransform.sizeDelta = new Vector2( width, height);
but fixing their alignment need something else, first of all, all alignments should be same, UpperLeft or MiddleLeft for example.
then set anchor point of all your entries to the same value also.
then something like this should work properly:
// in this example I considered all anchor points are middle left and all
// alignments are middle left too
float width = leaderboardEntriesPanel.GetComponent<RectTransform>().rect.width;
for (int i = 0; i < leaderboardEntries.Count; i++)
{
Transform newEntry = Instantiate(entryPrefab);
newEntry.SetParent(leaderboardEntriesPanel);
Text entryName = newEntry.GetChild(0).GetComponent<Text>();
Text entryScore = newEntry.GetChild(1).GetComponent<Text>();
Text entryRank = newEntry.GetChild(2).GetComponent<Text>();
entryName.text = leaderboardEntries[i].getName();
entryScore.text = leaderboardEntries[i].getScore().ToString();
entryRank.text = (i+1).ToString();
entryName.GetComponent<RectTransform>().anchoredPosition = new Vector2(width / 15, entryName.GetComponent<RectTransform>().anchoredPosition.y);
entryScore.GetComponent<RectTransform>().anchoredPosition = new Vector2(width / 2, entryScore.GetComponent<RectTransform>().anchoredPosition.y);
entryRank.GetComponent<RectTransform>().anchoredPosition = new Vector2(width * 4 / 5, entryRank.GetComponent<RectTransform>().anchoredPosition.y);
}

Datagridview issue with printing alternate rows

Hello all I am trying to print data from datagridview in such a way the alternate rows should print left and right. I am able to print but they are not getting in line. Here is the code I tried to print.
Complete code you can find here
https://drive.google.com/open?id=0ByVjmdncQgagRjZvME9STWJoVHc
using (Font font = new Font("Consolas", 13f))
{
float x = 0;
float y = 0;
for (int row = linesPrinted; row < DGV.Rows.Count; row++)
{
for (int col = 0; col < DGV.ColumnCount; col++)
{
x = 0;
string text = DGV[col, row].FormattedValue.ToString();
if (row % 2 == 0)
{
x += 10;
y += 5;
e.Graphics.DrawString(text, font, Brushes.Black, new PointF(x, y));
}
else
{
x += 45;
y += 5;
e.Graphics.DrawString(text, font, Brushes.Black, new PointF(x, y));
}
}
}
I am getting the output as follows
is getting printed after country1 what I need is it should align to top
Since it appears you are trying to print TWO different rows on the same line, then you are going to have to keep track of where columns 1’s “Y” value starts, this will facilitate lining up column 2. The other issue is the current code simply prints one page when there is more than one page to print.
When looping through the DataGridView printing the rows, and we reach the end of the page, we need to call the PrintPage method again. In its current form, the PrintPage method will start printing from the first row (0) of the DataGridView and it will do this for every new page. An infinite loop will ensue because each new page simply starts over with the first row. A global int variable dgvRowIndex should fix this so that each time the next page is printed we won’t start at the beginning.
I had a difficult time printing multiple pages, however #LarsTech solution worked well at Print multiple datagridview pages
To help, below is a method that simply prints ONE row of the data grid view. The method takes a rowIndex to identify which row to use in the DataGridView, a printX value to indicate the starting left print value, a printY value to indicate the vertical position to start printing this row, a lineIncrement used as a “leading” value for the text and lastly the PrintPageEventArgs variable e to draw the text to.
private void printRow(int rowIndex, float printX, float printY, float lineIncrement, PrintPageEventArgs e) {
for (int col = 0; col < dataGridView1.ColumnCount; col++) {
string text = dataGridView1[col, rowIndex].FormattedValue.ToString();
e.Graphics.DrawString(text, font, Brushes.Black, new PointF(printX, printY));
printY += lineIncrement;
}
}
Now that we have a method to print a single row from the DatagridView into a column in the print document, it should be easier to print the columns as rows and line the rows up. First as mentioned earlier, we need a global variable that keeps track of the DataGridViews row index while looping through it. This global variable dgvRowIndex is used each time the PrintPage method is called. I also added the font variable as a global variable.
// global variables
int dgvRowIndex = 0;
Font font = new Font("Consolas", 13f);
Below is the PrintPage method to print each page. Most variable’s names are self-explanatory. pageHeight is used to determine when we need a new page. Two columns, LeftColX and RightColX are the two horizontal (X) starting points for each column. curY keeps track of the vertical (Y) position on the print page and is used to compare its value to pageHeight and start a new page if needed. lineIncrement is used as a “leading’ value for each new line. spaceBefore is used to add extra vertical space between the two printed rows. Lastly, as stated before, we need to keep track the vertical “Y” value when a new “column 1” is printed. column2YValue is used for this and is sent to the PrintRow method as the starting “Y” value when printing the second column.
When the while loop starts looping through the rows of the DataGridView , a check is made to see if this row is in column 1 or column 2. If dgvRowIndex is in column 1, then we need to save this starting “Y” column2YValue to use when printing the second column. If dgvRowIndex is in column2 then we simply call printRow method again, but this time we pass over the starting “Y” value from the previous column column2YValue. Extra vertical space is added, dgvRowIndex is incremented to go to the next row, then a check is made to see if we need to add another page. Here a simple check is made to see if the current “Y” value curY is past the margin, if it is, then we need to indicate a new page is needed then return. The return simply starts the PrintPage method over, this should work as expected since we now have a global variable dgvRowIndex to avoid starting at the beginning of the DataGridView. Hope this helps.
private void printDocument1_PrintPage(object sender, PrintPageEventArgs e) {
float pageHeight = e.MarginBounds.Height;
float leftColX = 10;
float rightColX = 145;
float curY = 10;
float lineIncrement = 16;
float spaceBefore = 24;
float column2YValue = curY;
while (dgvRowIndex < dataGridView1.Rows.Count) {
if (dgvRowIndex % 2 == 0) {
column2YValue = curY;
printRow(dgvRowIndex, leftColX, curY, lineIncrement, e);
} else {
printRow(dgvRowIndex, rightColX, column2YValue, lineIncrement, e);
curY += spaceBefore;
}
dgvRowIndex++;
if (curY > pageHeight) {
e.HasMorePages = true;
return;
}
curY += spaceBefore;
}
}
Edit to address multiple columns and multiple rows.
As I commented, in order to print multiple columns the code will need to keep track of the print “X” value on the page. The idea is to keep printing on the same (print) row (y value) until the “X” value goes past the right margin.
The other change addresses where to start the next print group/grid row. When the code calls printRow it is not known before that call how many columns the grid row may have. If the grid has three, four or 10 columns, then the code will print that number of lines. If there are 10 columns in each grid row… there will be 10 “print” rows for each row in the grid. Since printRow is going to loop through the columns to print, it is used to return this “next” “Y” print row value.
Tracing the code below, loops through the grids rows, first the current row is printed, and the next “print” row nextRowYStart is set based on how many columns are in the grid.
The next if statement checks if there is enough room on the right of the page to print another column. If there is enough room on the page for another column, then curX simply moves to the next column. If there is not enough room, the “X” value curX is reset to the left most (starting) column, curY is set to nextRowYstart to move down to the next print row, and some vertical space is added. A check is then made to see if the new print rows “Y” value curY goes past the bottom margin. If it does, then we start a new page. Otherwise, simply print the next row of the grid.
The code assumes you want to print all the columns, however if you wanted to print only the first X columns, a stopColumn variable is added so you can decide how many of the first columns to print. I am guessing this may be what you are looking for. Hope it helps.
private void printDocument1_PrintPage(object sender, PrintPageEventArgs e) {
//int stopColumn = 3;
int stopColumn = dataGridView1.Columns.Count;
float pageHeight = e.MarginBounds.Height;
float pageWidth = e.MarginBounds.Width;
float startX = 50;
float startY = 50;
float columnWidth = 150;
float curX = startX;
float curY = startY;
float lineIncrement = 16;
float spaceBefore = 24;
float nextRowYStart = curY;
while (dgvRowIndex < dataGridView1.Rows.Count) {
nextRowYStart = printRow(dgvRowIndex, curX, curY, lineIncrement, stopColumn, e);
if (curX + columnWidth > pageWidth) {
// start a new PRINT group/row
curX = startX;
curY = nextRowYStart;
curY += spaceBefore;
if (curY > pageHeight + spaceBefore) {
dgvRowIndex++;
e.HasMorePages = true;
return;
}
} else {
curX += columnWidth;
}
dgvRowIndex++;
}
}
Updated printRow method
private float printRow(int rowIndex, float printX, float printY, float lineIncrement, int stopCol, PrintPageEventArgs e) {
for (int col = 0; col < dataGridView1.ColumnCount; col++) {
if (col < stopCol) {
string text = dataGridView1[col, rowIndex].FormattedValue.ToString();
e.Graphics.DrawString(text, font, Brushes.Black, new PointF(printX, printY));
printY += lineIncrement;
}
else {
break;
}
}
return printY;
}

Displaying pictureBox array

I would like to display 13 pictureBox, however, it ends up with only the last one visible.
So I was wondering if I did it in a wrong way.
The following code get image from resources folder.
var testP = new PictureBox();
for (int i = 0; i < 13; i++)
{
testP.Width = 65;
testP.Height = 80;
testP.BorderStyle = BorderStyle.None;
testP.SizeMode = PictureBoxSizeMode.StretchImage;
test[i] = getImage(testP, testPTemp[i]);
}
The following code is trying to display 13 pictureBox with shifting location.
These two codes segments should be able to perform the action.
test = new PictureBox[13];
for (var i = 0; i < 13; i++)
{
test[i].Image = (Image)Properties.Resources.ResourceManager.GetObject("_" + testTemp[i]);
test[i].Left = 330;
test[i].Top = 500;
test[i].Location = new Point(test[i].Location.X + 0 * displayShift, test[i].Location.Y);
this.Controls.Add(test[i]);
}
Here is the getImage()
private PictureBox getImage(PictureBox pB, string i) // Get image based on the for loop number (i)
{
pB.Image = (Image)Properties.Resources.ResourceManager.GetObject("_" + i); // Get the embedded image
pB.SizeMode = PictureBoxSizeMode.StretchImage;
return pB;
}
I'm pretty sure there are all PictureBox Controls but they have all the same location so they are lying above each other. That's why only the last one is visible to you.
I think you should replace the 0 with the i variable.
test[i].Location = new Point(test[i].Location.X + i * displayShift, test[i].Location.Y); this.Controls.Add(test[i]);
It's hard to tell the exact problem based off the code you've provided. One possible issue could be that when you are creating the PictureBoxes you only create a single instance before the for loop and then fill the array with references to that instance. Another possibility is that when you're calculating the X position of the controls, you're multiplying by 0 which will always result in 0 (meaning all the controls are at location 330).
Below is code that will achieve basically what you're trying but without all your code I can't give you a more specific example.
In Your Class
const int PICTURE_WIDTH = 65;
const int PICTURE_HEIGHT = 85;
Inside You Function
//Loop through each image
for(int i = 0; i < testTemp[i].length; i++)
{
//Create a picture box
PictureBox pictureBox = new PictureBox();
pictureBox.BorderStyle = BorderStyle.None;
pictureBox.SizeMode = PictureBoxSizeMode.StretchImage;
//Load the image date
pictureBox.Image = (Image)Properties.Resources.ResourceManager.GetObject("_" + testTemp[i]);
//Set it's size
pictureBox.Size = new Size(PICTURE_WIDTH, PICTURE_HEIGHT);
//Position the picture at (330,500) with a left offset of how many images we've gone through so far
pictureBox.Location = new Point(330 + (i * PICTURE_WIDTH), 500);
//Add the picture box to the list of controls
this.Controls.Add(pictureBox);
}
If you need to keep a list of the picture boxes, just create a new list before the loop and add each pictureBox to the list inside the loop. If the control/window you're adding these PictureBoxes to needs to scroll left or right to see all the images set the AutoScroll property to true.

Rectangle's width increasing more than it should while trying to resize the rectangle

I don't think the title is on the spot. But please check the pictures below for a better demonstration.
I have a rectangle which fits text in it. What I'm trying to do is that, if the rectangle's height is bigger than the screen, then the rectangle should resize.
Resizing goes as follows: set the height to screen height, increase width, try to fit text, repeat.
Here is the method that parses my text, it returns the string that would fit in the rectangle given the width.
private String parseText(String text, int Width)
{
String line = String.Empty;
String returnString = String.Empty;
String[] wordArray = text.Split(' ');
int i = 1;
foreach (String word in wordArray)
{
if (Font.MeasureString(line + word).Length() > Width)
{
returnString = returnString + line + '\n';
line = String.Empty;
i++;
}
line = line + word + ' ';
}
this.Size = new Size(Size.Width, (int)Font.MeasureString(returnString + line).Y);
return returnString + line;
}
This part works really well. However, if the text exceeds the height, the text is drawn outside of the rectangle.
So I added this:
public string Text
{
get { return text; }
set
{
temp = parseText(value.Replace("\n", ""), Size.Width);
while(Size.Height > Viewport.Height)
{
Size = new Size(Size.Width + 5, Viewport.Height);
temp = parseText(value, Size.Width);
}
text = temp;
}
}
This is my problem:
Part 1: Text is ok. Does not hit the screen's edge
Part 2: Right after hitting the screen edge by adding a bunch of 'hello'
Part 3: Adding one more "hello" properly fixes the issue.
What is happening in Part 2? How does it get resized like that? Why is it fixed in part 3?
Note: Regardless of what I add here: Size = new Size(Size.Width + 5, Viewport.Height); either +5, or 5% of the screen, or 20% or 200% it still gets resized to the same amount.
Comment for more information. Thanks
Fixed by adding: value.Replace("\n", "") in temp = parseText(value, Size.Width);
I was doing that before the while loop, but not inside it. Therefore, the text was getting a bunch of new lines, and when it got called again, the new lines disappeared before the while loop.
It should look like this: temp = parseText(value.Replace("\n", ""), Size.Width - (int)LeftMargin);

Resize form depending on text length

I'm developing a desktop application in C#, which shows pop-up messages every X quantity of time. For this, I'm using a library called PopupNotify, and I'd like the label that contains the message(called NotifyMessage) to have a fixed width, and vertically enlarge on overflow.
There is an event in which they set this up, but I can't modify it to make it work. Here is the event's code:
private void SetLayout()
{
int padding = 8;
int iconRightPadding = 0;
int border = 1;
iconBox.Left = padding + border;
iconBox.Top = padding + border;
iconBox.Width = IconWidth;
iconBox.Height = IconHeight;
this.Height = iconBox.Height + 2 * padding + 2 * border;
closeButton.Left = Width - padding - border - closeButton.Width + 3;
closeButton.Top = padding + border - 3;
NotifyTitle.Top = iconBox.Top - 5; //fudge factor
NotifyTitle.Left = iconBox.Right + iconRightPadding;
NotifyMessage.Left = NotifyTitle.Left + 1; //fudgy
NotifyMessage.Width = Width - NotifyMessage.Left - padding - border;
NotifyMessage.Top = NotifyTitle.Bottom;
NotifyMessage.Height = Height - NotifyMessage.Top - padding - border;
}
I've tried modifying it's logic, and the way it calculates its height by adding the NotifyMessage's height, and some other things, but none of them worked.
I'll appreciate any help on this.
You'll need to use TextRenderer.MeasureText to calculate required height of the label. This should be close:
public static int CalcLabelHeight(Label lbl) {
Size sz = new Size(lbl.ClientSize.Width, Int32.MaxValue);
sz = TextRenderer.MeasureText(lbl.Text, lbl.Font, sz, TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl);
int height = sz.Height;
if (height < lbl.Font.Height) height = lbl.Font.Height;
return height + lbl.Padding.Vertical;
}
From there, set the form's ClientSize property to fit the label.
From my comment:
try docking that beautiful label
Something like this:
Graphics g = wnd.CreateGraphics();
String s = "Your string";
SizeF size = g.MeasureString(Font, s);
myHeight = size.Height + padding + border;
Use StringFormat as an argument to MeasureString to specify wrapping options when calculating the correct height.

Categories

Resources