First, a little overview of how my current UI looks like:
Note that other than the stuff in the ToolStripControlHost, everything else is standard WinForms.
In short, I want to have something similar to the ToolStripControlHost but I need it "pinned" to the bottom of the menu, mostly so that when there is a lot of items, it is not scrolled like the rest of the menu items.
After some searching around I came to the conclusion that maybe customizing painting might be the solution, don't know if this is the case though.
Here's some sample code, but I'm not sure how useful it is:
public ToolStripDropDownButtonContainer(ToolStripDropDownButton button)
{
this.UIControl = button.GetCurrentParent();
this.Button = button;
if (this.Button.Tag == null)
{
this.Button.Tag = true;
this.Button.DropDownDirection = ToolStripDropDownDirection.AboveLeft;
ToolStripDropDownMenu menu = (ToolStripDropDownMenu)this.Button.DropDown;
menu.SuspendLayout();
try
{
menu.BackColor = Color.White;
menu.ShowImageMargin = false;
menu.ShowCheckMargin = false;
menu.AutoSize = true;
menu.Margin = Padding.Empty;
menu.Padding = Padding.Empty;
menu.GripMargin = Padding.Empty;
menu.GripStyle = ToolStripGripStyle.Hidden;
menu.MinimumSize = new Size(310, 0);
menu.MaximumSize = menu.MinimumSize;
// TODO pin panel (or some control) to the bottom-side of the menu
}
finally
{
menu.ResumeLayout();
}
}
}
My solution to this problem is to completely avoid using the normal menu control containment system and instead have the menu show one FlowLayoutPanel which instead contains my menu items.
This involved having to add some various trickeries to get the panel to behave nicely with the UI. The added advantage of this approach is more flexibility and control over the system.
On the downside, I noticed a degrade in performance when I have a tonne of sub-items, but I'll investigate this separately.
Related
When programmatically adding controls to a tab control, I have been using the Form_Load event to create and embed things like datagridviews into my UI. I made a class that inherits from DataGridView
class DBDataGridView : DataGridView
{
public DBDataGridView()
{
DoubleBuffered = true;
AllowUserToAddRows = false;
AllowUserToDeleteRows = false;
AllowUserToResizeRows = false;
AllowUserToOrderColumns = false;
AllowUserToResizeColumns = false;
RowHeadersVisible = false;
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
ReadOnly = true;
Dock = DockStyle.Fill;
SelectionMode = DataGridViewSelectionMode.FullRowSelect;
TabStop = false;
}
}
And I call it later in the Form_Load event like so
private void MainDesignerForm_Load(object sender, EventArgs e)
{
DBDataGridView _DGV = new DBDataGridView();
var listOfOverlays = new List<OverlaySelectionList>()
{
new OverlaySelectionList { Description = "Description 1", PartNumber = "123-R1"},
new OverlaySelectionList { Description = "Description 2", PartNumber = "456-R1"}
};
var overlayList = new BindingList<OverlaySelectionList>(listOfOverlays);
_DGV.DataSource = overlayList;
Tab_Overlay.Controls.Add(_DGV);
_DGV.ClearSelection();
}
This gridview is on the THIRD tab of the TabControl, and everything works as expected except the ClearSelection(). No matter where I call it, it does not clear the initial row selection of the DGV. However, if I fire the same code block from a button ON the third tab, the formatting AND the ClearSelection() behave as expected.
What is causing this behavior?
Thanks to 41686d6564 and Jimi for the insight into the specifics on why this was happening.
Reiterating what they said in the comments: Assignment of properties appear to be cached regardless of whether the control they belong to is active or not (Hence why all the sizing and formatting properties were present at run time). However, actions that require a handle, like ClearSelection() require the control to be shown and active for the intended behavior to be observed.
Setting the selected tab to where the DataGridView before calling ClearSelection() was the solution (Or in my case, I had nested tabs, so I had to follow the tab tree to get to the specific tab that the DataGridView was on)
So now, part of the Load_Form logic is to check WHERE the control is located, make that tab active, THEN format and clear selections for each control that is being added. This allowed ClearSelection() to work as intended.
TLDR; Looking for a method to retrieve all radiobuttons by means of something like this... (psudo)
List<RadioButton> btn = new List<RadioButton>;
btn = stackPanel.getAllRadioButtons()
I am currently building a little quiz application in C#. I have functions that add the required GUI elements to a groupbox. I would like to know if there is any way that I can loop through the created elements (for instance radio buttons) to see which are checked.
Here is one of the functions along with how they are added to the window.
private void tfQ(string questionBody)
{
StackPanel questionPanel = new StackPanel{Orientation = Orientation.Vertical};
questionPanel.Children.Add(new Label { Content = questionBody });
GroupBox group = new GroupBox();
RadioButton trueRadio = new RadioButton();
trueRadio.Content = "True";
RadioButton falseRadio = new RadioButton();
falseRadio.Content = "False";
questionPanel.Children.Add(trueRadio);
questionPanel.Children.Add(falseRadio);
group.Content = questionPanel;
mainStack.Children.Add(group);
}
Constructor:
public quiz()
{
tfQ("This is a true/false question");
Window w = new Window();
w.Content = mainStack;
w.Show();
}
I have found many ways to do it in the C# scripting format
(using the Control function ...)
var checkedButton = container.Controls.OfType<RadioButton>()
.FirstOrDefault(r => r.Checked);
but I have not yet found a "programmatic" way of doing it. I have considered changing the type of void tfQ to a StackPanel, but that only helps me to easier loop through the stack panels, althought that only partly solves me problem - allows me to easier loop through the StackPanels but I still don't know how to get the RadioButtons on a panel.
P.S I am very new to C# - with experience in Java/C/C++/Python
I made use of the following code to cast the content into a new groupbox and stackpanel, respectively.
if (child is GroupBox)
{
if ((child as GroupBox).Content is StackPanel)
{
StackPanel d = (StackPanel)((GroupBox)child).Content;
}
}
This allowed me to cast the content into a local copy of a control. It may not be the best way - I'm sure it is very inefficient to be honest. Solved the problem.
so I figured I'm making just a stupid mistake here. In the first of what will be many controls, I need to either show a balloon tooltip when a bool is true or not show them when the bool is false. I know that ShowAlways is not what I need to modify and I've tried various solutions already. Does anyone spot the problem? The bool is set by a checked dropdown item in a Help Menu Strip Item.
It will open with the application with the correct display, but as soon as I check that option to show it, it always shows there after.
public void changeBalloonProperties(bool boolSet)
{
ToolTip helpDeskInfoButtonToolTip = new ToolTip();
if (boolSet)
{
helpDeskInfoButtonToolTip.ToolTipTitle = "HelpDesk Information Button";
helpDeskInfoButtonToolTip.UseFading = true;
helpDeskInfoButtonToolTip.UseAnimation = true;
helpDeskInfoButtonToolTip.IsBalloon = true;
helpDeskInfoButtonToolTip.ShowAlways = true;
helpDeskInfoButtonToolTip.AutoPopDelay = 5000;
helpDeskInfoButtonToolTip.InitialDelay = 1000;
helpDeskInfoButtonToolTip.ReshowDelay = 500;
helpDeskInfoButtonToolTip.SetToolTip(helpDeskButton, "Click to launch HelpDesk user info page in default browser.");
}
else
{
helpDeskInfoButtonToolTip.RemoveAll();
}
}
You are creating a new ToolTip instance each time the changeBalloonProperties is called so the code isn't removing the caption associated with the original ToolTip that was used with the helpDeskButton. Try moving the ToolTip declaration outside of your changeBalloonProperties method so the same ToolTip object is used with RemoveAll().
Also note you can use that same ToolTip object to add captions for multiple controls (as shown in the sample here) and it's probably better to set helpDeskInfoButtonToolTip.Active = false to disable them all at once instead of setting and removing the captions (and other properties) each time you toggle.
I have two panels occupying the same space in one side of a splitviewcontainer.
I want to dynamically show or hide one panel or the other.
Approaches I've tried so far are:
if (treeView1.SelectedNode.Name.Contains("cat")) {
menuItemPanel.SendToBack();
foreach (Control control in menuItemPanel.Controls)
control.SendToBack();
menuItemPanel.Visible = false;
categoryPanel.Hide();
categoryPanel.Visible = true;
categoryPanel.BringToFront();
foreach (Control control in categoryPanel.Controls)
control.BringToFront();
categoryPanel.Show();
// ...
}
and the converse for the else case.
None of these seem to work. That is the categoryPanel is shown when a category node is selected, but the screen shows blank space when a menuItem node is selected.
What am I doing wrong?
The code doesn't make much sense, re-ordering the controls in the panels is not what you want to do. Also sounds that the code in the else clause is failing, code you didn't post. Do it like this instead (a guess):
bool categorySelected = treeView1.SelectedNode.Name.Contains("cat");
menuItemPanel.Visible = !categorySelected;
categoryPanel.Visible = categorySelected;
try this
if (treeView1.SelectedNode.Name.Contains("cat"))
{
menuItemPanel.Visible = false;
categoryPanel.Visible = true;
}
I am using a ToolStripDropDown control to implement the dropdown portion of a custom ComboBox-like control. In order to be visually appealing, I am imposing a MaximumSize on the dropdown and manually specifying the width of each ToolStripButton within it - the result is a popup which is the same width as the control that activates it, with a cap on the height of the height of the dropdown portion.
Example (simplified):
ToolStripDropDown dropDown = new ToolStripDropDown();
dropDown.MaximumSize = new Size(200, 100);
dropDown.RenderMode = ToolStripRenderMode.System;
dropDown.AutoSize = true;
for (int i = 0; i < 50; i++) {
ToolStripButton dropDownItem = (ToolStripButton)dropDown.Items.Add("Item " + i);
dropDownItem.AutoSize = false;
dropDownItem.Size = new Size(200, 20);
}
dropDown.Show(owningControl, new Point(0, owningControl.Height - 1));
As you can see, the constraints on the popup's size are applied, however the up/down scroll buttons are not displayed and there seems to be no way to make them appear. There do not appear to be any methods or properties within ToolStripDropDown regarding the scrolling offset or a mechanism to scroll a particular item into view (such as EnsureVisible() on ListViewItem).
How, then, can I get the dropdown to scroll? Any method would be sufficient, be it a scroll bar, scroll buttons or even the mouse-wheel.
(Incidentally, I have tried many times to make similar controls using a Form for the dropdown portion - despite trying dozens of solutions to prevent the popup from stealing focus or gaining focus when its controls are clicked, this seems to be a dead end. I have also ruled out using ToolStripControlHost, whose hosted control can still take focus away from the form that opened it.)
Finally cracked this one. It occurred to me that ContextMenuStrip and ToolStripDropDownMenu are capable of the auto-scrolling behaviour which their base class, ToolStripDropDown, cannot provide. Initially, I avoided these alternative controls because they usually add a wide margin. This can be removed via ShowImageMargin and ShowCheckMargin. Even after doing this, a small (approx 5px) margin remains. This can be removed by overriding the DefaultPadding property:
public class MyDropDown : ToolStripDropDownMenu {
protected override Padding DefaultPadding {
get { return Padding.Empty; }
}
public MyDropDown() {
ShowImageMargin = ShowCheckMargin = false;
RenderMode = ToolStripRenderMode.System;
MaximumSize = new Size(200, 150);
}
}
// adding items and calling Show() remains the same as in the question
This results in a popup window which can contain any type of ToolStrip item, enforces MaximumSize, has no margin and, most importantly, does not steal focus and cannot receive focus.
This is your nemesis:
internal virtual bool RequiresScrollButtons
{
get
{
return false;
}
set
{
}
}
It is internal, you cannot override it. You can revive your approach of using a Form by fixing the focus stealing behavior. Paste this into the form class:
protected override bool ShowWithoutActivation
{
get { return true; }
}