Draw only outer border for TableLayoutPanel Cells - c#

I am using the TableLayoutPanel for example if I have 3 rows and 5 columns. I want to draw only the outer border for the entire panel. By default the the panel provides CellBorderStyle which adds all side borders to all the cells available. Is there any way where we can set only outside borders?
I have provided a sample code below.
TableLayoutPanel tblPanel = new TableLayoutPanel;
tblPanel.CellBorderStyle = TableLayoutPanelCellBorderStyle.Single;
Label lblName;
TextBox txtName;
Button btnAdd;
int colCnt = 0;
for(int rw =0; rw < 3; rw++)
{
lblName = new Label();
lblName.Name = "mylabel" + rw.ToString();
tblPanel.Controls.Add(lblName, colCnt, rw);
colCnt++;
txtName = new TextBox();
txtName.Name = "mytext" + rw.ToString();
tblPanel.Controls.Add(txtName, colCnt, rw);
colCnt++;
btnAdd = new Button();
btnAdd.Name = "mybutton" + rw.ToString();
tblPanel.Controls.Add(btnAdd, colCnt, rw);
colCnt = 0;
}

TableLayoutPanel does in fact support the BorderStyle property, which is what you want. For example:
tableLayoutPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
https://msdn.microsoft.com/en-us/library/system.windows.forms.tablelayoutpanel.borderstyle(v=vs.110).aspx
It is decorated with:
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
So Intellisense won't show it to you, but it is documented and it works. I have no insight into why it is non-browsable.

You'd be better off painting the cell border yourself. Something along the following lines, then customize:
public TableForm() {
InitializeComponent();
this.tableLayoutPanel.CellPaint += tableLayoutPanel_CellPaint;
}
private void tableLayoutPanel_CellPaint(object sender, TableLayoutCellPaintEventArgs e) {
var topLeft = e.CellBounds.Location;
var topRight = new Point(e.CellBounds.Right, e.CellBounds.Top);
e.Graphics.DrawLine(Pens.Black, topLeft, topRight);
}
At design-time:
At runtime:

You can achieve by changing the property CellBorderStyle to Single or desired selection.
Property Change :
Sample :

TableLayOutPanel itself does not support a property for border except CellBorderStyle which is not what you want.
I suggest you to put your TableLayOutPanel into a Panel control and set Dock property of your TableLayOutPanel to Fill.
Then Set BorderStyle of Panel to what you want (FixedSingle or Fixed3D)

public TestForm()
{
InitializeComponent();
tableLayoutPanel.Paint += tableLayoutPanel_Paint;
}
private void tableLayoutPanel_Paint(object sender, PaintEventArgs e){
e.Graphics.DrawRectangle(new Pen(Color.Blue), e.ClipRectangle);
}

Related

Dynamically resize TabControl and Form width to the number of TabPages

