I have a TableLayoutPanel that has a dynamic amount of columns and rows determined by the user. I want the buttons inside to be square and the same size, but whenever I use a loop to set the column/rows styles, they never turn out to be the size I want them to be.
How can I get the column/row styles to set the appropriate widths and height os the container elements?
Here is the loop method of the code that handles setting the width size of the table (I use a similar method for rows)
void FormatTableWidth(ref TableLayoutPanel container)
{
TableLayoutColumnStyleCollection columnStyles = container.ColumnStyles;
foreach (ColumnStyle style in columnStyles)
{
style.SizeType = SizeType.Absolute;
style.Width = 60;
}
}
You can do it like....
public void AddButtontControls()
{
tblPanel.SuspendLayout();
tblPanel.Controls.Clear();
tblPanel.GrowStyle = TableLayoutPanelGrowStyle.FixedSize;//.AddColumns;
tblPanel.ColumnStyles.Clear();
for (int i = 0; i < tblPanel.ColumnCount; i++)
{
ColumnStyle cs = new ColumnStyle(SizeType.Percent, 100 / tblPanel.ColumnCount);
tblPanel.ColumnStyles.Add(cs);
//Add Button
Button a = new Button();
a.Text = "Button " + i + 1;
tblPanel.Controls.Add(a);
}
tblPanel.ResumeLayout();
}
Sorry to tell you, but you don't use the right control.
You definitely must use FlowLayoutPanel control, and you can add as many controls you want in it, you can tell which direction will fill the control, if it wrap content or not, and many others.
And the most important - It Will Not Flickering like TableLayoutPanel does :)
Related
There's some spacing between the Buttons I add to my TableLayoutPanel. I removed the border in the Button and set the Margin and Padding to 0 in the Panel. But I continue getting that spacing.
tableLayoutPanel.RowCount is set to 8 and the Rows collection I've added 8 rows with Size Type Absolute.
Am I missing something? Here's the code:
private void FillSelectLayout()
{
tableLayoutPanelSelect.Controls.Clear();
tableLayoutPanelSelect.RowStyles.Clear();
tableLayoutPanelSelect.RowCount = 8;
for (int i = 0; i < 8; i++)
{
Button buttonSelector = new Button();
buttonSelector.Height = 64;
buttonSelector.Width = 100;
buttonSelector.FlatStyle = FlatStyle.Flat;
buttonSelector.FlatAppearance.BorderSize = 0;
buttonSelector.BackColor = Color.Yellow;
tableLayoutPanelSelect.Controls.Add(buttonSelector, 0, i);
}
}
Here's how it's displayed:
To remove the space between buttons in cells, it's enough to set dock property of them to fill and then remove default margins of buttons:
var b = new Button();
b.Dock = DockStyle.Fill;
b.Margin = new Padding(0);
Note:
Usually it's better to set Dock property of controls which you host in cells to Fill. This way your controls will follow TableLayouPanel sizing rules which you set for columns and rows.
TableLayoutPanel use Margin property of control to set the location of control in cell. So If you don'n want to set Dock and you prefer to set the Size manually, it's enough to set the Margin only.
I .. set the Margin and Padding to 0 in the Panel.
Why didn't you remove the Margin in the Buttons instead:
buttonSelector.Margin = new Padding(0);
MSDN:
The Margin property defines the space around the control that keeps
other controls a specified distance from the control's borders.
I faced the same problem while using different control in TableLayoutPanel
You can do this
Go to Design View
Click on the properties
GoTo Columns, When you click text box besides Columns, a button (...) appears on extreme right, click it
A pop up window appears, Select AutoSize (Instead of Absolute or Percentage).
In the same window in Show: select Rows and again select Autosize.
Click okay and you are done.
I am wondering if it is possible to have a dataGridView column to adjust automatically its width to its content and at the same time being resizable by the user ?
Here what i have tried so far :
dataGridView.AllowUserToResizeColumns = true;
dataGridView.Columns[0].AutoSizeMode=DataGridViewAutoSizeColumnMode.DisplayedCells;
dataGridView.Columns[0].Resizable = DataGridViewTriState.True;
But, I am still unable to resize the column size manually.
If anyone already faced this issue or have any idea let me know.
Thanks.
I finally find a way to do what I wanted.
The idea is to
let the dataGridView resize the columns itself to fit the content, and then
change theAutoSizeColumnMode and set the width with the value you just stored.
Here is the code:
dataGridView.Columns[i].AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells;
int widthCol = dataGridView.Columns[i].Width;
dataGridView.Columns[i].AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
dataGridView.Columns[i].Width = widthCol;
Hope this will help.
Building on the code above to iterate it for all columns and also auto adjust the width of the DataGridView
//initiate a counter
int totalWidth = 0;
//Auto Resize the columns to fit the data
foreach (DataGridViewColumn column in mydataGridView.Columns)
{
mydataGridView.Columns[column.Index].AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells;
int widthCol = mydataGridView.Columns[column.Index].Width;
mydataGridView.Columns[column.Index].AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
mydataGridView.Columns[column.Index].Width = widthCol;
totalWidth = totalWidth + widthCol;
}
//the selector on the left of the DataGridView is about 45 in width
mydataGridView.Width = totalWidth + 45;
You can just use this simple code:
dataGridView1.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells);
After setting datasource. It will set the width and allow resize.
The thing that is not obvious (it was not to me) is that use of AutoSizeMode means we want the size to be managed, so that implies that the user would not (cannot) do a resize. So what I have tried and it seems to work is to use DataGridView.AutoResizeColumn (note that it does a one-time resize) for each column or most columns then set DataGridView.AllowUserToResizeColumns to true. There are probably other variations but the important thing is that use of AutoSizeMode is mutually exclusive with allowing the user to do a resize.
Thanks for the solution above (To iterate through the DataGridView.Columns, change AutoSizeMode to a valid one, collect width value and set it back after change AutoSizeMode to DataGridViewAutoSizeColumnMode.None).
I struggled with it, and noticed it won't work whenever it is called from the class constructor or any line before Form.Show() or Form.ShowDialog(). So I put this code snippet in the Form.Shown event and this works for me.
My transformed code, reguardless of whatever DataGridView.AutoSizeColumnsMode set before, I use DataGridViewColumn.GetPreferredWidth() instead of changing DataGridViewColumn.AutoSizeMode and set the width value immediately, then change DataGridView.AutoSizeColumnsMode once:
private void form_Shown(object sender, EventArgs e)
{
foreach (DataGridViewColumn c in dataGridView.Columns)
c.Width = c.GetPreferredWidth(DataGridViewAutoSizeColumnMode.DisplayedCells, true);
dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
}
Be sure to set
dataGridView.AllowUserToResizeColumns = true;
I don't know how come this only works after the form is shown.
Simple solution: Either bound filed or template filed. you can use ItemStyle-Width
<asp:BoundField DataField="SSSS" HeaderText="XXX" ItemStyle-Width="125px"/>
<asp:TemplateField HeaderText="EEEE" ItemStyle-Width="100px">
I have a performance issue with the WPF DataGrid (.net 4.0)
first, some details:
I have a datagrid with an Observable collection as ItemsSource.
this observableCollection itself contains collections of objects, each collection hence being a row, each object being a cell ("logical" cell of course, not actual dataGridCell)
the reason why I do this is because I only know at runtime how many columns I will have in my dataGrid.
then I bind each DataGridCell's value to the value of the object in the "logical" table (= the collection of collections)
now the trouble I have is that I also have to be able to change whatever cell's Properties (like Background, Foreground, FontFamily, etc...) at any time while the app is running.
The solution I came up with is one involving setting the columns' cellStyles with bindings that bind to the "logical" cells' properties
here Is a sample code (no Xaml in my app):
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Width = 1200;
Height = 780;
Top = 60;
Left = 200;
DataGrid dg = new DataGrid();
Content = dg;
ObservableCollection<Row> Source = new ObservableCollection<Row>();
dg.ItemsSource = Source;
dg.SelectionMode = DataGridSelectionMode.Extended;
dg.IsSynchronizedWithCurrentItem = true;
dg.CanUserSortColumns = false;
dg.CanUserReorderColumns = true;
dg.CanUserResizeColumns = true;
dg.CanUserResizeRows = true;
dg.CanUserAddRows = false;
dg.CanUserDeleteRows = false;
dg.AutoGenerateColumns = false;
dg.EnableColumnVirtualization = true;
dg.EnableRowVirtualization = false; // unuseful in my case : I alawys have less lines than the DG can contain
dg.VerticalScrollBarVisibility = ScrollBarVisibility.Visible;
dg.GridLinesVisibility = DataGridGridLinesVisibility.None;
dg.HorizontalGridLinesBrush = Brushes.LightGray;
dg.MinRowHeight = 20;
dg.RowHeaderWidth = 20;
for (int i = 0; i < 100; i++)
{
DataGridTextColumn column = new DataGridTextColumn();
column.Binding = new Binding(String.Format(CultureInfo.InvariantCulture, "[{0}].Text", i));
Style style = new Style(typeof(DataGridCell));
style.Setters.Add(new Setter(DataGridCell.BackgroundProperty, new Binding(String.Format(CultureInfo.InvariantCulture, "[{0}].Background", i))));
style.Setters.Add(new Setter(DataGridCell.ForegroundProperty, new Binding(String.Format(CultureInfo.InvariantCulture, "[{0}].Foreground", i))));
column.CellStyle = style;
column.Header = "Column " + i;
dg.Columns.Add(column);
}
for (int i = 0; i < 35; i++)
{
Row dgRow = new Row();
Source.Add(dgRow);
for (int j = 0; j < 100; j++)
dgRow.Add(new TextBox() { Text = "cell " + i + "/" + j, Background = Brushes.AliceBlue, Foreground = Brushes.BlueViolet });
}
}
}
public class Row : ObservableCollection<TextBox>
{
}
my problem is: with the VolumnVirtualisation On (I don't need row Virtualization in my case), the grid takes about 2sec to load, and then 1sec each time I move the horizontal scrollbar by a big leap (clic in the scrollBar bg, not the arrow)
this is too much for my purpose
so my question is: am I doing something wrong and if yes, what? what better way to do this do I have?
thanks for reading
If ColumnVirtualization make so problems, why do you need it?
You can do a several improvements, but they can't solve the problem completely.
Change TextBoxes for light-weight objects:
public class TextItem
{
public string Text { get; set; }
public Brush Background { get; set; }
public Brush Foreground { get; set; }
}
public class Row : ObservableCollection<TextItem>
{
}
Enable VirtualizingStackPanel: dg.SetValue(VirtualizingStackPanel.IsVirtualizingProperty, true);
Replace styles with templates:
for (int i = 0; i < 100; i++)
{
DataGridTemplateColumn column = new DataGridTemplateColumn();
column.CellTemplate = (DataTemplate)XamlReader.Parse(
"<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>" +
"<TextBlock DataContext='{Binding [" + i + "]}' Text='{Binding Text}' Background='{Binding Background}' Foreground='{Binding Foreground}'/>" +
"</DataTemplate>");
column.Header = "Column " + i;
dg.Columns.Add(column);
}
After a lot of time put into this, I came to the conclusion that I've reached the limit.
Here are a few thoughts for those that are dealing with the same issue:
There is no easy way to manage a single cell's visual properties in WPF as of .net 4.0: MS did not plan anything to make this easy so basically you are stuck with 2 possibilities to do this:
get the actual dataGridCell using some kind of helper function and then change its properties directly. This is easily done but can lead to big trouble if virtualization is turned on.
bind each cell's visual properties to dependency properties from your VM inside the dataGridCell's Style. you can use either the DataGrid.CellStyle or the Column.CellStyle to do so, depending on you constraints. This slows the dataGrid quite a bit though, and is quite a hassle to manage.
if like me you have no choice but to use the second option (because I need virtualization), here are a few things to consider:
you are not stuck with C#. There is actually a way to do your CellStyle in Xaml. See Martino's post on this issue. As far as I'm concerned, this works pretty well. I tweaked it a bit so as not to have to use the hack though: I define my style in Xaml and apply it to the Column.CellStyle. Then when I create a column in my code behind, I simply create a new Style inheriting this one, and I add the Tag setter with a binding set to: "[column's Index].Self". This breaks the MVVM model, but I'm not using it anyway and It's easier to maintain like this.
obviously, the more properties you have to bind, the more time it will take for your dataGrid to load, so stick to the minimum (using light-weight objects does make a small difference, as stated by Vorrtex).
while using templates or styles makes absolutely no difference regarding performance, if you are using dataGridTemplateColumns, you'd want to set you bindings up directly in the template instead of adding a style on top of the template, obviously (this does make a small difference with a huge number of data)
if anybody has anything to add to this, please do so! I'm still looking for any idea that can improve things up and would be glad for whatever crazy idea you have on the subject. Even in 3 months...
How do I add padding, or some space between the textboxes when using dockstyle.top property?
for(int i =0; i< 10; i++) {
textboxes[i] = new TextBox();
textboxes[i].Dock = DockStyle.Top;
mypanel.Controls.Add(textboxes[i]);
}
The code above puts textboxes right beneath each other. Can't figure this out without using mass panels or fixed positioning. How to do the following?
1) I would like to add around 10-20pixels between boxes.
2) How to change size (height,width) of the textboxes, since when using dockstyle.top it ignores the size commands ?
With DockStype.Top you can't change the width of your TextBoxes, cause they are docked. You can only change the height. But to change the height of a TextBox you have to set the Multiline = true beforehand.
To get the space between the different boxes you have to put each TextBox within a panel, set the TextBox.Dock = Fill, the Panel.Dock = Top and the Panel.Padding = 10. Now you have some space between each TextBox.
Sample Code
for (int i = 0; i < 10; i++)
{
var panelTextBox = CreateBorderedTextBox();
this.Controls.Add(panelTextBox);
}
private Panel CreateBorderedTextBox()
{
var panel = CreatePanel();
var textBox = CreateTextBox();
panel.Controls.Add(textBox);
return panel;
}
private Panel CreatePanel()
{
var panel = new Panel();
panel.Dock = DockStyle.Top;
panel.Padding = new Padding(5);
return panel;
}
private TextBox CreateTextBox()
{
var textBox = new TextBox();
textBox.Multiline = true;
textBox.Dock = DockStyle.Fill;
return textBox;
}
What i forgot, you can also give a try to the FlowLayoutPanel. Just remove the DockStyle.Top from the panels and put them into the FlowLayoutPanel. Also you should set the FlowDirection to TopDown. Maybe this can also help you to solve your problem, too.
Another work around that suits smaller layouts is to just add a Label control afterwards also docked to the Top, which is not AutoSized, Text=" ", Height=your padding. This is quite useful for the odd bit of padding when using the designer.
I know where you're coming from, this is especially frustrating after returning to WinForms from WPF.
I would suggest using a TableLayoutPanel, in which each TextBox would get its own cell, and adjusting the properties of the cells. This should solve both your padding and size problems.
Another alternative would be to use some more complex layout controls, such as the DevExpress ones (not free).
I have a Form and a DataGridView. I populate the DataGridView at runtime, so I want to know how do I resize the Form dynamically according to the size of the DataGridView? Is there any sort of property or method? Or do I have to determine the size myself and update accordingly?
You can find the actual width by count Columns width.
Don't forget that your form may be more complex and your should count other controls.
public class YourForm : Form
{
public YourForm()
{
DataGridView _dgv = new DataGridView() { Dock = DockStyle.Fill};
Controls.Add(_dgv);
}
public void CorrectWindowSize()
{
int width = WinObjFunctions.CountGridWidth(_dgv);
ClientSize = new Size(width, ClientSize.Height);
}
DataGridView _dgv;
}
public static class WinObjFunctions
{
public static int CountGridWidth(DataGridView dgv)
{
int width = 0;
foreach (DataGridViewColumn column in dgv.Columns)
if (column.Visible == true)
width += column.Width;
return width += 20;
}
}
int dgv_width = dataGridView1.Columns.GetColumnsWidth(DataGridViewElementStates.Visible);
int dgv_height = dataGridView1.Rows.GetRowsHeight(DataGridViewElementStates.Visible);
this.Width = dgv_width;
this.Height = dgv_height;
this.Width resizes this Form width.
Of course you've to add fixed values (for example margin, Form title heigth, ecc.).
With tests i've reached the values working for me (don't ask why...):
this.Width = dgv_width + 147;
this.Height = dgv_height + 47;
Normally controls adapt their sizes to the size of the containing form. To adjust the size of your form to the size of your DataGridView, you do have to determine the size yourself and then set the form's size to match, remembering to take into account the extra size required by the form's menu strip and/or toolbars, status bars or other controls.
In your case, it would probably be best not to resize the form to match the grid view control. Most likely, you will have many more rows in your grid view than could fit on your Windows screen, and you don't want to have a form that extends below the viewable desktop area. Generally speaking, this type of situation is exactly why you want to have a scrollable grid view - for viewing more data than can fit on the screen at one time.
I would go the other direction and size the grid to the form. (some users may have low res)
Set 'WindowState' property of the form => maximized. (optional)
Set 'anchor' property of the DGV => 'Top, Bottom, Left, Right'.
You may be able to make use of the PreferredSize property (MSDN PreferredSize entry). For DataGridView controls, I found that the preferred width and height were about 20 units bigger than I expected. I guess that the control might be calculating its preferred size taking scroll bars into account.
Another caveat I discovered is that the PreferredSize calculation will not be accurate immediately after adding or changing items in the table. To get around this I made a handler for the RowHeadersWidthChanged event.
Here is what worked for me:
class GridToy {
private DataGridView grid;
public GridToy(DataGridView dgv) {
grid = dgv;
grid.RowHeadersWidthChanged += AdjustWidth; // Event handler.
Layout();
}
public void Layout() {
// Just do some arbitrary manipulation of the grid.
grid.TopLeftHeaderCell.Value = "Some Arbitrary Title";
}
public void AdjustWidth() {
Control horizontal = grid.Controls[0]; // Horizontal scroll bar.
Control vertical = grid.Controls[1]; // Vertical scroll bar.
grid.Width = grid.PreferredSize.Width - vertical.Width + 1;
grid.Height = grid.PreferredSize.Height - horizontal.Height + 1;
}
}
You could set the Property " Height " to Auto in the form after classifying or using an ID and this should do it ,,
I just tried it .. and It worked
#form1{
background-color:white;
height:auto;
width:1500px;
border-top:medium solid #3399FF;
margin-top:100px;
margin-left:30px;
display: inline-block;
text-align: center;
float: none;
}
I'm just putting exactly what I did in case you got lost .. Don't worry about the other properties there for my design.
Set AutoSizeColumnsMode :Fill in Grid Properties