I want to show on my listbox this type of format:
"File name Size";
foreach (string filePath in filePaths)
{
BarraDeProgresso.Visible = true;
PegarMeuFicheiro = filePath;
// here is where im tryng to do it. but isn´tworking as i expected.
lbMostrarItems.Items.Add(String.Format("{0, 5} {1, 30}",PegarMeuFicheiro.Substring(9 + nome.Length + 20, PegarMeuFicheiro.Length - (9 + nome.Length + 20)),"Size"));
//lbMostrarItems.SelectedIndex = lbMostrarItems.Items.Count - 1;
}
What can I do to make it all align on the right?
Make use of the ListView control instead which allows for columns and many other features.
First add the control, then select it and go to the control's property and change the View to Details. This will allow you to see a list with resizable column names.
Next, create two columns (One for the file name and the other for the file size) or whatever it may be in your case. To do this, on the properties window go to Columns and click it to get the dialog window which will allow you to add columns.
Finally, here is some sample code on how to use ListView.
private void Form1_Load(object sender, EventArgs e)
{
var fileListForExample = Directory.GetFiles(#"C:\");
foreach (var item in fileListForExample)
{
FileInfo fileInfo = new FileInfo(item);
var lstItem = new ListViewItem(Path.GetFileName(item));
lstItem.SubItems.Add(fileInfo.Length.ToString());
var itemAdded = listView1.Items.Add(lstItem);
}
}
You can manually draw item in the ListBox.
Example:
public Form1()
{
//InitializeComponent();
this.Width = 500;
var listBox = new ListBox { Parent = this, Width = 400, Height = 250 };
listBox.DrawMode = DrawMode.OwnerDrawFixed;
var files = new DirectoryInfo(".").GetFiles();
listBox.DataSource = files;
listBox.DrawItem += (o, e) =>
{
e.Graphics.DrawString(files[e.Index].Name, listBox.Font, Brushes.Black, e.Bounds);
var length = files[e.Index].Length.ToString();
var size = e.Graphics.MeasureString(length, listBox.Font);
e.Graphics.DrawString(length, listBox.Font,
Brushes.Black, e.Bounds.Width - size.Width, e.Bounds.Y);
};
}
Related
I'm practicing my coding and am trying to create a till by Dynamically prefill the items that are stored in a text file. I can dynamically create Tabs, Buttons and create menu buttons based on the categories of items. Where I'm struggling is I'm trying to switch the Tab on a button click. The Tabs are given a Name, which is the category ID, and the Text displays the category. In the event that trys to switch the tab I get the following error:
Error CS0266 Cannot implicitly convert type 'object' to 'System.Windows.Forms.TabPage'. An explicit conversion exists (are you missing a cast?)
I'm presuming that I need to Create a tab page or something, but I can't find out how to do this. Been on it for hours! Here's my code....
public partial class Form1 : Form
{
private void Form1_Load(object sender, EventArgs e)
{
string[] loadedFile = File.ReadAllLines(#"h:\shopItemsV2.txt");
foreach (string line in loadedFile)
{
// Split string and create required variables
string[] newBtnData = line.Split(',');
int catID = int.Parse(newBtnData[0]);
string cat = newBtnData[1];
string item = newBtnData[2];
double price = double.Parse(newBtnData[3]);
// Create tab if needed
if (tabControl1.TabCount < catID)
{
TabPage tp = new TabPage()
{
Text = cat,
Name = catID.ToString()
};
tabControl1.TabPages.Add(tp);
// Add FlowLayoutPanel with unique name
FlowLayoutPanel fp = new FlowLayoutPanel()
{
Name = "fp" + catID.ToString(),
Dock = DockStyle.Fill
};
// Add FlowLayoutPanel to correct Tab
tabControl1.TabPages[catID-1].Controls.Add(fp);
// Create Button for menu
Button newMenuBtn = new Button()
{
Name = cat + "Btn",
Tag = catID,
Width = 100,
Height = 50,
Text = cat,
};
newMenuBtn.Click += SwitchTab;
menuFP.Controls.Add(newMenuBtn);
}
// Create Button
Button newBtn = new Button()
{
Name = item,
Tag = price,
Width = 100,
Height = 100,
Text = item,
};
newBtn.Click += AddItem;
//Add button to correct tab
foreach (TabPage tabP in tabControl1.TabPages)
{
if (tabP.Name == catID.ToString())
{
Control fp = this.Controls.Find("fp"+catID, true).First();
fp.Controls.Add(newBtn);
}
}
}
}
private void SwitchTab(object sender, EventArgs e)
{
// Create button, switch to required Tab
// Tabs named the same as Tag on the button
Button clickedBtn = sender as Button;
tabControl1.SelectedTab = clickedBtn.Tag;
}
}
Any help would be greatly appreciated.
You can store anything in the Tag() property of your button. With that in mind, store a reference to your TabPage!
Change:
// Create Button for menu
Button newMenuBtn = new Button()
{
Name = cat + "Btn",
Tag = catID,
Width = 100,
Height = 50,
Text = cat,
};
To:
// Create Button for menu
Button newMenuBtn = new Button()
{
Name = cat + "Btn",
Tag = tp; // store the reference to the TabPage you created
Width = 100,
Height = 50,
Text = cat,
};
Then the suggestion by Uwe Keim should work:
tabControl1.SelectedTab = clickedBtn.Tag as TabPage;
private void AddNewPr_Click(object sender, EventArgs e)
{
TabPage tab = new TabPage();
_list = new ListBox();
_list2 = new ListBox();
PictureBox picBox = new PictureBox();
picBox.Click = picBox_Click;
//More stuff here
//Add the controls
tabControl1.Controls.Add(tab);
tab.Controls.Add(list);
tab.Controls.Add(list2);
tab.Controls.Add(pictureBox);
}
I got a problem with my WinForms-Application; I want to Drag&Drop a Outlook Mail into a RichTextBox. I found many articles about the Drag&Drop function but they all insert the Mailtext into the rTB (see: Link).Actually I can insert document like .txt, .jpg, Outlook-mails from desktop... to my program. My richTextBox automatic generate an image for the file and insert this image on a position. Like:
.
After the user Drag and Drop the file a image will be created on the Drop position and if the user double click the image the file will be opened.
PROBLEM:
The program work fine, but if I try to drag a mail out of Outlook, the program insert the mailbody to the richTextBox and no as an image.
I have saved one Mail on the desktop and try to insert this mail to my program. The following output is given in my richTextBox (would be perfect):
Mailicon from desktop per Drag&Drop:
Otherwise I tried to Drag&Drop a mai from Outlook to my program and the following output is given (Just look at the text and not the images:
Mail from Outlook per Drag&Drop (THE PROBLEM!!!):
The Programm insert the cc/mailadress and Mailbody to the rTB.
Here is the code behind: (My richTextBox is a own created richTextBox called "MyRichTextBox" Download the project under: link_RICHTEXTBOX. )
CODE
private void Form1DragDrop(object sender, DragEventArgs e)
{
Startup();
//Microsoft.Office.Interop.Outlook.ApplicationClass oApp =
// new Microsoft.Office.Interop.Outlook.ApplicationClass();
Microsoft.Office.Interop.Outlook.Explorer oExplorer = _Outlook.ActiveExplorer();
Microsoft.Office.Interop.Outlook.Selection oSelection = oExplorer.Selection;
foreach (object item in oSelection)
{
Microsoft.Office.Interop.Outlook.MailItem mi = (Microsoft.Office.Interop.Outlook.MailItem)item;
rTB_test.Text = mi.Body.ToString();
string mailName = "Mail\n" + (mailList.Count + 1);
// load an image with enough room at the bottom to add some text:
Image img = Image.FromFile(Imagepath);
// now we add the text:
int width = img.Width;
using (Graphics G = Graphics.FromImage(img))
using (Font font = new Font("Arial", 7f))
{
SizeF s = G.MeasureString(mailName, font, width);
G.DrawString(mailName, font, Brushes.Black,
(width - s.Width) / 2, img.Height - s.Height - 1);
}
// adding the image is easy only if we use the clipboard..
Clipboard.SetImage(img);
// now insert image
rTB_test.Paste();
// now we can get a hashcode as a unique key..
// ..we select the image we have just inserted:
rTB_test.SelectionStart = rTB_test.TextLength - 1;
rTB_test.SelectionLength = 1;
// finally we need to store the mail itself with its key:
mailList.Add(rTB_test.SelectedRtf.GetHashCode(), mi);
// cleanup: unselect and set cursor to the end:
rTB_test.SelectionStart = rTB_test.TextLength;
rTB_test.SelectionLength = 0;
}
Microsoft.Office.Interop.Outlook.Application _Outlook = null;
Dictionary<int, Microsoft.Office.Interop.Outlook.MailItem> mailList =
new Dictionary<int, Microsoft.Office.Interop.Outlook.MailItem>();
private void rTB_test_DoubleClick(object sender, EventArgs e)
{
var ss = rTB_test.SelectionStart;
var sl = rTB_test.SelectionLength;
int hash = rTB_test.SelectedRtf.GetHashCode();
// a few checks:
if (sl == 1 && mailList.Keys.Contains(hash))
{
Microsoft.Office.Interop.Outlook.MailItem mi = mailList[hash];
// do stuff with the msgItem..
// ..
}
}
void lbl_MouseDoubleClick(object sender, MouseEventArgs e)
{
Microsoft.Office.Interop.Outlook.MailItem mi =
(Microsoft.Office.Interop.Outlook.MailItem)((Label)sender).Tag;
// code to process the doubleclicked mail item..
}
void Startup()
{
_Outlook = new Microsoft.Office.Interop.Outlook.Application();
}
private void Form1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
After the user double click on the picture the mail should be opened in Outlookexplorer.
UPDATE
If I use the code from TaW´s answer the following output is given:
After I double click the icon the mail won´t be open... So the code from the answer is just a "iconcreation".
Thank you in advanced!
Here is what I meant in my comments:
private void Form1DragDrop(object sender, DragEventArgs e)
{
Startup();
Microsoft.Office.Interop.Outlook.ApplicationClass oApp =
new Microsoft.Office.Interop.Outlook.ApplicationClass();
Microsoft.Office.Interop.Outlook.Explorer oExplorer = _Outlook.ActiveExplorer();
Microsoft.Office.Interop.Outlook.Selection oSelection = Explorer.Selection;
foreach (object item in oSelection)
{
Microsoft.Office.Interop.Outlook.MailItem mi =
(Microsoft.Office.Interop.Outlook.MailItem)item;
// rTB_test.Text = mi.Body.ToString();
Label lbl = new Label();
lbl.AutoSize = false;
lbl.Size = new Size( 80, 50); // <-- your choice!
lbl.Text = someText; // <-- your choice!
lbl.TextAlign = ContentAlignment.BottomCenter;
lbl.Image = someImage; // <-- your choice!
lbl.ImageAlign = ContentAlignment.TopCenter;
int count = rTB_test.Controls.Count;
int itemsPerRow = rTB_test.Width / 80;
lbl.Location = new Point( (count % itemsPerRow) * 80,
count / itemsPerRow * 50);
lbl.Tag = mi; // store the data object
lbl.MouseDoubleClick += lbl_MouseDoubleClick;
lbl.Parent = rTB_test; // add to the RTF's Controls
}
}
void lbl_MouseDoubleClick(object sender, MouseEventArgs e)
{
Microsoft.Office.Interop.Outlook.MailItem mi =
(Microsoft.Office.Interop.Outlook.MailItem) ( (Label)sender).Tag;
// code to process the doubleclicked mail item..
}
These Labels will sit on top of any Text in the RTB without interfering with it. If you want to, you can modify the code for the Location to move them out of the way or style the Labels in many other ways..
Update
After the last remarks the problem get a lot clearer: other parts of the application are already adding mail icons to the RTB and we shall add more 'of the same'..
Adding an Image is best done via the clipboard; here is code that will do that:
// create some test text, maybe extract it from the mailheader?..
string mailName = "Mail\n" + (mailList.Count + 1);
// load an image with enough room at the bottom to add some text:
Image img = Image.FromFile(yourMailImageFile);
// make the images unique by embedding a counter in a bright pixel:
img = (Image)fingerPrintID((Bitmap)img, 250 - mailList.Count); //*1*
// now we add the text:
int width = img.Width;
using (Graphics G = Graphics.FromImage(img))
using (Font font = new Font("Arial", 7f))
{
SizeF s = G.MeasureString(mailName, font, width);
G.DrawString(mailName, font, Brushes.Black,
(width - s.Width) / 2, img.Height - s.Height - 1);
}
// adding the image is easy only if we use the clipboard..
Clipboard.SetImage(img);
// insert only at the end!
rTB_test.SelectionStart = rTB_test.TextLength;
rTB_test.SelectionLength = 0;
// now insert image
rTB_test.Paste();
// now we can get a hashcode as a unique key..
// ..we select the image we have just inserted:
rTB_test.SelectionStart = rTB_test.TextLength - 1;
rTB_test.SelectionLength = 1;
// retrieve the counter id:
string id = GetID(rTB_test.SelectedRtf); //*2*
// finally we need to store the mail itself with its key:
mailList.Add(id, mi);
// cleanup: unselect and set cursor to the end:
rTB_test.SelectionStart = rTB_test.TextLength;
rTB_test.SelectionLength = 0
We need to create a Dictionary to store our mails:
Dictionary<string, Microsoft.Office.Interop.Outlook.MailItem> mailList =
new Dictionary<string, Microsoft.Office.Interop.Outlook.MailItem>(); // *3*
Here is how we can access the mails in the DoubleClick event:
private void rTB_test_DoubleClick(object sender, EventArgs e)
{
var ss = rTB_test.SelectionStart;
var sl = rTB_test.SelectionLength;
string id = GetID(sr); //*4*
// a few checks:
if (sl == 1 && mailList.Keys.Contains(id) && sr.Contains(#"{\pict\") )
{
Microsoft.Office.Interop.Outlook.MailItem mi = mailList[id];
// do stuff with the msgItem, e.g..
mi.Display();
}
}
Here is the result along with the image I use:
Note that in addition to adding the image we also store the both the mail data and the position of the image in the RTB in a Dictionary.
Update 2: I have replaced the position of the image as key with a HashCode of its RtfText; this is robust to any changes in the rest of the RTF's content. However, it relies on the images being unique, so adding an index to their text as in the code is recommended. (Or setting a few random pixels, maybe based on a GUID..)
Update 3 & 4: (*1* - *6*)
We found that we need to make the key resilient to several changes, like fonts surrounding the icon, which will influence the Rtf code, or the user enlarging the image.
Here is a FingerPrint function, which will make the images we add in unobtrusivly unique by setting a few pixels at the top of the image. Three to set a marker and one to set an ID:
Bitmap fingerPrintID(Bitmap bmp, int key) //*5*
{
for (int i = 0; i < 3; i++)
{
bmp.SetPixel(i, 0, Color.FromArgb(255, 238,238,238)); // EE EE EE
}
bmp.SetPixel(3, 0, Color.FromArgb(255, key, key, key));
return bmp;
}
And to retrieve this function will pull the 3 hex digits as string from the RTF code:
string GetID(string Rtf) //*6*
{
int x = Rtf.IndexOf("eeeeeeeeeeeeeeeeee"); // 238 238 238
if (x < 0) return "";
string hex = Rtf.Substring(x +18, 6);
return hex;
}
I chose the pixels to be rather bright; if you know which image you use, you can optimze the color choice even more.. With the new string id, we don't need the GetHashcode calls..
I am unable to remove the textbox which is created Dynamically using Combobox selected Item in Grid. if the selected value is not equal to "Other (describe)", i have to remove the textbox. I have this code..
private void btn_addnew_Click(object sender, RoutedEventArgs e)
{
ComboBox cmb=new ComboBox();
.....
cmb.SelectionChanged+= cmb_SelectionChanged;
.....
}
void cmb_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var txt = new TextBox();
if (e.AddedItems[0].ToString() == "Other (describe)")
{
var row = (int)((ComboBox)sender).Tag;
Grid.SetRow(txt, row);
Grid.SetColumn(txt, 1);
txt.Margin = new Thickness(10, 10, 0, 0);
grid_typeFixture.Children.Add(txt);
}
else
grid_typeFixture.Children.Remove(txt);
}
There is a risk of setting the Textbox names dynamically if it doesn't follow the namming rules (Example : Textbox name can not have white space), Instead you can use "Tag" property of the textbox while creating and search it whenever want to remove it.
Assign a name to your TextBox while creating, you can use RegisterName,
txt = new TextBox();
txt.Margin = new Thickness(10, 10, 0, 0);
txt.Name = "DynamicLine" + i;
RegisterName(txt.Name, txt);
Grid.SetRow(txt, i);
Grid.SetColumn(txt, 2);
grid_typeFixture.Children.Add(txt);
And you can remove like this, using FindName
txt = (TextBox)grid_typeFixture.FindName("lbl_DynamicLine" + row);
if (txt != null)
{
UnregisterName(txt.Name);
grid_typeFixture.Children.Remove(txt);
}
I have a ToolStrip with a ContextMenu, like so:
I have 2 icons in my Resources, like so:
I'm trying to switch out the icons to their 16x16 counterparts:
void largeIconsToolStripMenuItem_Click(object sender, EventArgs e)
{
var ContextItem = (ToolStripMenuItem) sender;
var ContextMenu = (ContextMenuStrip) ContextItem.Owner;
var ToolStrip = (ToolStrip) ContextMenu.SourceControl;
var Checked = ContextItem.Checked;
ToolStrip.ImageScalingSize = Checked ? new Size(32, 32) : new Size(16, 16);
foreach(ToolStripButton Button in ToolStrip.Items)
Button.Image = Resources.t_new16;
}
This works, but I don't want to add a new line in my foreach for each individual icon. How can I substitute the Resource name to t_icon16, where it is currently t_icon32?
I tried looking for a Name property in Button.Image, but there isn't one.
I also tried:
foreach(ToolStripButton Button in ToolStrip.Items)
foreach(PropertyItem P in Button.Image.PropertyItems)
MessageBox.Show(P.Id.ToString() + " - " + P.Value.ToString());
But no MessageBoxes are shown.
How can I swap the resource image dynamically?
Create two image lists - one with small icons, and another with large icons (in same order):
toolStrip.ImageList = smallImageList;
toolStripButton1.ImageIndex = 0;
toolStripButton2.ImageIndex = 1;
And just switch between lists:
toolStrip.ImageList = largeImageList;
I managed to do this without having to maintain 2 lists of images.
Firstly, the ToolStripButton needs to have its Tag property set to the name of the initial resource:
With that set, this code handles the rest dynamically:
void largeIconsToolStripMenuItem_Click(object sender, EventArgs e)
{
var ResourceManager = new ResourceManager(typeof(Resources));
var ContextItem = (ToolStripMenuItem) sender;
var ContextMenu = (ContextMenuStrip) ContextItem.Owner;
var ToolStrip = (ToolStrip) ContextMenu.SourceControl;
var Checked = ContextItem.Checked;
ToolStrip.ImageScalingSize = Checked ? new Size(32, 32) : new Size(16, 16);
foreach(ToolStripButton Button in ToolStrip.Items)
{
var CurrentResource = Button.Tag.ToString();
var NewResource = CurrentResource.Substring(0, CurrentResource.Length - 2) + (Checked ? "32" : "16");
Button.Image = (Image) ResourceManager.GetObject(NewResource);
Button.Tag = NewResource;
}
}
I'm looking to implement a Visual Studio-style undo drop-down button:
I've looked all over the internet, and can't seem to find any real implementations of this.
I've started by deriving from ToolStripSplitButton, but don't really know where to go from there. Its DropDown property is a ToolStripDropDown, but that doesn't seem to have anything regarding multiple items being selected, much less scrolling, and the text at the bottom.
So instead of the default ToolStripDropDown, I'm thinking maybe the whole drop down part should be a custom control, based on a combobox. The question then, is how to cause the right-side (drop down arrow) button to do something other than show its default drop down?
Am I on the right track here? Thanks!
Yes, I think you're on the right track. And in this case, ToolStripControlHost is your friend.
You don't necessarily need to derive from it (unless you are making your own control), but try just subscribing to the ToolStripSplitButton's DropDownOpening event:
Working example:
private ListBox listBox1;
public Form1()
{
InitializeComponent();
listBox1 = new ListBox();
listBox1.IntegralHeight = false;
listBox1.MinimumSize = new Size(120, 120); \\ <- important
listBox1.Items.Add("Item 1");
listBox1.Items.Add("Item 2");
}
private void toolStripSplitButton1_DropDownOpening(object sender, EventArgs e) {
ToolStripControlHost toolHost = new ToolStripControlHost(listBox1);
toolHost.Size = new Size(120, 120);
toolHost.Margin = new Padding(0);
ToolStripDropDown toolDrop = new ToolStripDropDown();
toolDrop.Padding = new Padding(0);
toolDrop.Items.Add(toolHost);
toolDrop.Show(this, new Point(toolStripSplitButton1.Bounds.Left,
toolStripSplitButton1.Bounds.Bottom));
}
Here is the result:
For your application, you would need to replace the ListBox with your own UserControl, so you can contain whatever your want in it. The ToolStripControlHost can only hold one control, and it's important to set the MinimumSize property, or else the dropped control isn't sized correctly.
Extra thanks to LarsTech! (I didn't know about ToolStripControlHost a few hours ago)
Here is my implementation, which is really close to the VS drop down...
You should be able to just drop this delegate & function into your Form:
public delegate void UndoRedoCallback(int count);
private void DrawDropDown(ToolStripSplitButton button, string action, IEnumerable<string> commands, UndoRedoCallback callback)
{
int width = 277;
int listHeight = 181;
int textHeight = 29;
Panel panel = new Panel()
{
Size = new Size(width, textHeight + listHeight),
Padding = new Padding(0),
Margin = new Padding(0),
BorderStyle = BorderStyle.FixedSingle,
};
Label label = new Label()
{
Size = new Size(width, textHeight),
Location = new Point(1, listHeight - 2),
TextAlign = ContentAlignment.MiddleCenter,
Text = String.Format("{0} 1 Action", action),
Padding = new Padding(0),
Margin = new Padding(0),
};
ListBox list = new ListBox()
{
Size = new Size(width, listHeight),
Location = new Point(1,1),
SelectionMode = SelectionMode.MultiSimple,
ScrollAlwaysVisible = true,
Padding = new Padding(0),
Margin = new Padding(0),
BorderStyle = BorderStyle.None,
Font = new Font(panel.Font.FontFamily, 9),
};
foreach (var item in commands) { list.Items.Add(item); }
if (list.Items.Count == 0) return;
list.SelectedIndex = 0;
ToolStripControlHost toolHost = new ToolStripControlHost(panel)
{
Size = panel.Size,
Margin = new Padding(0),
};
ToolStripDropDown toolDrop = new ToolStripDropDown()
{
Padding = new Padding(0),
};
toolDrop.Items.Add(toolHost);
panel.Controls.Add(list);
panel.Controls.Add(label);
toolDrop.Show(this, new Point(button.Bounds.Left + button.Owner.Left, button.Bounds.Bottom + button.Owner.Top));
// *Note: These will be "up values" that will exist beyond the scope of this function
int index = 1;
int lastIndex = 1;
list.Click += (sender, e) => { toolDrop.Close(); callback(index); };
list.MouseMove += (sender, e) =>
{
index = Math.Max(1, list.IndexFromPoint(e.Location) + 1);
if (lastIndex != index)
{
int topIndex = Math.Max(0, Math.Min(list.TopIndex + e.Delta, list.Items.Count - 1));
list.BeginUpdate();
list.ClearSelected();
for (int i = 0; i < index; ++i) { list.SelectedIndex = i; }
label.Text = String.Format("{0} {1} Action{2}", action, index, index == 1 ? "" : "s");
lastIndex = index;
list.EndUpdate();
list.TopIndex = topIndex;
}
};
list.Focus();
}
You can set it up and test like this, assuming you have a blank form (Form1) with a toolStrip that has 1 ToolStripSplitButton (toolStripSplitButton1) added:
public Form1()
{
InitializeComponent();
// Call DrawDropDown with:
// The clicked ToolStripSplitButton
// "Undo" as the action
// TestDropDown for the enumerable string source for the list box
// UndoCommands for the click callback
toolStripSplitButton1.DropDownOpening += (sender, e) => { DrawDropDown(
toolStripSplitButton1,
"Undo",
TestDropDown,
UndoCommands
); };
}
private IEnumerable<string> TestDropDown
{
// Provides a list of strings for testing the drop down
get { for (int i = 1; i < 1000; ++i) { yield return "test " + i; } }
}
private void UndoCommands(int count)
{
// Do something with the count when an action is clicked
Console.WriteLine("Undo: {0}", count);
}
Here is a better example using the Undo/Redo system from: http://www.codeproject.com/KB/cs/AutomatingUndoRedo.aspx
public Form1()
{
InitializeComponent();
// Call DrawDropDown with:
// The Undo ToolStripSplitButton button on the Standard tool strip
// "Undo" as the action name
// The list of UndoCommands from the UndoRedoManager
// The Undo method of the UndoRedoManager
m_TSSB_Standard_Undo.DropDownOpening += (sender, e) => { DrawDropDown(
m_TSSB_Standard_Undo,
"Undo",
UndoRedoManager.UndoCommands,
UndoRedoManager.Undo
); };
}
*Note: I did modify the Undo & Redo methods in the UndoRedoManager to accept a count:
// Based on code by Siarhei Arkhipenka (Sergey Arhipenko) (http://www.codeproject.com/KB/cs/AutomatingUndoRedo.aspx)
public static void Undo(int count)
{
AssertNoCommand();
if (CanUndo == false) return;
for (int i = 0; (i < count) && CanUndo; ++i)
{
Command command = history[currentPosition--];
foreach (IUndoRedo member in command.Keys)
{
member.OnUndo(command[member]);
}
}
OnCommandDone(CommandDoneType.Undo);
}
I'd suggest implementing the popup separately from the toolbar button. Popups are separate windows with a topmost-flag which auto-close when losing focus or pressing escape. If you code your own popup window that frees you from having to fit your behaviour to a preexisting model (which is going to be hard in your case). Just make a new topmost window with a listbox and status bar, then you are free to implement the selection behavior on the listbox like you need it.
Vs 2010 is a WPF application. If you are in the beginning of this application development than use WPF as a core technology. WPF drop down button is implemented in WPF ribbon. Source code is available on CodePlex.