I have a windows form with a TabControl and a ListView.
When I run the application, I want the Width of the TabControl to increase/decrease to show all the TabPages without horizontal scrollbar and have the Form resize it's Width accordingly, to insure that the TabControl and ListView are visible.
A screenshot is below.
To auto-size a TabControl to the size of its Headers, you need to calculate the width of the text of each Header. It's simpler in case the TabControl.SizeMode is set to Fixed, since you can set the ItemSize.Width and all Headers will have the same width.
If the TabControl.SizeMode is set to the default Normal, you have to measure the Text of each Header, adding 1px for the Border (2px if it's the second TabPage - small bug in the base Control).
In the first case, the size of the TabControl is:
tabControl1.Width = tabControl1.TabPages.Count * (tabControl1.ItemSize.Width + 1);
in the second case, measure the text of each Header using TextRendrer.MeasureText:
private int MeasureTabPagesWidth(TabControl tc)
{
if (tc.TabPages.Count == 0) return tc.Width;
int newWidth = 0;
int border = tc.TabPages.Count == 2 ? 2 : 1;
var flags = TextFormatFlags.LeftAndRightPadding;
using (var g = tc.CreateGraphics()) {
foreach (TabPage tab in tc.TabPages) {
newWidth += TextRenderer.MeasureText(g, tab.Text, tc.Font,
new Size(int.MaxValue, tc.Font.Height + 4), flags).Width + border;
}
}
return newWidth;
}
Setup the Layout:
Add a TableLayoutPanel to your Form, with one Row and two Columns (i.e., remove one Row)
Add the TabControl to the Cell on the left and the ListBox to the other Cell.
Set both Cells's style to AutoSize (after you have added your Controls).
Set the TableLayoutPanel to: AutoSize = true, AutoSizeMode = GrowAndShrink
Set the Form to auto-size in the same way
Set the Form's MinimumSize and MaximumSize. The former is usually set to the design size, the latter is up to you; you could use the current Screen WorkingArea as reference.
Calculate the new Width of the TabControl when the Form is created or loaded (i.e., in its Constructor or OnLoad() or Form.Load), so the Form will auto-size to the size of the TableLayoutPanel, whici in turn auto-sizes to the size of its child Controls.
Now you can add or remove TabPages at run-time and the Form will auto-size to the width you calculate in the TabControl.ControlAdded and TabControl.ControlRemoved event handlers (also checking whether the Control added is of Type TabPage).
Example:
The MeasureTabPagesWidth() method is the one shown above.
The TableLayoutPanel is named tlp1
The TabControl is named tabControl1
The Buttons used in the visual example have names that define their role.
public partial class AutoSizeForm : Form
{
public AutoSizeForm()
{
InitializeComponent();
tabControl1.Width = MeasureTabPagesWidth(tabControl1);
}
private void tabControl1_ControlAdded(object sender, ControlEventArgs e)
{
// Event notified after the TabPage has been added
if (e.Control is TabPage) {
tabControl1.Width = MeasureTabPagesWidth(tabControl1);
}
}
private void tabControl1_ControlRemoved(object sender, ControlEventArgs e)
{
if (e.Control is TabPage) {
// Use deferred execution, since the TabPage is removed after
// the event handler method completes.
BeginInvoke(new Action(()=> tabControl1.Width = MeasureTabPagesWidth(tabControl1)));
}
}
private void btnAddPage_Click(object sender, EventArgs e)
{
tabControl1.TabPages.Add(new TabPage("New TabpPage Text"));
}
private void btnRemovePage_Click(object sender, EventArgs e)
{
if (tabControl1.TabPages.Count > 0) {
tabControl1.TabPages.RemoveAt(tabControl1.TabPages.Count - 1);
}
}
private void btnAddCtlToTLP_Click(object sender, EventArgs e)
{
tlp1.ColumnCount += 1;
tlp1.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
var mc = new MonthCalendar();
tlp1.SetColumn(mc, tlp1.ColumnCount - 1);
tlp1.Controls.Add(mc);
}
}
This is how it works:
Tested in Windows 7, since this appears to be the System in use
Sample Project:
Sample Project on Google Drive (.Net Framework 4.8 - C# 7.3)
Rebuild the Solution before running
Starting out with that form, I'm going to add 8 tabs at run-time, calculate width of the text in the tabs + padding size x2 (both sides of the tabs) and then resize controls as needed.
public Form1()
{
InitializeComponent();
//Clear our default tabs.
tabControl1.TabPages.Clear();
//Add more tabs than would be visible by default
for (int i=1;i<=8;i++)
{
tabControl1.TabPages.Add("Tab " + i.ToString());
}
ResizeTabControl();
ResizeListViewControl();
ResizeForm();
}
void ResizeTabControl()
{
int tabCount = tabControl1.TabCount;
float length = 0;
using (Graphics g = CreateGraphics())
{
//Iterate through the tabs and get the length of the text.
for (int i = 0; i <= tabCount - 1; i++)
length += g.MeasureString(tabControl1.TabPages[i].Text, tabControl1.Font).Width;
}
//Resize the tab control where X is the length of all text in the tabs plus padding x 2 x total tabs.
tabControl1.Size = new Size(Convert.ToInt32(length) + (tabCount * 2 * tabControl1.Padding.X), tabControl1.Width);
}
void ResizeListViewControl()
{
//Move listview 10 pixels away from tabcontrol's edge
listView1.Location = new Point(tabControl1.Location.X + tabControl1.Width + 10, listView1.Location.Y);
}
void ResizeForm()
{
//Resize form to accomodate changes.
this.Width = listView1.Location.X + listView1.Width + 20;
}
After it's all said and done, this is what it looks like:
And with 20 tabs, because why not.

Why does my Panel cut the text strangely off?

I have the problem that the text inside my panel gets cut of strangely. The panel is located inside a textbox. But even if I replace the textbox by a flowlayoutpanel, I have the same issue.
Code:
List<string> list = datenbank.FerienAuswahl(monat, jahr);
int i = 0;
//Create Panel
try
{
//Fill Panel
do
{
Label panel = new Label();
panel.Name = "panel" + i;
panel.Height = 30;
panel.Width = 400;
panel.AutoSize = false;
panel.TextAlign = ContentAlignment.MiddleCenter;
panel.ForeColor = Color.Black;
panel.Text = list[i];
Label ferien = new Label();
panel.Controls.Add(ferien);
tbFerien.Controls.Add(panel);
i++;
} while (i < list.Count);
}
catch { }
Result:
I have already tried to change the width of the panel. But as result I only get a messed up alignment of the text.
The only settings of the textbox I have changed are these:
Multiline: True
TextAlign: Center
Size: 359; 125
Does Someone know what else I could try ?
These lines worry me:
Label panel = new Label();
Label ferien = new Label();
panel.Controls.Add(ferien);
tbFerien.Controls.Add(panel);
It seems to me you are adding one label to another. That's not good. Use a Panel or TableLayoutPanel instead of the actual panel and make sure you have your positioning good.

Transparent canvas, with opaque elements

I'm trying to simulate an Android UI element that unfortunately doesn't exist in Windows 7 phone: ListPreference
I thought about using a Popup, that would take exactly the whole screen (to simulate a modal window).
So the popup would be made of the following elements:
Popup -> Canvas -> Border -> StackPanel -> RadioButtons
The Canvas would be fully transparent (or lightly whitish to clearly show that the element underneath aren't available)
The border would be made so it only big enough to contain all the RadioButtons
Then the StackPanel would be opaque and black.
Unfortunately, if I make the bottom canvas transparent, all children elements are also transparent. I can only make the elements more transparent.
The way transparency works is slightly different than with Android or iPhone (where it's quite easy to have a parent fully transparent, but opaque children).
Is there a way to make a parent fully transparent with the children opaque?
Or maybe someone could suggest another way to simulate a modal window.
Who knows, maybe someone even developed a ListPreference-like UIElement :)
Thank you
Here is how I ended up doing it.
It works in a similar fashion as ListPreference on Android. The constructor takes a string, an array of string and an int indicating which is the default value
When the windows is closed, the delegate Dismissed is called..
So you call it like so:
string[] choices = { "Choice 1", "Choice 2", "Choice3" };
ListPreference lp = new ListPreference("name", choices, 1);
lp.dismissed += new ListPreferences.DismissedHandler(lp_Dismissed);
the code:
public class ListPreference
{
Popup p;
string Name;
int oldValue;
public delegate void DismissedHandler(string name, bool changed, int newvalue);
public event DismissedHandler Dismissed;
public bool IsOpen
{
get
{
return p.IsOpen;
}
set
{
p.IsOpen = value;
}
}
public ListPreference(string name, Array elements, int default_value)
{
p = new Popup();
Name = name;
Dismissed = null;
oldValue = default_value;
double height = (App.Current.RootVisual as FrameworkElement).ActualHeight;
double width = (App.Current.RootVisual as FrameworkElement).ActualWidth;
p.VerticalOffset = SystemTray.IsVisible ? 32.0 : 0.0;
p.Height = height;
p.Width = width;
Canvas canvas = new Canvas();
SolidColorBrush colorBrush = new SolidColorBrush(Colors.Black);
colorBrush.Opacity = 0.75;
//Color.FromArgb(0xff, 0x8a, 0x8a, 0x8a));
canvas.Background = colorBrush;
//canvas.Opacity = 0.765;
canvas.Height = height;
canvas.Width = width;
p.Child = canvas;
Border border = new Border();
border.Width = width - 50.0 * 2.0;
border.BorderBrush = new SolidColorBrush(Colors.LightGray);
border.BorderThickness = new Thickness(5.0);
border.Background = new SolidColorBrush(Colors.Black);
canvas.Children.Add(border);
StackPanel panel2 = new StackPanel();
panel2.Orientation = System.Windows.Controls.Orientation.Vertical;
int i = 0;
foreach (string val in elements)
{
RadioButton radio1 = new RadioButton();
radio1.GroupName = "group1";
radio1.Content = val;
if (i == default_value)
radio1.IsChecked = true;
int j = i;
radio1.Click += (sender, args) => radio1_Checked(radio1, j);
i++;
panel2.Children.Add(radio1);
}
Button button1 = new Button();
button1.Background = new SolidColorBrush(Colors.Black);
button1.Foreground = new SolidColorBrush(Colors.White);
button1.Opacity = 1.0;
button1.Content = "Cancel";
button1.Margin = new Thickness(5.0);
button1.Click += new RoutedEventHandler(closeButton_Click);
panel2.Children.Add(button1);
border.Child = panel2;
// Open the popup.
p.IsOpen = true;
p.UpdateLayout();
border.Height = panel2.DesiredSize.Height + 5.0 * 2.0;
border.SetValue(Canvas.TopProperty, (height - border.Height) / 2.0);
border.SetValue(Canvas.LeftProperty, (width - border.Width) / 2.0);
p.UpdateLayout();
}
void closeButton_Click(object sender, RoutedEventArgs e)
{
// Close the popup.
p.IsOpen = false;
if (Dismissed != null)
{
Dismissed(Name, false, -1);
}
}
void radio1_Checked(object sender, int idx)
{
p.IsOpen = false;
if (Dismissed != null)
{
Dismissed(Name, idx != oldValue, idx);
}
}
}
I would suggest creating a Usercontrol that would do what you need. Set the LayoutRoot grid's background to PhoneSemitransparentBrush or changing the opacity will change the child element's opacity as well. Then your child elements can have any opacity you'd like. You can add this control as a child to the popup. Additionally, you can add doubleanimation to the popup with the opened and closed event triggers. Change the design height of the UserControl to 480x760 to simulate full page.
To answer your question. Using resources like PhoneSemitransparentBrush and TransparentBrush for the Canvas background is one of your options. Opacity will change the opacity of the whole UIElement including its children.

C#: Changing Button BackColor has no effect

I'm having a problem with C# buttons in Windows Forms.
I've create a number of buttons programmatically and add them to a form afterwards.
Interestingly, every modification to those buttons (location and size) except for the modification of the BackColor is readily executed. Only the button's color remains unchanged.
The code looks something like this:
public class SimpleSortAlgDisplayer : ISortAlgDisplayer
{
#region ISortAlgDisplayer Member
void ISortAlgDisplayer.Init(int[] Data)
{
this.DataLength = Data.Length;
this.DispWin = new CurrentSortStateWin();
this.DispWin.Show();
this.DispWin.Size = new Size(60 + (10 * this.DataLength), 120);
this.myArrayElements = new Button[this.DataLength];
for (int i = 0; i < this.DataLength; i++)
{
this.myArrayElements[i] = new Button();
//begin of series of invoked actions
this.myArrayElements[i].Size=new Size(5,(int)(((80)*(double)Data[i])/1000));
this.myArrayElements[i].Location = new Point(30 + (i * 10), 90-(this.myArrayElements[i].Size.Height));
this.myArrayElements[i].Enabled = true;
this.myArrayElements[i].BackColor = Color.MidnightBlue;
this.myArrayElements[i].UseVisualStyleBackColor = true;
this.DispWin.Controls.Add(this.myArrayElements[i]);
this.myArrayElements[i].Refresh();
}
}
Ideas anyone?
A similar question was asked here but the answers to it were not very helpful:
Trying to use Invoke gives me the run-time error that DispWin is not yet created.
Setting UseVisualStyleBackColor to false changes nothing.
Setting BackColor and ForeColor or Showing DispWin only after adding and formatting the Buttons also had no effect.
Where am I going wrong?
You are trying to set up the color, but then you override it saying UseVisualStyleBackColor = true
if you want to use your custom color, you need to set UseVisualStyleBackColor to false or the color will only be applied to the button upon mouse over.
a simple test uploaded to GitHub
public partial class mainForm : Form
{
Random randonGen = new Random();
public mainForm()
{
InitializeComponent();
}
private void mainForm_Load(object sender, EventArgs e)
{
populate();
}
private void populate()
{
Control[] buttonsLeft = createButtons().ToArray();
Control[] buttonsRight = createButtons().ToArray();
pRight.Controls.AddRange(buttonsRight);
pLeft.Controls.AddRange(buttonsLeft);
}
private List<Button> createButtons()
{
List<Button> buttons = new List<Button>();
for (int i = 1; i <= 5; i++)
{
buttons.Add(
new Button()
{
Size = new Size(200, 35),
Enabled = true,
BackColor = GetColor(),
ForeColor = GetColor(),
UseVisualStyleBackColor = false,
Left = 20,
Top = (i * 40),
Text = String.Concat("Button ", i)
});
}
return buttons;
}
private Color GetColor()
{
return Color.FromArgb(randonGen.Next(255), randonGen.Next(255), randonGen.Next(255));
}
}
result
If FlatStyle for button is set to System, it will not show any backcolor rather use backcolor from template of system colors.
Make sure you do not have a BackgroundImage set. This overrides the BackColor.
In the properties window for Button. Look for 'FlatStyle' property and change it from 'System' to 'Flat', 'Standard' or 'Popup' and you will be able to see the button color change. I just fixed my issue with this.

Scaling WinForms TableLayoutPanel

I have an issue with TableLayoutPanel, which fills an userControl (DockStyle.Fill). I face a trouble, when this control is being resized, I need all cells to change their size, but only last row and last column changes size (so whole tableLayoutPanel fills the control). I change this controler size using Bounds property.
let's say I wrote following code:
// creating tableLayoutPanel:
private void createTableLayoutPanel(int count)
{
tableLayoutPanel = new TableLayoutPanel();
tableLayoutPanel.MouseClick += new MouseEventHandler(this.observe_MouseClick);
tableLayoutPanel.AutoSize = true;
tableLayoutPanel.ColumnCount = 3;
tableLayoutPanel.RowCount = count;
tableLayoutPanel.Dock = DockStyle.Fill;
tableLayoutPanel.AutoSize = true;
this.Controls.Add(tableLayoutPanel);
}
// resizing:
private void OnMouseWheel(MouseEventArgs e)
{
this.Bounds = new Rectangle(this.Location.X, this.Location.Y, (int)(this.Width*newScale),(int)(this.Height*newScale));
}
Thanks for any help.
For each row, add an item to the TableLayoutPanel's RowStyles collection with SizeType = SizeType.Percent and Height = 100 / tableLayoutPanel.RowCount. Do the same with the ColumnStyles collection.

Categories