Windows Forms Top Property Bug - c#

I am trying to populate a container with any number of controls that have the same height and width. I allow this container to be shrunk or grown by the user and the container will organize the controls so that it fits the most controls on one row as possible. Here is the code to organize it:
int row = 0;
int column = 0;
for (int i = 1; i <= controls.Count; i++)
{
controls.Values[i-1].Top = row * controls.Values[0].Height;
controls.Values[i-1].Left = column * controls.Values[0].Width;
if (i % controlsPerRow == 0)
{
// This finishes a row
row++;
column = 0;
}
else
{
column++;
}
}
The problem i run into is that on the first iteration of the loop, I will be multiplying the control height by the row and assigning that value to the control Top property. The first row is 0 and the first height is 165. 0 * 165 = 0, but the Top property contains a magical -20 after assigning the 0.
Anyone have any idea how this can happen?

You're trying to rewrite the FlowLayoutPanel.
Consider using it instead.
Also, it looks like your controls field is a Dictionary<Something, Control>.
Be aware that the iteration order of Dictionary.Values is not guaranteed, meaning that you aren't looping over the controls in the order that they were added to the dictionary.

Related

c# epplus not filling background color of cell

Ok I am stumped the set font works and sets the correct cell. The Fill.BackgroundColor.SetColor does not work. It literally does nothing. Am I missing something? Every search I do that syntax should work.
Yes this is reading a data table dt that is not the issue the loop works properly.
int startupRow = 2; // row 1 is header
for (int row = startupRow; row <= dt.Rows.Count; row++)
{
//if allocation check is populated it will have a value > 0
if (Convert.ToInt32(workSheet.Cells[row, 8].Value) > 0)
{
//if Balance Remaining Barrels < allocation check
if (Convert.ToInt32(workSheet.Cells[row, 7].Value) < Convert.ToInt32(workSheet.Cells[row, 8].Value))
{
//set the font to red
var cell = workSheet.Cells[row, 7];
cell.Style.Font.Color.SetColor(System.Drawing.Color.Red);
cell.Style.Font.Bold = true;
//Setting the background color to red
cell.Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid;
cell.Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.Red);
}
}
}
I resolved my own question so hopefully this helps someone else.
The border has to be last in the formatting. Certain elements take precedent over other elements and they will disappear.

DevExpress GridView Scroll Bar (adjusting Grid Left Coord to bring focused column in the center of the grid)

I have a form which has a DevExpress GridView. The GridView has close to 100 columns. Currently, at a time I show 30 columns to the user and the rest of the columns can be accessed by scrolling on the right hand side.
Instead of scrolling, I want to implement a functionality such that I enter a value(Column Number) in a textbox and click a button (for example), it should navigate to that particular column in the gridView.
So far, I am able to implement the following:
If suppose, in the beginning i show columns 1-30 and search column 45 it shows the result with the column selected at the extreme right.
If suppose, in the beginning i have columns 20-50 columns shown to the user and search column 10, it shows in the extreme right.
I want to implement in such a way that whenever I search a column, it should not show in the extreme right or left but should show in the center.
Please check the code given below. The logic seems to be fine but it is not working correctly.
private void SetLeftCoord(int toBeFocusedIndex)
{
int currLeftEndColIndex = gvVisualizeNextGenSequence.LeftCoord / gvVisualizeNextGenSequence.VisibleColumns[0].Width;
int currRightEndColIndex = currLeftEndColIndex - 1 + GetVisibleColumnCount(gvVisualizeNextGenSequence);
if (toBeFocusedIndex < currRightEndColIndex && toBeFocusedIndex > currLeftEndColIndex)
{
int difference = toBeFocusedIndex - currLeftEndColIndex;
gvVisualizeNextGenSequence.LeftCoord +=
(difference * gvVisualizeNextGenSequence.VisibleColumns[0].Width) -
(GetVisibleColumnCount(gvVisualizeNextGenSequence) / 2) * gvVisualizeNextGenSequence.VisibleColumns[0].Width;
}
if (toBeFocusedIndex < (GetVisibleColumnCount(gvVisualizeNextGenSequence) / 2) ||
toBeFocusedIndex > gvVisualizeNextGenSequence.VisibleColumns.Count - (GetVisibleColumnCount(gvVisualizeNextGenSequence) / 2))
{ }
if (toBeFocusedIndex < currLeftEndColIndex)
{ gvVisualizeNextGenSequence.LeftCoord -= (GetVisibleColumnCount(gvVisualizeNextGenSequence) / 2) * gvVisualizeNextGenSequence.VisibleColumns[0].Width; }
if (toBeFocusedIndex > currRightEndColIndex)
{ gvVisualizeNextGenSequence.LeftCoord += (GetVisibleColumnCount(gvVisualizeNextGenSequence) / 2) * gvVisualizeNextGenSequence.VisibleColumns[0].Width; }
gvVisualizeNextGenSequence.FocusedColumn = gvVisualizeNextGenSequence.Columns["Column" + toBeFocusedIndex];
}
Is there any other way to do this?
"at any point of time, my focused column should be in the center of the grid"
Thanks
Mangesh Vilas Kaslikar
gridview1.LeftCoord = (gridview1.FocusedColumn.VisibleIndex - visibleColumnsCount / 2) * gridview1.VisibleColumns[0].Width;
Visiblecolumnscount = // count of currently visible columns in the view
public int GetVisibleColumnCount(GridView view)
{
GridViewInfo info = view.GetViewInfo() as GridViewInfo;
int visibleColumnCount = 0;
for (int i = 0; i < view.VisibleColumns.Count; i++)
{
if (info.GetColumnLeftCoord(view.GetVisibleColumn(i))
< view.ViewRect.Width - info.ViewRects.IndicatorWidth)
visibleColumnCount++;
}
return visibleColumnCount;
}

