I'd like to have a toolstrip with some buttons on it (WinFroms/c#/.net4). I'd like to have the clicked button be checked and all others unchecked, so I want to have only the clicked button checked, all the others unchecked.
I know toolstrip button has checkedonlclick property, but when I click one button, others may be also checked. In good old VB6 there was an automatic solution for this problem: buttongroup on toolbar. Is there any similar in Winfoms?
Or should I handle it from code?! Switch all other buttons to unchecked state when one button is checked? If so, then it would not be my favourite solution...
Call this code on the toolStripButton_Click events and you should get the desired result.
foreach (ToolStripButton item in ((ToolStripButton)sender).GetCurrentParent().Items)
{
if (item == sender) item.Checked = true;
if ((item != null) && (item != sender))
{
item.Checked = false;
}
}
I guess this is an old question, however I was looking for a solution to this problem as well and ended up making a kind of ToolStripRadioButton by extending ToolStripButton. The behaviour should be the same as a normal radio button, as far as I can see. I have however added a group id to make it possible to have multiple radio button groups within the same toolstrip.
One can add the radio button to a toolstrip like a normal ToolStripButton:
To make the button stand more out when checked I have given it a gradient background (CheckedColor1 to CheckedColor2 from top to bottom):
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
public class ToolStripRadioButton : ToolStripButton
{
private int radioButtonGroupId = 0;
private bool updateButtonGroup = true;
private Color checkedColor1 = Color.FromArgb(71, 113, 179);
private Color checkedColor2 = Color.FromArgb(98, 139, 205);
public ToolStripRadioButton()
{
this.CheckOnClick = true;
}
[Category("Behavior")]
public int RadioButtonGroupId
{
get
{
return radioButtonGroupId;
}
set
{
radioButtonGroupId = value;
// Make sure no two radio buttons are checked at the same time
UpdateGroup();
}
}
[Category("Appearance")]
public Color CheckedColor1
{
get { return checkedColor1; }
set { checkedColor1 = value; }
}
[Category("Appearance")]
public Color CheckedColor2
{
get { return checkedColor2; }
set { checkedColor2 = value; }
}
// Set check value without updating (disabling) other radio buttons in the group
private void SetCheckValue(bool checkValue)
{
updateButtonGroup = false;
this.Checked = checkValue;
updateButtonGroup = true;
}
// To make sure no two radio buttons are checked at the same time
private void UpdateGroup()
{
if (this.Parent != null)
{
// Get number of checked radio buttons in group
int checkedCount = this.Parent.Items.OfType<ToolStripRadioButton>().Count(x => x.RadioButtonGroupId == RadioButtonGroupId && x.Checked);
if (checkedCount > 1)
{
this.Checked = false;
}
}
}
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
this.Checked = true;
}
protected override void OnCheckedChanged(EventArgs e)
{
if (this.Parent != null && updateButtonGroup)
{
foreach (ToolStripRadioButton radioButton in this.Parent.Items.OfType<ToolStripRadioButton>())
{
// Disable all other radio buttons with same group id
if (radioButton != this && radioButton.RadioButtonGroupId == this.RadioButtonGroupId)
{
radioButton.SetCheckValue(false);
}
}
}
}
protected override void OnPaint(PaintEventArgs e)
{
if (this.Checked)
{
var checkedBackgroundBrush = new LinearGradientBrush(new Point(0, 0), new Point(0, this.Height), CheckedColor1, CheckedColor2);
e.Graphics.FillRectangle(checkedBackgroundBrush, new Rectangle(new Point(0, 0), this.Size));
}
base.OnPaint(e);
}
}
Perhaps useful to others as well.
I'm not aware of a way to do this in the designer, but it's fairly simple to do in code:
readonly Dictionary<string, HashSet<ToolStripButton>> mButtonGroups;
...
ToolStripButton AddGroupedButton(string pText, string pGroupName) {
var newButton = new ToolStripButton(pText) {
CheckOnClick = true
};
mSomeToolStrip.Items.Add(newButton);
HashSet<ToolStripButton> buttonGroup;
if (!mButtonGroups.TryGetValue(pGroupName, out buttonGroup)) {
buttonGroup = new HashSet<ToolStripButton>();
mButtonGroups.Add(pGroupName, buttonGroup);
}
newButton.Click += (s, e) => {
foreach (var button in buttonGroup)
button.Checked = button == newButton;
};
return newButton;
}
I haven't done it in a little but if you use a group box around radio buttons it will only allow one to be checked at a time.
Related
I don't know much about C# so I apologize in advance if my question has been answered elsewhere. I don't know what to search for.
I'm making a tic tac toe game in windows forms. I've set it up like this: each cell has 2 buttons(x and o) and two labels (X and O). Clicking one of the buttons will change the corresponding label's visible property to true while at the same time disabling (enable=false) the opposite button from being selected. There are 9 cells total. I also have a menu button (reset) that when clicked will enable all buttons and hide all labels so that the game may be re-played.
I'm looking for a way to reset all the items I want without having to individually type code for each item. Is there a way to group the items together so that they can all be fired at once with a minimum of code?
Source code is incomplete as I am still writing the program. I've posted the code for three cells.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Tick_Tack_Toe
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void xButton1_Click(object sender, EventArgs e)
{
xLabel1.Visible = true;
oButton1.Enabled = false;
}
private void oButton1_Click(object sender, EventArgs e)
{
oLabel1.Visible = true;
xButton1.Enabled = false;
}
private void xButton2_Click(object sender, EventArgs e)
{
xLabel2.Visible = true;
oButton2.Enabled = false;
}
private void oButton2_Click(object sender, EventArgs e)
{
oLabel2.Visible = true;
xButton2.Enabled = false;
}
private void resetToolStripMenuItem_Click(object sender,
EventArgs e)
{
xButton1.Enabled = true;
oButton1.Enabled = true;
xButton2.Enabled = true;
oButton2.Enabled = true;
xButton3.Enabled = true;
oButton3.Enabled = true;
xLabel1.Visible = false;
oLabel1.Visible = false;
xLabel2.Visible = false;
oLabel2.Visible = false;
xLabel3.Visible = false;
oLabel3.Visible = false;
}
}
}
One way you can do this is to loop through the controls collection. Since you seem to have a standard on the naming of the controls, you can first narrow the collection down to only those whose name begins with 'o' or 'x'. Then, you can look at the type of control, and if it's a label you can hide it, and if it's a button you can enable it:
private void ResetControls()
{
foreach (Control control in this.Controls)
{
// Only look at controls whose names begin with 'o' or 'x'
if (control.Name.StartsWith("o") ||control.Name.StartsWith("x"))
{
// Hide it if it's a label
if(control.GetType() == typeof(Label))
{
control.Visible = false;
}
// Enable it if it's a button
else if (control.GetType() == typeof(Button))
{
control.Enabled = true;
}
}
}
}
You mentioned that all your button and labels are inside their own panel control, which has it's own control collection. So you can modify the code above to first search for panel controls, then search the panel control's control collection for buttons and labels:
private void ResetControls()
{
foreach (Control control in this.Controls)
{
if (control is Panel)
{
var panel = control as Panel;
foreach (Control panelControl in panel.Controls)
{
// Only look at controls whose names begin with 'o' or 'x'
if (panelControl.Name.StartsWith("o") || panelControl.Name.StartsWith("x"))
{
// Hide it if it's a label
if (panelControl is Label)
{
panelControl.Visible = false;
}
// Enable it if it's a button
else if (panelControl is Button)
{
panelControl.Enabled = true;
}
}
}
}
}
}
Note that this is just one possible solution to get you going, but certainly not the best. Normally the controls would be created at runtime, added to a collection of some sort (usually an array called GameBoard or something like that), and placed on the form all by code. Their click events would all be hooked up to the same method, which would do the right thing based on either control name or some data in the Tag field.
They are grouped together in the Controls array of the form. Here is a simple example of how you can cycle through them:
private void doReset()
{
foreach (Control c in this.Controls)
{
if (c.GetType() == typeof(Button))
{
c.Enabled = true;
}
else if (c.GetType() == typeof(Label))
{
c.Visible = false;
}
}
}
You can do some more intelligent logic in there for the specific controls if you like. Something like adding a tag to each button if it gets picked up by the method or, naming each button in a certain way if it is to get picked up by the method.
Let's try following idea:
List<Button> buttons = new List<Button>();
List<Label> labels = new List<Label>();
buttons.Add(xButton1)
buttons.Add(oButton1)
buttons.Add(xButton2)
buttons.Add(oButton2)
...
labels.Add(xLabel1)
labels.Add(oLabel1)
labels.Add(xLabel2)
labels.Add(oLabel2)
...
foreach(var ctr in buttons)
{
ctr.Enabled = true;
}
foreach(var ctr in labels)
{
ctr.Enabled = true;
}
This is how I always do it. If you give each button a different text value inside the click event you can do different things depending on the text value.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Tick_Tack_Toe
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
MyButton.buttons = new List<MyButton>() {
new MyButton() { button = xButton1, label = xLabel1},
new MyButton() { button = xButton2, label = xLabel2},
new MyButton() { button = xButton3, label = xLabel3},
new MyButton() { button = oButton1, label = oLabel1},
new MyButton() { button = oButton2, label = oLabel2},
new MyButton() { button = oButton3, label = oLabel3}
};
foreach (MyButton button in MyButton.buttons)
{
button.button.Click += new EventHandler(Button_Click);
}
}
private void Button_Click(object sender, EventArgs e)
{
MyButton button = sender as MyButton;
button.label.Visible = true;
button.button.Enabled = false;
switch (button.button.Text)
{
case "X":
//enter your code here
break;
case "Y":
//enter your code here
break;
}
}
private void resetToolStripMenuItem_Click(object sender,
EventArgs e)
{
foreach (MyButton button in MyButton.buttons)
{
button.button.Enabled = true;
button.label.Visible = false;
}
}
}
public class MyButton
{
public static List<MyButton> buttons { get; set; }
public Button button { get; set; }
public Label label { get; set; }
}
}
I'm working on a utility that helps me construct long, repetitive HTML tables. I have data imported from an XML file, a listBox that shows the names, buttons that insert text into a richTextBox that contains certain data from the XML, but there's info I have to enter manually for each name.
I have a treeView that lists committees ( parent ) and subcommittees ( 1 level of children ). I can check the appropriate boxes, click a button, and all the corresponding HTML is created. However, each committee ( and sub ) has a chairperson and a ranking member. Instead of adding those two extra nodes under each parent and child - I have 71 parents and children. I'd rather not add 142 more nodes - I'm hoping there's a way to have "four-click" checkboxes...? 1st click = checkmark; 2nd = green; 3rd = red; 4th = cleared. Or similar. That way I could "check" for being a member, "double-check" for Chair, "triple-check" for Ranking, and a fourth would just start over.
I'm open to suggestions for another approach, as well. This is the last bit I need to get working to save me typing 2-3K lines of HTML by hand, so I don't care how I get it done. Thanks.
Okay, sorry it's been a while. You know, life and such. Anyway, I ended up my own TreeNode and TreeView classes. This is my first adventure with WinForms, so a lot of this seemed quite finicky. Ended up be much simpler than I had imagined. If anybody wants to use this, knock yourself out.
The Check*.bmp images are just a checked box that I colored in. I'll probably make my own four images for this and neaten up the populateStateImageList() method. Also, I would think this would work for as many states as you need, up to the 14 image limit of StateImageList.
using System.Drawing;
using System.Windows.Forms;
namespace jkHTMLBuilder
{
public class QuadCheckTreeNode : TreeNode
{
public enum CheckState : int { UnChecked, Checked, Chair, Rank } // The four possible states
private CheckState _cs = CheckState.UnChecked; // The node's current state
public CheckState checkState
{
get
{
return _cs;
}
set
{
_cs = value;
}
}
public QuadCheckTreeNode(string initString) : base()
{
this.Text = initString;
this.checkState = CheckState.UnChecked;
this.Checked = false;
this.StateImageIndex = 0;
}
public void checkAdvance() // This is called from onAfterCheck to set the next consecutive state
{
switch (checkState)
{
case CheckState.UnChecked:
checkState = CheckState.Checked;
break;
case CheckState.Checked:
checkState = CheckState.Chair;
break;
case CheckState.Chair:
checkState = CheckState.Rank;
break;
case CheckState.Rank:
checkState = CheckState.UnChecked;
break;
}
this.Checked = (this.checkState == CheckState.UnChecked ? false : true);
}
}
class QuadCheckTreeView : TreeView
{
private bool clickStop = false;
private bool shouldAdvance = false;
public QuadCheckTreeView() : base()
{
StateImageList = new ImageList();
}
public void populateStateImageList() // I made this a separate method and call it from my Load_Senators method so the images are
{ // set up when the XML file is loaded. Apparently, loading the files ( Check*.bmmp )
for (int i = 0; i < 2; i++) // from the ctor causes problems...? Whatever. This works.
{
Bitmap bmp = new Bitmap(16, 16);
Graphics chkGraphics = Graphics.FromImage(bmp);
switch (i)
{
case 0:
CheckBoxRenderer.DrawCheckBox(chkGraphics, new Point(0, 1), System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal);
break;
case 1:
CheckBoxRenderer.DrawCheckBox(chkGraphics, new Point(0, 1), System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal);
break;
}
StateImageList.Images.Add(bmp);
}
Bitmap myBitmap = new Bitmap("..\\..\\CheckBlue.bmp");
StateImageList.Images.Add(myBitmap);
myBitmap = new Bitmap("..\\..\\CheckRed.bmp");
StateImageList.Images.Add(myBitmap);
}
public void ClearNodes(TreeNodeCollection nodes) // This is for when I move on to the next record. Unchecks everything.
{
clickStop = true;
foreach (QuadCheckTreeNode qctn in nodes)
{
qctn.Checked = false;
qctn.checkState = QuadCheckTreeNode.CheckState.UnChecked;
if (qctn.Nodes.Count > 0)
{
foreach (QuadCheckTreeNode cqctn in qctn.Nodes)
{
cqctn.Checked = false;
cqctn.checkState = QuadCheckTreeNode.CheckState.UnChecked;
}
}
}
clickStop = false;
}
protected override void OnCreateControl()
{
base.OnCreateControl();
CheckBoxes = false; // Checkboxes off to use my images
ClearNodes(this.Nodes); // Probably not needed.
}
protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e)
{
base.OnNodeMouseClick(e);
TreeViewHitTestInfo tvhtInfo = HitTest(e.X, e.Y); // If you didn't click on it, ignore.
if (tvhtInfo == null || tvhtInfo.Location !=
TreeViewHitTestLocations.StateImage)
{
return;
}
QuadCheckTreeNode qctn = (QuadCheckTreeNode)e.Node; // If you right-clicked, set to UnChecked
if (e.Button == MouseButtons.Right)
{
if (qctn.checkState != QuadCheckTreeNode.CheckState.UnChecked)
{
qctn.checkState = QuadCheckTreeNode.CheckState.UnChecked;
qctn.Checked = false;
shouldAdvance = false;
}
}
else
shouldAdvance = true; // Left click sets this var==true so the node's Advance() method is called
qctn.Checked = qctn.Checked; // This fires the onAfterCheck event
}
protected override void OnAfterCheck(TreeViewEventArgs e)
{
base.OnAfterCheck(e);
if (clickStop) // This keeps the event from running if it's called inappropriately
{
return;
}
clickStop = true;
QuadCheckTreeNode qctn = (QuadCheckTreeNode)e.Node;
if (shouldAdvance)
{
qctn.checkAdvance();
shouldAdvance = false;
qctn.StateImageIndex = (int)qctn.checkState;
}
checkParent(qctn); // Calling this method, if it actually checks a parent node, won't call onAfterCheck because of clickStop
clickStop = false;
}
protected void checkParent(QuadCheckTreeNode qctn)
{
QuadCheckTreeNode pqctn = (QuadCheckTreeNode)qctn.Parent;
if (pqctn == null)
return;
if (pqctn.checkState == QuadCheckTreeNode.CheckState.UnChecked) // This checks a parent if it has a checked child
{
bool chkParent = false;
foreach (QuadCheckTreeNode n in pqctn.Nodes)
{
if (n.checkState != QuadCheckTreeNode.CheckState.UnChecked) // Checks all the children. If even one is checked, the parent gets checked
chkParent = true;
}
if (chkParent)
{
pqctn.Checked = true;
pqctn.checkState = QuadCheckTreeNode.CheckState.Checked;
}
}
}
}
}
I have one requirement to add three check box in a group box, and check-box must react as radio button i.e.,
1. one can check only one check-box at a time.
2. when user goes to check another check box, previous one automatically should be unchecked.
I also know that this is not a good approach to do this, and it's really confusing to non-technical or technical user but I also want to learn something new in this.
I went through lots of sites, but I didn't get any satisfactory solution. I hope I will get good one from you.
Moreover, There are simple three check box to indicate three things for selection respectively and one text box. If one checks anyone things then texbox shows the selected thing.
For example:- Check-box show C#,ASP.NET and MVC respectively.
If one select C# then text-box must show C#.
Thanks...
With Thanks and Regards,
Ravindra.
You can use radio button for this functionality.
I'd do it like this, assuming you use winforms:
public partial class Form1 : Form
{
private bool ignoreEvents = false;
public Form1()
{
InitializeComponent();
//set this handler to the events of all 3 checkboxes
checkBox1.CheckedChanged += radioCheckboxes_CheckedChanged;
checkBox2.CheckedChanged += radioCheckboxes_CheckedChanged;
checkBox3.CheckedChanged += radioCheckboxes_CheckedChanged;
}
private void radioCheckboxes_CheckedChanged(object sender, EventArgs e)
{
if (!ignoreEvents)
{
ignoreEvents = true; // otherwise the other checkboxes would react when i set the state programmatically
CheckBox current = sender as CheckBox;
if (current == checkBox1)
{
checkBox2.Checked = false;
checkBox3.Checked = false;
}
else if (current == checkBox2)
{
checkBox1.Checked = false;
checkBox3.Checked = false;
}
else
{
checkBox1.Checked = false;
checkBox2.Checked = false;
}
ignoreEvents = false;
}
}
}
With WPF I'd just use a style for a radiobutton so that it looks like a checkbox...
you can simply use another task like timer that updates every 50ms ,
so as to disable the check boxes you want to disable there ,
in your "checked changed event" only flag the controls ,
like for checkbox1 = flagdcheckbx = 1 ; and so on.
private async Task writeTimer()
{
if(ignoreEvents == true)
{
ignoreEvents = false;
switch(flagdcheckbx)
{
case 1:
checkBox2.Checked = false;
checkBox3.Checked = false;
break;
case 2:
checkBox1.Checked = false;
checkBox3.Checked = false;
break;
case 3:
checkBox1.Checked = false;
checkBox2.Checked = false;
break;
}
}
}
private void radioCheckboxes_CheckedChanged(object sender, EventArgs e)
{
CheckBox current = sender as CheckBox;
if (current == checkBox1)
{
if (current.checked)
{
ignoreEvents = true;
flagdcheckbx=1;
}
}
else if (current == checkBox2)
{
if (current.checked) // it should happen only when checked
{
ignoreEvents = true;
flagdcheckbx=2;
}
}
else
{
if (current.checked)
{
ignoreEvents = true;
flagdcheckbx=3;
}
}
}
I assume you know how to group RadioButton controls inside a container and you just look for the CheckBox appearance for your radio button groups.
As an option you can modify the RadioButtonList that I've shared in [this post] and add the CheckBox appearance to it. In the following picture you can see two RadioButtonList control, one with CheckBox appearance and the other with RadioButton appearance:
radioButtonList1.DataSource = Enum.GetValues(typeof(DayOfWeek));
radioButtonList1.Appearance = RadioButtonListAppearance.CheckBox;
radioButtonList2.DataSource = Enum.GetValues(typeof(DayOfWeek));
Here is the modified version of code to add Appearance property:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
public class RadioButtonList : ListBox
{
RadioButtonListAppearance appearance = RadioButtonListAppearance.RadioButton;
public RadioButtonListAppearance Appearance
{
get { return appearance; }
set { appearance = value; Invalidate(); }
}
Size s;
public RadioButtonList()
{
this.DrawMode = DrawMode.OwnerDrawFixed;
using (var g = Graphics.FromHwnd(IntPtr.Zero))
s = RadioButtonRenderer.GetGlyphSize(
Graphics.FromHwnd(IntPtr.Zero), RadioButtonState.CheckedNormal);
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
var text = (Items.Count > 0) ? GetItemText(Items[e.Index]) : Name;
Rectangle r = e.Bounds; Point p;
var flags = TextFormatFlags.Default | TextFormatFlags.NoPrefix;
var selected = (e.State & DrawItemState.Selected) == DrawItemState.Selected;
var state = selected ?
(Enabled ? RadioButtonState.CheckedNormal :
RadioButtonState.CheckedDisabled) :
(Enabled ? RadioButtonState.UncheckedNormal :
RadioButtonState.UncheckedDisabled);
if (RightToLeft == System.Windows.Forms.RightToLeft.Yes)
{
p = new Point(r.Right - r.Height + (ItemHeight - s.Width) / 2,
r.Top + (ItemHeight - s.Height) / 2);
r = new Rectangle(r.Left, r.Top, r.Width - r.Height, r.Height);
flags |= TextFormatFlags.RightToLeft | TextFormatFlags.Right;
}
else
{
p = new Point(r.Left + (ItemHeight - s.Width) / 2,
r.Top + (ItemHeight - s.Height) / 2);
r = new Rectangle(r.Left + r.Height, r.Top, r.Width - r.Height, r.Height);
}
var bc = selected ? (Enabled ? SystemColors.Highlight :
SystemColors.InactiveBorder) : BackColor;
var fc = selected ? (Enabled ? SystemColors.HighlightText :
SystemColors.GrayText) : ForeColor;
using (var b = new SolidBrush(bc))
e.Graphics.FillRectangle(b, e.Bounds);
if (appearance == RadioButtonListAppearance.RadioButton)
{
RadioButtonRenderer.DrawRadioButton(e.Graphics, p, state);
}
else if (appearance == RadioButtonListAppearance.CheckBox)
{
CheckBoxRenderer.DrawCheckBox(e.Graphics, p, (CheckBoxState)state);
}
else
{
throw new InvalidOperationException("Appearance is invalid.");
}
TextRenderer.DrawText(e.Graphics, text, Font, r, fc, bc, flags);
e.DrawFocusRectangle();
base.OnDrawItem(e);
}
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override SelectionMode SelectionMode
{
get { return System.Windows.Forms.SelectionMode.One; }
set { }
}
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override int ItemHeight
{
get { return (this.Font.Height + 2); }
set { }
}
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override DrawMode DrawMode
{
get { return base.DrawMode; }
set { base.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; }
}
}
public enum RadioButtonListAppearance
{
RadioButton,
CheckBox
}
Assuming you have a few CheckBox controls in a container like a GroupBox, In the load event of your form:
Find all CheckBox controls of the container
Set AutoCheck property of the CheckBox controls to false
Handle Click event of the CheckBox controls and in the event handler, set Checked property of clicked element to true and for rest, set it to false
Here is the code:
private void Form1_Load(object sender, EventArgs e)
{
var all = groupBox1.Controls.OfType<CheckBox>().ToList();
foreach (CheckBox c in all)
{
c.AutoCheck = false;
c.Click += (obj, args) =>
{
var ThisCheckBox = (CheckBox)obj;
foreach( CheckBox cc in all.Except(new[] { ThisCheckBox }))
cc.Checked = false;
ThisCheckBox.Checked = true;
};
}
}
I am trying to make my custom ComboBox inheriting from ContainerControl. I used this article as a base but rewrote it, but I use a ToolStripControlHost, my own custom ListBox & a ToolStripDropDown.
Now the ComboBox is a button where you click on to show the DropDowncontaining my ListBox, works fine with overriding OnMouseClick.
The problems starts when I try to close the DropDown, with the DropDown's 'AutoClose' property to true, the DropDown closes if you click somewhere outside the DropDown (including the button) ...
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
/* listboxControl = ToolStripDropDown */
if (!listboxControl.Visible)
{
listboxControl.Show(this, GetDropLocation(), ToolStripDropDownDirection.BelowRight);
//listbox.Capture = true;
}
}
This is the code for the click on the button .. so what happens if you click it ?
If the DropDown is shown, it first closes the DropDown, then it fires the OnMouseClick event. Meaning: listboxControl.Visible is already false & it will show the DropDown again. All of this causing a quick close-open.
I have been stuck with this problem for some time now and google doesn't seem to know a lot about this subject (that article on CodeProject has the same bug).
What I have tried is disabling AutoClose and capturing the mouse after I show the DropDown, this works partially but it affects the working of my hosted ListBox. The ListBox contains a set of controls (the items), these items have a hover paint effect. Capturing the mouse in the ListBox control prevents the OnMouseEnter to be fired.
All input would be greatly appreciated !
You need a variable to track the cursor position when the DropDown is closing.
Here is a quick and dirty example control:
public class CustomDropBox : Control {
private ListBox box = new ListBox() { IntegralHeight = false };
private ToolStripControlHost host;
private ToolStripDropDown drop;
private bool wasShowing = false;
public CustomDropBox() {
box.MinimumSize = new Size(120, 120);
box.MouseUp += box_MouseUp;
box.KeyPress += box_KeyPress;
box.Items.AddRange(new string[] { "aaa", "bbb", "ccc" });
host = new ToolStripControlHost(box) { Padding = Padding.Empty };
drop = new ToolStripDropDown() { Padding = Padding.Empty };
drop.Closing += drop_Closing;
drop.Items.Add(host);
}
private Rectangle GetDownRectangle() {
return new Rectangle(this.ClientSize.Width - 16, 0, 16, this.ClientSize.Height);
}
void drop_Closing(object sender, ToolStripDropDownClosingEventArgs e) {
if (e.CloseReason == ToolStripDropDownCloseReason.AppClicked) {
wasShowing = GetDownRectangle().Contains(this.PointToClient(Cursor.Position));
} else {
wasShowing = false;
}
}
void box_KeyPress(object sender, KeyPressEventArgs e) {
if (e.KeyChar == (char)Keys.Enter && box.SelectedIndex > -1) {
drop.Close();
}
}
void box_MouseUp(object sender, MouseEventArgs e) {
int index = box.IndexFromPoint(e.Location);
if (index > -1) {
drop.Close();
}
}
protected override void OnMouseDown(MouseEventArgs e) {
if (e.Button == MouseButtons.Left && GetDownRectangle().Contains(e.Location)) {
if (wasShowing) {
wasShowing = false;
} else {
drop.Show(this, new Point(0, this.Height));
}
}
base.OnMouseDown(e);
}
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.Clear(Color.White);
ControlPaint.DrawComboButton(e.Graphics, GetDownRectangle(), ButtonState.Normal);
base.OnPaint(e);
}
}
This question already has answers here:
Hiding and Showing TabPages in tabControl
(21 answers)
Closed 7 years ago.
How to hide TabPage from TabControl in WinForms 2.0?
No, this doesn't exist. You have to remove the tab and re-add it when you want it. Or use a different (3rd-party) tab control.
Code Snippet for Hiding a TabPage
private void HideTab1_Click(object sender, EventArgs e)
{
tabControl1.TabPages.Remove(tabPage1);
}
Code Snippet for Showing a TabPage
private void ShowTab1_Click(object sender, EventArgs e)
{
tabControl1.TabPages.Add(tabPage1);
}
I realize the question is old, and the accepted answer is old, but ...
At least in .NET 4.0 ...
To hide a tab:
tabControl.TabPages.Remove(tabPage);
To put it back:
tabControl.TabPages.Insert(index, tabPage);
TabPages works so much better than Controls for this.
Visiblity property has not been implemented on the Tabpages, and there is no Insert method also.
You need to manually insert and remove tab pages.
Here is a work around for the same.
http://www.dotnetspider.com/resources/18344-Hiding-Showing-Tabpages-Tabcontrol.aspx
Variant 1
In order to avoid visual klikering you might need to use:
bindingSource.RaiseListChangeEvent = false
or
myTabControl.RaiseSelectedIndexChanged = false
Remove a tab page:
myTabControl.Remove(myTabPage);
Add a tab page:
myTabControl.Add(myTabPage);
Insert a tab page at specific location:
myTabControl.Insert(2, myTabPage);
Do not forget to revers the changes:
bindingSource.RaiseListChangeEvent = true;
or
myTabControl.RaiseSelectedIndexChanged = true;
Variant 2
myTabPage.parent = null;
myTabPage.parent = myTabControl;
Solutions provided so far are way too complicated.
Read the easiest solution at:
http://www.codeproject.com/Questions/614157/How-to-Hide-TabControl-Headers
You could use this method to make them invisible at run time:
private void HideAllTabsOnTabControl(TabControl theTabControl)
{
theTabControl.Appearance = TabAppearance.FlatButtons;
theTabControl.ItemSize = new Size(0, 1);
theTabControl.SizeMode = TabSizeMode.Fixed;
}
I combined the answer from #Jack Griffin and the one from #amazedsaint (the dotnetspider code snippet respectively) into a single TabControlHelper.
The TabControlHelper lets you:
Show / Hide all tab pages
Show / Hide single tab pages
Keep the original position of the tab pages
Swap tab pages
public class TabControlHelper
{
private TabControl _tabControl;
private List<KeyValuePair<TabPage, int>> _pagesIndexed;
public TabControlHelper(TabControl tabControl)
{
_tabControl = tabControl;
_pagesIndexed = new List<KeyValuePair<TabPage, int>>();
for (int i = 0; i < tabControl.TabPages.Count; i++)
{
_pagesIndexed.Add(new KeyValuePair<TabPage, int> (tabControl.TabPages[i], i ));
}
}
public void HideAllPages()
{
for (int i = 0; i < _pagesIndexed.Count; i++)
{
_tabControl.TabPages.Remove(_pagesIndexed[i].Key);
}
}
public void ShowAllPages()
{
for (int i = 0; i < _pagesIndexed.Count; i++)
{
_tabControl.TabPages.Add(_pagesIndexed[i].Key);
}
}
public void HidePage(TabPage tabpage)
{
if (!_tabControl.TabPages.Contains(tabpage)) return;
_tabControl.TabPages.Remove(tabpage);
}
public void ShowPage(TabPage tabpage)
{
if (_tabControl.TabPages.Contains(tabpage)) return;
InsertTabPage(GetTabPage(tabpage).Key, GetTabPage(tabpage).Value);
}
private void InsertTabPage(TabPage tabpage, int index)
{
if (index < 0 || index > _tabControl.TabCount)
throw new ArgumentException("Index out of Range.");
_tabControl.TabPages.Add(tabpage);
if (index < _tabControl.TabCount - 1)
do
{
SwapTabPages(tabpage, (_tabControl.TabPages[_tabControl.TabPages.IndexOf(tabpage) - 1]));
}
while (_tabControl.TabPages.IndexOf(tabpage) != index);
_tabControl.SelectedTab = tabpage;
}
private void SwapTabPages(TabPage tabpage1, TabPage tabpage2)
{
if (_tabControl.TabPages.Contains(tabpage1) == false || _tabControl.TabPages.Contains(tabpage2) == false)
throw new ArgumentException("TabPages must be in the TabControls TabPageCollection.");
int Index1 = _tabControl.TabPages.IndexOf(tabpage1);
int Index2 = _tabControl.TabPages.IndexOf(tabpage2);
_tabControl.TabPages[Index1] = tabpage2;
_tabControl.TabPages[Index2] = tabpage1;
}
private KeyValuePair<TabPage, int> GetTabPage(TabPage tabpage)
{
return _pagesIndexed.Where(p => p.Key == tabpage).First();
}
}
Example on how to use it:
TabControl myTabControl = new TabControl();
TabControlHelper myHelper = new TabControlHelper(myTabControl);
myHelper.HideAllPages();
myHelper.ShowAllPages();
private System.Windows.Forms.TabControl _tabControl;
private System.Windows.Forms.TabPage _tabPage1;
private System.Windows.Forms.TabPage _tabPage2;
...
// Initialise the controls
...
// "hides" tab page 2
_tabControl.TabPages.Remove(_tabPage2);
// "shows" tab page 2
// if the tab control does not contain tabpage2
if (! _tabControl.TabPages.Contains(_tabPage2))
{
_tabControl.TabPages.Add(_tabPage2);
}
Create a new empty class and past this inside it:
using System.Windows.Forms;
namespace ExtensionMethods
{
public static class TabPageExtensions
{
public static bool IsVisible(this TabPage tabPage)
{
if (tabPage.Parent == null)
return false;
else if (tabPage.Parent.Contains(tabPage))
return true;
else
return false;
}
public static void HidePage(this TabPage tabPage)
{
TabControl parent = (TabControl)tabPage.Parent;
parent.TabPages.Remove(tabPage);
}
public static void ShowPageInTabControl(this TabPage tabPage,TabControl parent)
{
parent.TabPages.Add(tabPage);
}
}
}
2- Add reference to ExtensionMethods namespace in your form code:
using ExtensionMethods;
3- Now you can use yourTabPage.IsVisible(); to check its visibility, yourTabPage.HidePage(); to hide it, and yourTabPage.ShowPageInTabControl(parentTabControl); to show it.
you can set the parent of the tabpage to null for hiding
and to show just set tabpage parent to the tabcontrol
public static Action<Func<TabPage, bool>> GetTabHider(this TabControl container) {
if (container == null) throw new ArgumentNullException("container");
var orderedCache = new List<TabPage>();
var orderedEnumerator = container.TabPages.GetEnumerator();
while (orderedEnumerator.MoveNext()) {
var current = orderedEnumerator.Current as TabPage;
if (current != null) {
orderedCache.Add(current);
}
}
return (Func<TabPage, bool> where) => {
if (where == null) throw new ArgumentNullException("where");
container.TabPages.Clear();
foreach (TabPage page in orderedCache) {
if (where(page)) {
container.TabPages.Add(page);
}
}
};
}
Use it like this:
var hider = this.TabContainer1.GetTabHider();
hider((tab) => tab.Text != "tabPage1");
hider((tab) => tab.Text != "tabpage2");
The original ordering of the tabs is kept in a List that is completely hidden inside the anonymous function. Keep a reference to the function instance and you retain your original tab order.
Well, if you don't want to mess up existing code and just want to hide a tab, you could modify the compiler generated code to comment the line which adds the tab to the tabcontrol.
For example:
The following line adds a tab named "readformatcardpage" to a Tabcontrol named "tabcontrol"
this.tabcontrol.Controls.Add(this.readformatcardpage);
The following will prevent addition of the tab to the tabcontrol
//this.tabcontrol.Controls.Add(this.readformatcardpage);
+1 for microsoft :-) .
I managed to do it this way:
(it assumes you have a Next button that displays the next TabPage - tabSteps is the name of the Tab control)
At start up, save all the tabpages in a proper list.
When user presses Next button, remove all the TabPages in the tab control, then add that with the proper index:
int step = -1;
List<TabPage> savedTabPages;
private void FMain_Load(object sender, EventArgs e) {
// save all tabpages in the list
savedTabPages = new List<TabPage>();
foreach (TabPage tp in tabSteps.TabPages) {
savedTabPages.Add(tp);
}
SelectNextStep();
}
private void SelectNextStep() {
step++;
// remove all tabs
for (int i = tabSteps.TabPages.Count - 1; i >= 0 ; i--) {
tabSteps.TabPages.Remove(tabSteps.TabPages[i]);
}
// add required tab
tabSteps.TabPages.Add(savedTabPages[step]);
}
private void btnNext_Click(object sender, EventArgs e) {
SelectNextStep();
}
Update
public class TabControlHelper {
private TabControl tc;
private List<TabPage> pages;
public TabControlHelper(TabControl tabControl) {
tc = tabControl;
pages = new List<TabPage>();
foreach (TabPage p in tc.TabPages) {
pages.Add(p);
}
}
public void HideAllPages() {
foreach(TabPage p in pages) {
tc.TabPages.Remove(p);
}
}
public void ShowAllPages() {
foreach (TabPage p in pages) {
tc.TabPages.Add(p);
}
}
public void HidePage(TabPage tp) {
tc.TabPages.Remove(tp);
}
public void ShowPage(TabPage tp) {
tc.TabPages.Add(tp);
}
}
TabPage pageListe, pageDetay;
bool isDetay = false;
private void btnListeDetay_Click(object sender, EventArgs e)
{
if (isDetay)
{
isDetay = false;
tc.TabPages.Remove(tpKayit);
tc.TabPages.Insert(0,pageListe);
}
else
{
tc.TabPages.Remove(tpListe);
tc.TabPages.Insert(0,pageDetay);
isDetay = true;
}
}
As a cheap work around, I've used a label to cover up the tabs I wanted to hide.
We can then use the visible prop of the label as a substitute. If anyone does go this route, don't forget to handle keyboard strokes or visibility events. You wouldn't want the left right cursor keys exposing the tab you're trying to hide.
Not sure about "Winforms 2.0" but this is tried and proven:
http://www.mostthingsweb.com/2011/01/hiding-tab-headers-on-a-tabcontrol-in-c/
In WPF, it's pretty easy:
Assuming you've given the TabItem a name, e.g.,
<TabItem Header="Admin" Name="adminTab" Visibility="Hidden">
<!-- tab content -->
</TabItem>
You could have the following in the code behind the form:
if (user.AccessLevel == AccessLevelEnum.Admin)
{
adminTab.Visibility = System.Windows.Visibility.Visible;
}
It should be noted that a User object named user has been created with it's AccessLevel property set to one of the user-defined enum values of AccessLevelEnum... whatever; it's just a condition by which I decide to show the tab or not.
I also had this question. tabPage.Visible is not implemented as stated earlier, which was a great help (+1). I found you can override the control and this will work. A bit of necroposting, but I thought to post my solution here for others...
[System.ComponentModel.DesignerCategory("Code")]
public class MyTabPage : TabPage
{
private TabControl _parent;
private bool _isVisible;
private int _index;
public new bool Visible
{
get { return _isVisible; }
set
{
if (_parent == null) _parent = this.Parent as TabControl;
if (_parent == null) return;
if (_index < 0) _index = _parent.TabPages.IndexOf(this);
if (value && !_parent.TabPages.Contains(this))
{
if (_index > 0) _parent.TabPages.Insert(_index, this);
else _parent.TabPages.Add(this);
}
else if (!value && _parent.TabPages.Contains(this)) _parent.TabPages.Remove(this);
_isVisible = value;
base.Visible = value;
}
}
protected override void InitLayout()
{
base.InitLayout();
_parent = Parent as TabControl;
}
}
I've used the same approach but the problem is that when tab page was removed from the tab control TabPages list, it is removed from the tab page Controls list also. And it is not disposed when form is disposed.
So if you have a lot of such "hidden" tab pages, you can get windows handle quota exceeded error and only application restart will fix it.
If you are talking about AjaxTabControlExtender then set TabIndex of every tabs and set Visible property True/False according to your need.
myTab.Tabs[1].Visible=true/false;
// inVisible
TabPage page2 = tabControl1.TabPages[0];
page2.Visible= false;
//Visible
page2.Visible= true;
// disable
TabPage page2 = tabControl1.TabPages[0];
page2.Enabled = false;
// enable
page2.Enabled = true;
//Hide
tabCtrlTagInfo.TabPages(0).Hide()
tabCtrlTagInfo.TabPages(0).Show()
Just copy paste and try it,the above code has been tested in vs2010, it works.
Hide TabPage and Remove the Header:
this.tabPage1.Hide();
this.tabPage3.Hide();
this.tabPage5.Hide();
tabControl1.TabPages.Remove(tabPage1);
tabControl1.TabPages.Remove(tabPage3);
tabControl1.TabPages.Remove(tabPage5);
Show TabPage and Visible the Header:
tabControl1.TabPages.Insert(0,tabPage1);
tabControl1.TabPages.Insert(2, tabPage3);
tabControl1.TabPages.Insert(4, tabPage5);
this.tabPage1.Show();
this.tabPage3.Show();
this.tabPage5.Show();
tabControl1.SelectedTab = tabPage1;