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;
Related
So, I'm trying to add an authorization administration to an existing WPF aplication. The aim is to allow administrator handpick UI elements (textboxes, buttons..) and assign who can use them.
All forms inherit from MetroBaseWindow, and there is too many forms to add events to each control or form, so I need to do this 'Globaly'.
The problem for me is registering the click on UIElement and retrieving it.
So far I've registered to an event..
public class MetroBaseWindow : Window
{
// ...
public MetroBaseWindow()
{
...
this.PreviewMouseDown += RegisterControlOnClick;
}
}
Then I retrieved the position.
private void RegisterControlOnClick(object sender, MouseButtonEventArgs e)
{
Point pt = e.GetPosition((UIElement)sender);
hitResultsList.Clear();
VisualTreeHelper.HitTest(this, null,
new HitTestResultCallback(MyHitTestResult),
new PointHitTestParameters(pt));
if (hitResultsList.Count > 0)
{
Console.WriteLine("Number of Visuals Hit: " + hitResultsList.Count);
}
}
public HitTestResultBehavior MyHitTestResult(HitTestResult result)
{
hitResultsList.Add(result.VisualHit);
return HitTestResultBehavior.Continue;
}
And tried to use VisualTree to retrieve those elements. But when clicking a combobox, all that is returned are border elements, for textbox a textview, etc...
How do I get the logical elements directly under the cursor?
Ok i Figured it out...still needs some tweaks but here goes the code:
List<DependencyObject> hitResultsList = new List<DependencyObject>();
private void RegisterControlOnClick(object sender, MouseButtonEventArgs e)
{
Point pt = e.GetPosition((UIElement)sender);
VisualTreeHelper.HitTest(this, new HitTestFilterCallback(MyHitTestFilter), new HitTestResultCallback(MyHitTestResult), new PointHitTestParameters(pt));
}
public HitTestResultBehavior MyHitTestResult(HitTestResult result)
{
return HitTestResultBehavior.Continue;
}
public HitTestFilterBehavior MyHitTestFilter(DependencyObject o)
{
if (o.GetType().IsSubclassOf(typeof(ContentControl)))
{
hitResultsList.Add(o);
((Control)o).Background = Brushes.Red;
}
return HitTestFilterBehavior.Continue;
}
Created a custom intellisense textbox (textbox with listbox a child).
As shown in below image, the listbox pops up when i enter a char which all works fine and good but when i am at the end of textbox the listbox is partially visible, is there anyway i can show the whole listbox content?
Tried this "Show control inside user control outside the boundaries of its parent
But when the popup window opens the text box looses focus and i cannot type anything further, my intellisense textbox keeps giving better results based on what they type but in this situation i am not able to type anymore.
FYI tried to add pParentControl.Focus() into show method defined in other article as shown below, missing something?
public void Show(Control pParentControl)
{
if (pParentControl == null) return;
// position the popup window
var loc = pParentControl.PointToScreen(new Point(0, pParentControl.Height));
pParentControl.Focus();
m_tsdd.Show(loc);
}
Here is the complete code
class TextBox_AutoComplete : TextBox
{
#region Class Members
List<string> dictionary;
ListBox listbox = new ListBox();
#endregion
private PopupHelper m_popup;
#region Extern functions
[DllImport("user32")]
private extern static int GetCaretPos(out Point p);
#endregion
#region Constructors
public TextBox_AutoComplete() : base()
{
this.Margin = new Padding(0, 0, 0, 0);
this.Multiline = true;
this.Dock = DockStyle.Fill;
this.KeyDown += Textbox_KeyDown;
this.KeyUp += Textbox_KeyUp;
listbox.Parent = this;
listbox.KeyUp += List_OnKeyUp;
listbox.Visible = false;
this.dictionary = new List<string>();
}
#endregion
#region Properties
public List<string> Dictionary
{
get { return this.dictionary; }
set { this.dictionary = value; }
}
#endregion
#region Methods
private static string GetLastString(string s)
{
Regex rgx = new Regex("[^a-zA-Z0-9_.\\[\\]]");
s = rgx.Replace(s, " ");
string[] strArray = s.Split(' ');
return strArray[strArray.Length - 1];
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
Point cp;
GetCaretPos(out cp);
List<string> lstTemp = new List<string>();
List<string> TempFilteredList = new List<string>();
string LastString = GetLastString(this.Text.Substring(0, SelectionStart));
//MessageBox.Show(LastString);
/*seperated them so that column name matches are found first*/
TempFilteredList.AddRange(dictionary.Where(n => n.Replace("[", "").ToUpper().Substring(n.IndexOf(".") > 0 ? n.IndexOf(".") : 0).StartsWith(LastString.ToUpper())
).Select(r => r)
.ToList());
TempFilteredList.AddRange(dictionary.Where(n => n.Replace("[", "").ToUpper().StartsWith(LastString.ToUpper())
|| n.ToUpper().StartsWith(LastString.ToUpper()))
.Select(r => r)
.ToList());
lstTemp = TempFilteredList.Distinct().Select(r => r).ToList();
/*Getting max width*/
int maxWidth = 0, temp = 0;
foreach (var obj in lstTemp)
{
temp = TextRenderer.MeasureText(obj.ToString(), new Font("Arial", 10, FontStyle.Regular)).Width;
if (temp > maxWidth)
{
maxWidth = temp;
}
}
listbox.SetBounds(cp.X + 20, cp.Y + 20, maxWidth, 60);
if (lstTemp.Count != 0 && LastString != "")
{
listbox.DataSource = lstTemp;
// listbox.Show();
if (m_popup == null)
m_popup = new PopupHelper(listbox);
m_popup.Show(this);
}
else if (m_popup != null)
{
//listbox.Hide();
m_popup.Hide();
}
}
protected void Textbox_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Down)
{
if (listbox.Visible == true)
{
listbox.Focus();
}
e.Handled = true;
}
else if (e.KeyCode == Keys.Escape)
{
listbox.Visible = false;
e.Handled = true;
}
}
protected void Textbox_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space && listbox.Visible == true)
{
listbox.Focus();
List_OnKeyUp(listbox, new KeyEventArgs(Keys.Space));
e.Handled = true;
}
if (e.KeyCode == Keys.Down && listbox.Visible == true)
{
listbox.Focus();
e.Handled = true;
}
}
private void List_OnKeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space || e.KeyCode == Keys.Enter)
{
int Selection_Start = this.SelectionStart;
string StrLS = GetLastString(this.Text.Substring(0, Selection_Start));
this.Select(Selection_Start - StrLS.Length, StrLS.Length);
// MessageBox.Show(this.Selection_Start.ToString() + " Last string" + StrLS);
this.SelectedText=((ListBox)sender).SelectedItem.ToString();
listbox.Hide();
this.Focus();
}
}
#endregion
}
public sealed class PopupHelper : IDisposable
{
private readonly Control m_control;
private readonly ToolStripDropDown m_tsdd;
private readonly Panel m_hostPanel; // workarround - some controls don't display correctly if they are hosted directly in ToolStripControlHost
public PopupHelper(Control pControl)
{
m_hostPanel = new Panel();
m_hostPanel.Padding = Padding.Empty;
m_hostPanel.Margin = Padding.Empty;
m_hostPanel.TabStop = false;
m_hostPanel.BorderStyle = BorderStyle.None;
m_hostPanel.BackColor = Color.Transparent;
m_tsdd = new ToolStripDropDown();
m_tsdd.CausesValidation = false;
m_tsdd.Padding = Padding.Empty;
m_tsdd.Margin = Padding.Empty;
m_tsdd.Opacity = 0.9;
m_control = pControl;
m_control.CausesValidation = false;
m_control.Resize += MControlResize;
//m_hostPanel.Controls.Add(m_control);
m_tsdd.Padding = Padding.Empty;
m_tsdd.Margin = Padding.Empty;
m_tsdd.MinimumSize = m_tsdd.MaximumSize = m_tsdd.Size = pControl.Size;
m_tsdd.Items.Add(new ToolStripControlHost(m_control));
}
private void ResizeWindow()
{
m_tsdd.MinimumSize = m_tsdd.MaximumSize = m_tsdd.Size = m_control.Size;
m_hostPanel.MinimumSize = m_hostPanel.MaximumSize = m_hostPanel.Size = m_control.Size;
}
private void MControlResize(object sender, EventArgs e)
{
ResizeWindow();
}
/// <summary>
/// Display the popup and keep the focus
/// </summary>
/// <param name="pParentControl"></param>
public void Show(Control pParentControl)
{
if (pParentControl == null) return;
// position the popup window
var loc = pParentControl.PointToScreen(new Point(0, pParentControl.Height));
pParentControl.Focus();
m_tsdd.Show(loc);
}
public void Hide()
{
m_tsdd.Hide();
}
public void Close()
{
m_tsdd.Close();
}
public void Dispose()
{
m_control.Resize -= MControlResize;
m_tsdd.Dispose();
m_hostPanel.Dispose();
}
}
Firstly, I personally don't see any benefit in having a control inside another. Yes, the child control is locked inside its parent's boundaries automatically for you, but this benefit is negated by the issue that you're facing, and solving that issue requires the same work as when the two controls have no relation. In both cases, you'll have to do the calculations manually to keep the child visible inside its parent. In the second case the parent is the app's window.
Secondly, I don't recommend using hacks like the one mentioned in the comments to show the child outside its parent's boundaries. The hack creates more issues than it solves, as you found out. And what's the point of that hack anyway? If you want to show the child outside the parent, then don't make it a child control in the first place, and you don't need any hack.
The best solution is the one that you find in any well designed app, and in Windows itself. Open any app, let's say Notepad, and right-click near the upper-left corner. You'll see the context menu pulling to lower-right direction. Now right-click near the other three corners and you'll see the context menu pulling in different direction each time, so it will always be visible inside the app. Now if you resize the app window too small and right-click, the context menu will choose the best direction but some of it will be outside the app because the window is too small. That's why you need your list not to be a child, but it's up to you, and it's only about these edge cases. The solution will be similar in both cases.
You're displaying the list in this line:
listbox.SetBounds(cp.X + 20, cp.Y + 20, maxWidth, 60);
The key is cp.X and cp.Y. This is what decides where the list will appear. You need to make this point dynamic and responsive to the boundaries of the parent. You fixed the width to maxWidth and height to 60, so I will use those values in the calculation.
To make sure the list will not go beyond the bottom:
var y = this.Height < cp.Y + 60 ? this.Height - 60 : cp.Y;
To make sure the list will not go beyond the right:
var x = this.Width < cp.X + maxWidth ? this.Width - maxWidth : cp.X;
Now you can show your list at the calculated point:
listbox.SetBounds(x, y, maxWidth, 60);
Notes:
I didn't include the 20 gap that you used. I think it looks better without the gap and I haven't seen any app that has a gap. If you prefer the gap, add it to the calculation of x and y. Don't add it in the SetBounds() or that will screw up the calculation.
The calculation above doesn't take into account when the parent size is too small to show the child inside. If you want to support that edge case, you need to make the child a separate control and add some checks to the calculation.
Hello I made a custom text box with an associated label.
and I have a custom Form.
when on a form if I drag drop my custom textbox from the toolbox and set it's properties I can see that it works. However what I would like to do is when I'm on my custom control where I have a TableLayoutPanel (with 3 rows)
on row index 1(mid row) I would like to add my custom controls programatically.
when I do this the text label is somewhere else then the textbox.
My Code and the Image to my problem is below:
MyCustomTextBox:
public class MyLbTextBox : TextBox
{
#region CustomProperties
private Label AssociatedLabel = new Label();
private string _myLbText;
public string MyTextLabel
{
get => _myLbText;
set
{
_myLbText = value;
AssociatedLabel.Text = _myLbText ?? _myBindingField;
Size s = TextRenderer.MeasureText(AssociatedLabel.Text, AssociatedLabel.Font);
AssociatedLabel.Location =
new Point(Location.X - s.Width - AssociatedLabel.Padding.Right, Location.Y);
var MyMargin = this.Margin;
MyMargin.Left = 100;
this.Margin = MyMargin;
}
}
#endregion
private string _myBindingField;
public string MyBindingField
{
get { return _myBindingField; }
set
{
_myBindingField = value;
}
}
private MyJoins.MyExpressions _myExpression;
public MyJoins.MyExpressions MyExpression
{
get => _myExpression;
set => _myExpression = value;
}
public MyLbTextBox()
{
_myExpression = MyJoins.MyExpressions.Equals;
ParentChanged += MyLbTextBox_ParentChanged;
LocationChanged += MyLbTextBox_LocationChanged;
Disposed += MyLbTextBox_Disposed;
}
private void MyLbTextBox_Disposed(object sender, EventArgs e)
{
AssociatedLabel.Dispose();
}
private void MyLbTextBox_LocationChanged(object sender, EventArgs e)
{
Size s = TextRenderer.MeasureText(AssociatedLabel.Text, AssociatedLabel.Font);
AssociatedLabel.Location =
new Point(Location.X - s.Width - AssociatedLabel.Padding.Right, Location.Y);
}
private void MyLbTextBox_ParentChanged(object sender, EventArgs e)
{
AutoAddAssociatedLabel();
}
private void AutoAddAssociatedLabel()
{
if (Parent == null) return;
AssociatedLabel.Padding = new Padding(3);
Size s = TextRenderer.MeasureText(AssociatedLabel.Text, AssociatedLabel.Font);
AssociatedLabel.Location =
new Point(Location.X - s.Width - AssociatedLabel.Padding.Right, Location.Y);
Parent.Controls.Add(AssociatedLabel);
}
}
By the way, this is how I add my controls:
after adding my controls through the property grid
this is how I set them on the screen
private void _mySearchFields_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_mySearchFields == null) return;
foreach (var searchField in _mySearchFields)
{
if (MySearchFieldsPanel.Contains(searchField.MyControl)) continue;
MySearchFieldsPanel.Controls.Add(searchField.MyControl, 1, 0);
}
var myHeight = MySearchFieldsPanel.Controls.Cast<Control>().Sum(variable => variable.Height);
MyBdPanel.RowStyles[1].Height = myHeight + 40;
}
I appreciate any help
This line is a reason of all problems:
Parent.Controls.Add(AssociatedLabel);
This is bad idea if you are creating composite controls (composite = consisting from several real controls). That will cause layout problems you have experienced and more.
Instead consider either:
Utilize UserControl to create composition.
Create custom control (like you do) but without more controls. If you need label - draw it as text in OnPaint while allocating some space: fixed with margin, adjustable with some property or dynamic with measuring text.
I have added splitcontainer in a form for my application. Basically, I have multiple buttons in left panel, each having drop down menu items added dynamically. When an item is clicked, it brings a common form with updated values(in labels) in right panel. It works fine for the first time when a menu item is clicked.But in the next click of another menu item, brings out the old form details which should be giving a form with updated label values.The issue I am having is that the form is not updating label values. I couldn't find much about this issue online.Any help would be much appreciated.
Splitcontainer form;
public int lightIndex
{
get { return _lightindex; }
set { _lightindex = value; }
}
public int groupIndex
{
get { return _groupindex; }
set { _groupindex = value; }
}
Button click event
private void button_Click(object sender, EventArgs e)
{
Button btnSender = (Button)sender;
groupIndex = groupbuttons.IndexOf((Button)sender);
ContextMenu cm = new ContextMenu();
List<UserLight> alllight = Global.g_userlightgroups[_groupindex].getUserlights();
for (int i = 0; i < alllight.Count(); i++)
{
ContextMenu = cm;
MenuItem item = new MenuItem(alllight[i].getlightType().ToString());
item.Click += Item_Click;
cm.MenuItems.Add(item);
}
Point ptLowerLeft = new Point(0, btnSender.Height);
ptLowerLeft = btnSender.PointToScreen(ptLowerLeft);
cm.Show(btnSender, ptLowerLeft);
}
private void Item_Click(object sender, EventArgs e )
{
MenuItem menuItem = (MenuItem)sender;
String lighttype = menuItem.Text;
if (lighttype.Equals("PRIMITIVE"))
{
PrimitiveLight prim = new PrimitiveLight();
prim.TopLevel = false;
prim.Parent = lightcontainer.Panel2;
prim.groupIndex = _groupindex;
prim.lightIndex = menuItem.Index;
prim.Show();
prim.Dock = DockStyle.Fill;
}
}
PrimitiveLight form: This form will be inserted into splitcontainer panel2 when menu item is clicked.
private int _lightindex;
private int _groupindex;
public int lightIndex
{
get { return _lightindex; }
set { _lightindex = value; }
}
public int groupIndex
{
get { return _groupindex; }
set { _groupindex = value; }
}
private void PrimitiveLight_Load(object sender, EventArgs e)
{
lightDefname.Text = Global.g_userlightgroups[_groupindex].getUserlights().ElementAt(_lightindex).getdefName().ToString();
lightCustname.Text = Global.g_userlightgroups[_groupindex].getUserlights().ElementAt(_lightindex).getcustName().ToString();
}
panel2.Refresh() Maybe, dont know your issue but you can refresh the panel.
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.