ListView AutoResizeColumns based on both Column content and header

we use this two methods to adjust column length based on Column content and header resp.
ListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
ListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
But how to adjust based on both? i.e. adjust to the longest length for header and column content.
lvw.Columns[0].Width = -2
See remarks in MSDN for details:
http://msdn.microsoft.com/en-us/library/system.windows.forms.columnheader.width.aspx
Also note that MSDN says that 'To autosize to the width of the column heading, set the Width property to -2.', but actually it works for column heading AND column contents.
Here is a code to prove that:
lvw.Columns.Add(new String('x', 25)); // short header
lvw.Items.Add(new String('x', 100)); // long content
lvw.Columns[0].Width = -2;
// in result column width will be set to fit content
As answered here, calling both resizing options do the job :
myListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
myListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
This is what I use to adjust column width to both content and header:
public static void autoResizeColumns(ListView lv)
{
lv.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
ListView.ColumnHeaderCollection cc = lv.Columns;
for (int i = 0; i < cc.Count; i++)
{
int colWidth = TextRenderer.MeasureText(cc[i].Text, lv.Font).Width + 10;
if (colWidth > cc[i].Width)
{
cc[i].Width = colWidth;
}
}
}
Example use:
autoResizeColumns(listView1);
The method isn't that well tested, but at least it works in the context I'm using it in.
It's possible indeed to use MeasureText and then to calculate how much space is left and somehow distribute between all columns. But this is quick-and-dirty approach which I have quickly coded:
/// <summary>
/// Enables autoresizing for specific listview.
/// You can specify how much to scale in columnScaleNumbers array - length of that array
/// should match column count which you have.
/// </summary>
/// <param name="listView">control for which to enable auto-resize</param>
/// <param name="columnScaleNumbers">Percentage or numbers how much each column will be scaled.</param>
private void EnableAutoresize(ListView listView, params int[] columnScaleNumbers)
{
listView.View = View.Details;
for( int i = 0; i < columnScaleNumbers.Length; i++ )
{
if( i >= listView.Columns.Count )
break;
listView.Columns[i].Tag = columnScaleNumbers[i];
}
listView.SizeChanged += lvw_SizeChanged;
DoResize(listView);
}
void lvw_SizeChanged(object sender, EventArgs e)
{
ListView listView = sender as ListView;
DoResize(listView);
}
bool Resizing = false;
void DoResize( ListView listView )
{
// Don't allow overlapping of SizeChanged calls
if (!Resizing)
{
// Set the resizing flag
Resizing = true;
if (listView != null)
{
float totalColumnWidth = 0;
// Get the sum of all column tags
for (int i = 0; i < listView.Columns.Count; i++)
totalColumnWidth += Convert.ToInt32(listView.Columns[i].Tag);
// Calculate the percentage of space each column should
// occupy in reference to the other columns and then set the
// width of the column to that percentage of the visible space.
for (int i = 0; i < listView.Columns.Count; i++)
{
float colPercentage = (Convert.ToInt32(listView.Columns[i].Tag) / totalColumnWidth);
listView.Columns[i].Width = (int)(colPercentage * listView.ClientRectangle.Width);
}
}
}
// Clear the resizing flag
Resizing = false;
}
And depending how many columns you have - you specify each column "percentage" or simply number. For example for 3 columns - call looks like this:
EnableAutoresize(listView1, 6, 3, 1);
This will distribute column sizes as:
6 * 100% / (6 + 3 + 1) = 60% for first column,
30% for next and 10% for remaining.
This is somehow poor man quick implementation. :-)
In my case, I do this through the next steps (for two columns of data):
Creating a ColumnHeader object for each column.
Setting the size by AutoResize based on HeaderSize (on both columns)
Store that value in a Integer variable
Setting the size by AutoResize based on ColumnContent (on both columns)
Updating the value of each Integer variable through the Max criteria between the old value and the new value (for each column).
Setting the column width size for each ColumnHeader object.
In VB.NET:
'Create two header objects as ColumnHeader Class
Dim header1, header2 As ColumnHeader
'Construcción de los objetos header
header1 = New ColumnHeader
header1.Text = "ID"
header1.TextAlign = HorizontalAlignment.Right
header1.Width = 10
header2 = New ColumnHeader
header2.Text = "Combinaciones a Procesar"
header2.TextAlign = HorizontalAlignment.Left
header2.Width = 10
'Add two columns using your news headers objects
ListView.Columns.Add(header1)
ListView.Columns.Add(header2)
'Fill three rows of data, for each column
ListView.Items.Add(New ListViewItem({"A1", "B1"}))
ListView.Items.Add(New ListViewItem({"A2", "B2"}))
ListView.Items.Add(New ListViewItem({"A3", "B3"}))
'Change the size of each column
Dim headsz1, headsz2 As Integer
SelectionInTable.ListView.AutoResizeColumn(0, ColumnHeaderAutoResizeStyle.HeaderSize)
SelectionInTable.ListView.AutoResizeColumn(1, ColumnHeaderAutoResizeStyle.HeaderSize)
headsz1 = header1.Width
headsz2 = header2.Width
SelectionInTable.ListView.AutoResizeColumn(0, ColumnHeaderAutoResizeStyle.ColumnContent)
SelectionInTable.ListView.AutoResizeColumn(1, ColumnHeaderAutoResizeStyle.ColumnContent)
headsz1 = Math.Max(headsz1, header1.Width)
headsz2 = Math.Max(headsz2, header2.Width)
header1.Width = headsz1
header2.Width = headsz2
Here's a C# solution that can be used for any ListView. It assumes your column count and headers won't change for any given list view. Get rid of the listViewHeaderWidths dictionary if you want to recalculate header widths every time (if headers change, or number of columns changes).
private Dictionary<string, int[]> listViewHeaderWidths = new Dictionary<string, int[]>();
private void ResizeListViewColumns(ListView lv)
{
int[] headerWidths = listViewHeaderWidths.ContainsKey(lv.Name) ? listViewHeaderWidths[lv.Name] : null;
lv.BeginUpdate();
if (headerWidths == null)
{
lv.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
headerWidths = new int[lv.Columns.Count];
for (int i = 0; i < lv.Columns.Count; i++)
{
headerWidths[i] = lv.Columns[i].Width;
}
listViewHeaderWidths.Add(lv.Name, headerWidths);
}
lv.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
for(int j = 0; j < lv.Columns.Count; j++)
{
lv.Columns[j].Width = Math.Max(lv.Columns[j].Width, headerWidths[j]);
}
lv.EndUpdate();
}
Anton Kedrov answer is best one but in my case i have a listview with more than 50 columns and i update its data frequently in this case i notice listview's this.AutoResizeColumns performs much faster work so i m writing this solution also
First Method by setting with to -2
public void AutoUpdateColumnWidth(ListView lv)
{
for (int i = 0; i <= lv.Columns.Count - 1; i++) {
lv.Columns(i).Width = -2;
}
}
Second method i used (less flicker on multiple calls)
public void AutoUpdateColumnWidth(ListView lv)
{
ListViewItem nLstItem = new ListViewItem(lv.Columns(0).Text);
for (int i = 1; i <= lv.Columns.Count - 1; i++) {
nLstItem.SubItems.Add(lv.Columns(i).Text);
}
v.Items.Add(nLstItem);
lv.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
lv.Items.RemoveAt(nLstItem.Index);
}
This is simple (although it took me a while to figure out)...
We know that the width must be at least as great as the column headers, so that we see all of the header text. Beyond that, the width can expand larger to accommodate contents. Hence, we do the following:
Autosize the columns to header.
Iterate through the columns and set the minimum width property for each column to the current column width (which guarantees your columns will never get too small to see the header).
From now on, autosize columns by content.
It is not necessary to track widths separately and reset them as other posters suggest. Setting the minimum width for the column solves the issue until the header text is changed, in which case you set the minimum width to 0, autosize just the modified column, and then set the minimum width to the current width again.
EDIT: My apologies, I forgot that I was not using the standard listview, but instead the 3rd party product BetterListView (a free version is available). The standard listview columns don't appear to support minimum width. I do recommend BetterListView highly as a great alternative (much better feature set and performance).

