I have a tool strip which contains a ToolStripDropDownButton. As an alternative access method, I would also like to be able to display this button's dropdown menu as a context menu, when the user right-clicks in the area below the tool strip.
I tried the following code, but it didn't work (it displayed the button's dropdown in the normal location, directly under the button):
Point contextMenuLocation = [get from WM_CONTEXTMENU]
myButton.DropDown.Show( contextMenuLocation );
The best idea I can think of would be to copy the toolstrip items from the button's dropdown into a ContextMenuStrip, but I don't see any simple way to do that (ToolStripItem does not implement ICloneable or a Clone method). Tool strip items store a reference to their Parent, so I can't just add the existing items to the context menu, as that would break the button.
Does anybody have a good idea on how to accomplish this?
A good way of populating two different dropdown with the same items is to extract the items creation into a function that builds the necessary drop down just before you open any instance of that dropdown. This also lets you enable a disable stuff if the application state changes.
class A
{
public A()
{
button = new ToolStripDropDownButton();
button.DropDown = new ToolStripDropDown();
ToolStripDropDown dropDown = new ToolStripDropDown();
dropDown.Opening += DropDownOpening;
menu.Items.DropDown = dropDown;
}
void DropDownOpening(object sender, EventArgs e)
{
ToolStripDropDown dropDown = sender as ToolStripDropDown;
if(dropDown != null)
{
dropDown.Items.Clear();
BuildMenu(dropDown);
}
else
{
// throw if you like
}
}
void BuildMenu(ToolStripDropDown dropDown)
{
// TODO : Add items to dropdown
// TODO : Take decisions depending on current application state
}
ToolStripDropDownButton button;
MenuStrip menu;
}
Related
I need to view new content in the same form (not to open a new form) when I click some button.
For exapmle this opera installer. When I click "Options" content changes in this window. And when I click "Back" I see previous components.
There is different ways to achive this easily :
You can use the TabControl control, which holds different pages you can design separatly in the vs designer.
Then you choose the initial one, hide the tabs selection bar on top, and control the TabPage selection with your buttons with SelectedTab or SelectedIndex properties.
Or you can simply wrap each section in a GroupBox control, set them all to Visible=false except the initial one and handle their visibility with your buttons with something like :
List<GroupBox> sections = new List<GroupBox>() { /* add your sections */ };
nextButton.OnClick += (o, e) =>
{
sections.ForEach(s => s.Visible = .../* condition to select the section you want displayed and hide others */);
};
I need to create a treeview with checkbox node like this image :
How to do that?
thx!
The TTreeView component does not natively support checkboxes, but the standard Windows TREEVIEW control does, via the TVS_CHECKBOXES style:
TVS_CHECKBOXES
Version 4.70. Enables check boxes for items in a tree-view control. A check box is displayed only if an image is associated with the item. When set to this style, the control effectively uses DrawFrameControl to create and set a state image list containing two images. State image 1 is the unchecked box and state image 2 is the checked box. Setting the state image to zero removes the check box altogether. For more information, see Working with state image indexes.
Version 5.80. Displays a check box even if no image is associated with the item.
Once a tree-view control is created with this style, the style cannot be removed. Instead, you must destroy the control and create a new one in its place. Destroying the tree-view control does not destroy the check box state image list. You must destroy it explicitly. Get the handle to the state image list by sending the tree-view control a TVM_GETIMAGELIST message. Then destroy the image list with ImageList_Destroy.
If you want to use this style, you must set the TVS_CHECKBOXES style with SetWindowLong after you create the treeview control, and before you populate the tree. Otherwise, the checkboxes might appear unchecked, depending on timing issues.
To apply the TVS_CHECKBOXES style to a TTreeView component, you should derive a new component and override the virtual CreateParams() method, eg:
class TMyTreeView : public TTreeView
{
protected:
virtual void __fastcall CreateParams(TCreateParams &Params);
};
void __fastcall TMyTreeView::CreateParams(TCreateParams &Params)
{
TTreeView::CreateParams(Params);
Params.Style |= TVS_CHECKBOXES;
}
To assign the checkbox states in code, you can then use the TreeView_GetItem()/TreeView_SetItem() macros to toggle a node's state image index as needed.
Alternatively, a more flexible approach is to simply assign your own TImageList to the TTreeView::StateImages property and fill it with whatever checkbox images you want, and then you can set the TTreeNode::StateIndex property as needed. To react to user input on the checkboxes, use the TTreeView::OnClick and TTreeView::OnKeyDown events to toggle the TTreeNode::StateIndex accordingly:
void __fastcall ToggleTreeNodeCheckBox(TTreeNode *Node)
{
if ((Node) && (Node->StateIndex != -1))
{
if (Node->StateIndex == MyCheckedStateImageIndex)
Node->StateIndex = MyUncheckedStateImageIndex;
else
Node->StateIndex = MyCheckedStateImageIndex;
}
}
void __fastcall TMyForm::TreeView1Click(TObject *Sender)
{
TPoint P;
::GetCursorPos(&P);
// or: P = Mouse->CursorPos;
// or: POINTS pts = MAKEPOINTS(::GetMessagePos()); P = Point(pts.x, pts.y);
P = TreeView1->ScreenToClient(P);
if (TreeView1->GetHitTestInfoAt(P.x, P.y).Contains(htOnStateIcon))
ToggleTreeNodeCheckBox(TreeView1->GetNodeAt(P.x, P.y));
}
void __fastcall TMyForm1::TreeView1KeyDown(TObject *Sender, WORD &Key, TShiftState Shift)
{
if (Key == VK_SPACE)
ToggleTreeNodeCheckBox(TreeView1->Selected);
}
All I want to do is capture an event every time an item in a Grid using CheckBoxSelectionModel is either checked or unchecked. The checked/selected part is easy using the SelectionHandler. I'm not seeing anything that fires a deselection event in a multi-select mode though. I have a grid with 1000 items or so, and I let users multi-select items to track on a map. It's not giong to perform well to scan the entire model whenever a selection changes so I'm wondering how to handle this.
You are correct. SelectionHandler will only provide checked/selected status. I had a similar requirement and I solved it by overriding the onSelectChange() method of CheckBoxSelectionModel.
Here is the sample code for your reference.
IdentityValueProvider<VO> identity = new IdentityValueProvider<VO>();
CheckBoxSelectionModel<VO> sm = new CheckBoxSelectionModel<VO>(identity) {
protected void onSelectChange(VO model, boolean select) {
super.onSelectChange(model, select);
if (select) {
// Do something on select ...
} else {
// Do something on deselect ...
}
};
};
Hope this helps.
I have a Win form application (VS 2010 / C#) and I'm trying to figure out how to refresh pages without a refresh button. Currently I can refresh a page (basically to reset the data bindings) with a refresh button containing code something like this (this.refresh() does not seem to work for some reason):
this.Hide();
AccountSettings AS = new AccountSettings();
AS.ShowDialog();
An example I have is a page with numerous settings including data grids with CellClick events. When I click a cell I can make changes to a database. I hit close to go back to the Settings page but the only way for me to see the changes are to refresh() the page via the button.
So the short of it is, is there any way to refresh a form page from another form page?
For instance, when I click the Save button or close the child window.
Maybe pass the original form as an argument to the second form:
Form2 frm2 = new Form2(this);
And in Form2:
Form1 frm1;
public Form2(Form1 frm1)
{
InitializeComponent();
this.frm1 = frm1;
}
And then have in Form2:
frm1.Update();
Refresh on winform controls repaints the control itself. I find it useful to create a method that just loads my controls with the proper data, and then call it as necessary. (Including Form load)
private void ResetData()
{
//code to update settings
}
If you are showing the form that is closing as a dialog also you can take advantage of that, and check the status of the dialog instead of just opening it.
Form2 dlg = new Form2();
if (dlg.ShowDialog == System.Windows.Forms.DialogResult.OK) {
//code that updates your data
ResetData();
}
If its not a dialog there are a few things you could do and how your application works would make one method better than others. Here is just one example.
If your changes are something you don't need access to data from the other window to update you can handle the closed event of the form you create.
Create a class level variable to hold the form that is opened, so that you can also remove the event handlers you create:
private Form2 frm;
To create an instance of the form, and add the close event handler:
frm = new Form2();
frm.FormClosed += OnForm2Closed;
The event handler method:
private void OnForm2Closed(object sender, FormClosedEventArgs e)
{
ResetData();
frm.FormClosed -= OnForm2Closed;
}
How do I prevent Multiple forms from opening?
I do .show on the form but the user can click the main form and the button again and another instance of form opens.
Two options, depending on what you need:
Use ShowDialog instead of Show, which will open a modal window. This is the obvious solution if you don't need your main form to be active while the child form is open.
Or keep track of the window you opened already in the main form and do nothing if it's already open. This will be needed if you want the user to be able to use the main form while the child form is already open, maybe to open other forms.
do something like:
SingleForm myform = null;
void ShowMyForm_Click(object sender, EventArgs e)
{ if (myform == null)
{
myform = new SingleForm();
}
myform.Show();
myform.BringToFront();
}
Force your form object to adhere to the singleton pattern
I prefer to use Generics and lazy loading to handle my forms. Since all of my forms inherit from a base class, I can use the same method to bring forms to the front, send them to the back, destroy them, start them, etc.
If you keep a form manager class that's responsible for managing any loaded forms, you can bring whatever form to the front that you want, or prevent specific forms from being able to come back unless certain criteria are met.
public void LoadForm<T>() where T : MyNameSpace.MyBaseForm
{
// Load all your code in this joint and just call it when you
// need a form. In here, you can determine if a copy of the form
// already exists and then bring it forward or not
}
Disable the main form until the child form goes away, or disable the button.
button_onClick(object Sender, EventArgs e)
{
Button btn = sender as Button;
btn.Enabled = false;
Form myform = new MyForm();
myform.Show();
}
Of course, you really should be using form.ShowDialog() rather than form.Show() if you want modal behavior.