Comparing two datagridviews

I have two datagridviews in one form. The first, datagridview1 has columns and data:
name IC EMAIL TELEPHONE
------------------------------------------------------
rOO 898989096677 AB#YAHOO.COM 018-9097878
datagridview2 has
name IC EMAIL TELEPHONE ID
-----------------------------------------------------------
rOO 898989096677 AB#YAHOO.COM 018-9097878 8787
I would like to ask help on how to compare two datagridviews, as you can see in the figure I would like to compare the four columns from one datagridview to another datagridview and see if any results match. For example, does the roo name match with the another datagridview, I want the value in the id (8787) to be sent to another datagridview.
there is a very simple solution to compare twoo datagridview and show their result in 3rd one.
for (int i = 0; i < dtView1.Rows.Count; i++)
{
for (int j = 1; j < dtView1.Columns.Count; j++)
{
if ((dtView1.Rows[i][j]) == (dtView2.Rows[i][j]))
{
// here you can add your own logic
}
else
{
// here you can add your own logic
}
}
|
Let's assume that you populate both datagrids with the same objects that contain these 4 properties from your example. You must check for the selected row from the first datagrid if in the second datagrid there is an matching object, based on your filters. Use Linq. If so, then copy the data from the selected item from the first datagrid into the matching element from the seconds. I am affraid that you will need to do all these steps manually, because there is no method that can compare two data grids and you just set some filters and the job is done. You will have some work to do, but it is not hard. Good luck.
Steve's answer does not work correctly and will not compile.
Here is my solution:
int x = 0;
int y = 0;
int i = -1;
int z = 0;
foreach (DataGridViewRow row in dataGridView1.Rows)
{
i++;
if ((dataGridView1.Rows[i].Cells[i].Value) == (dataGridView2.Rows[z].Cells[i].Value))
{
x++;
}
else
{
y++;
}
if (z < dataGridView2.Rows.Count)
{
z++;
}
if(z == dataGridView2.Rows.Count)
{
z--; //subtract 1 from the total count because the datagrid is 0 index based.
}
MessageBox.Show("Matched: " + x.ToString() + "\r\n" + "Not Matched: " + y.ToString());
A foreach loop on the datagrid will cycle through each row, you can then select a cell or even cells to compare. This code is a bit lengthy and I'm sure it can be simplified, however it does the job.
Currently this will check every row from datagrid 2 for a match in datagrid 2, only on a single column.
Its import to remember the datagrid is 0 index based. The Count property of the datagrid gives a [1]index based count, so we need to subtract the last iteration on datagrid 2.

How to reduce the time in dynamic addition of rows and columns to TablelayoutPanel?

I am interested in reducing the time of adding rows and colums to tablelayoutpanel dynamically.
I need to add 10 rows and 10 columns (maximum 10x 10 =100 controls, may be less than 100 depending upon user input), I have construct the logic which works good but the problem of my logic is it's taken considerable time in adding rows and columns to tablelayoutpanel.
for (int rowNumber = 1; rowNumber <= (TSegments.Value); rowNumber++)
{
for (int columnNumber = 1; columnNumber < (PSegments.Value) * 2 + 2; columnNumber++)
{
tempTextBox = new TextBox();
tableLayoutPanel1.Controls.Add(tempTextBox, columnNumber, rowNumber);
tempTextBox.Anchor = System.Windows.Forms.AnchorStyles.Right;
tempTextBox.Dock = DockStyle.Fill;
}
}
The best/only way to speed it up is by surrounding the changes with Suspendlayout/ResumeLayout. Just call yourtable.Suspendlayout() before changing the table and ResumeLayout() after it.

Categories