running this in linux, all fine.
running in Windows making trouble, all tabs are twice present and i can't insert content into tabs.
https://abload.de/img/unbenannt82jfh.png
someone knows about?
public class CalendarApp : Form {
public CalendarApp() : base() {
tabControl = new TabControl();
tabPages = new TabPage[3];
string []tabText = {"Haupt", "Kontakte", "Termine"};
for (int i=0; i<3; i++) {
Console.WriteLine ("Ahhh windoof: {0}", i);
tabPages[i]=new TabPage();
tabPages[i].Text = tabText[i];
tabPages[i].TabIndex = i;
tabPages[i].Parent = tabControl;
tabControl.Controls.Add(tabPages[i]);
}
FlowLayoutPanel mainPanel = new FlowLayoutPanel();
tabControl.Size = new Size(mainPanel.Size.Width+8, mainPanel.Size.Height+24);
this.Text = "CalendarApp";
Controls.Add (tabControl);
AutoSize=true;
this.FormBorderStyle = FormBorderStyle.FixedSingle;
ResumeLayout(false);
PerformLayout();
}
[STAThread]
public static void Main() {
Application.Run(new CalendarApp());
}
private TabControl tabControl;
private TabPage []tabPages;
}
ok, i've yust found the Problem:
// tabPages[i].Parent = tabControl; <-this Line is causing the Problem
tabControl.Controls.Add(tabPages[i]);
The Set Up:
I have a System.Windows.Forms class called ProjectForm. In this form I have a TabControl called tabControl. When the form is initialized, so is the tabControl; however, the tabControl has no TabPages loaded. TabPages are created and loaded at runtime on demand when a user selects an item in a treeView control.
Example Call From ProjectForm:
this.tabControl.TabPages.Add(PageLibrary.CallStackPage(e.Node.Name, e.Node.Text));
(TabPageLibrary) as PageLibrary Class reference
class TabPageLibrary
{
private TabPageToolBar tabToolBar = new TabPageToolBar();
public TabPage CallStackPage(string name, string label)
{
TabPage tabPage = NewProjectPage();
tabPage.Name = "STACK:" + name;
tabPage.Text = label;
tabPage.Tag = name;
tabPage.ImageKey = "viewstack.png";
return tabPage;
}
private TabPage NewProjectPage()
{
TabPage tabPage = new TabPage();
tabPage.Padding = new Padding(3);
tabPage.UseVisualStyleBackColor = true;
tabPage.Controls.Add(this.tabToolBar);
return tabPage;
}
}
Problem
When the TabPage is loaded into the control at runtime - no image shows on the tab. the TabControl.ImageList is set to an ImageList that does contain the image I am referencing. Subsequently, the tree control is referencing the same ImageList and the images do show in the tree control.
I would be grateful for any suggestions, solutions or blinding flashes of the obvious you could share.
--Peace
+++ FIX UPDATE ++++
With DonBoitnott's insight - I was able to get these images to properly render with minor refactoring.
New Example Call From ProjectForm:
TabPage page = PageLibrary.NewProjectPage();
this.tabControl.TabPages.Add(page);
page = PageLibrary.CallStackPage(e.Node.Name, e.Node.Text, page);
Refactored (TabPageLibrary) as PageLibrary Class reference
class TabPageLibrary
{
private TabPageToolBar tabToolBar = new TabPageToolBar();
internal TabPage CallStackPage(string name, string label, TabPage page)
{
page.Name = "STACK:" + name;
page.Text = label;
page.Tag = name;
page.ImageKey = "viewstack.png";
//TODO: Load Additional CallStack Controls
return page;
}
internal TabPage NewProjectPage()
{
TabPage tabPage = new TabPage();
tabPage.Padding = new Padding(3);
tabPage.UseVisualStyleBackColor = true;
tabPage.Controls.Add(this.tabToolBar);
return tabPage;
}
}
Thanks again #DonBoitnott, works like a champ!
The code for TabPage tells us that the page will only pick up the image list if it has a parent from which to pull it. Odd, but you can prove it. Here it is:
//From TabPage.cs
public string ImageKey
{
get
{
return this.ImageIndexer.Key;
}
set
{
this.ImageIndexer.Key = value;
TabControl parentInternal = this.ParentInternal as TabControl;
if (parentInternal != null)
{
this.ImageIndexer.ImageList = parentInternal.ImageList;
}
this.UpdateParent();
}
}
That means you have to ensure you parent the tab page before you attempt to assign the image list key.
So, with that in mind, here's a bare bones example you can put in any form (you'll need to supply your own art, of course):
public partial class Form1 : Form
{
private ImageList _imgList;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(Object sender, EventArgs e)
{
foreach (TabPage p in tabControl1.TabPages)
p.Dispose();
_imgList = new ImageList();
_imgList.Images.Add("image0", Properties.Resources.ImageOne);
_imgList.Images.Add("image1", Properties.Resources.ImageTwo);
_imgList.Images.Add("image2", Properties.Resources.ImageThree);
tabControl1.ImageList = _imgList;
}
private void button1_Click(Object sender, EventArgs e)
{
Int32 count = tabControl1.TabPages.Count;
if (count < 3)
{
TabPage p = new TabPage();
p.Name = "page" + count;
p.Text = "page" + count;
tabControl1.TabPages.Add(p);
p.Parent = tabControl1;
p.ImageKey = "image" + count;
}
}
}
I asked a question here
and im unsure how to add the class the user gave me - I have just created a new class file then pasted in the class and i dont know how to apply that to the richtextbox?
Heres how my richtextbox is found... I have a richtextbox for each tabpage opened in my text editor i created a new textbox on the newtab void
public RichTextBox GetRichTextBox()
{
RichTextBox rtb = null;
TabPage starting = tabControl1.SelectedTab;
if (starting != null)
{
rtb = starting.Controls[0] as RichTextBox;
}
rtb.TextChanged += new EventHandler(txtBox_TextChanged);
rtb.MouseClick += new MouseEventHandler(rtbh_MouseClick);
//rtb.Select(rtb.Text.Length, 0);
rtb.Font = new Font(rtb.Font.FontFamily, 12);
rtb.Select(rtb.Text.Length, 0);
return rtb;
}
The class the user gave you inherits from RichTextBox - so when adding textboxes to your text editor, add this custom class. And for your function of finding textboxes, use the custom control. So change the above function to this:
public HighlightableRTB GetRichTextBox()
{
HighlightableRTB rtb = null;
TabPage starting = tabControl1.SelectedTab;
if (starting != null)
{
rtb = starting.Controls[0] as HighlightableRTB;
}
if (rtb != null)
{
rtb.TextChanged += new EventHandler(txtBox_TextChanged);
rtb.MouseClick += new MouseEventHandler(rtbh_MouseClick);
//rtb.Select(rtb.Text.Length, 0);
rtb.Font = new Font(rtb.Font.FontFamily, 12);
rtb.Select(rtb.Text.Length, 0);
}
return rtb;
}
The actual adding of the custom textbox should probably look something like this:
TabPage tabPage = new TabPage("Test");
tabPage.Name = "Test";
tabControl1.TabPages.Add(tabPage);
HighlightableRTB customTextBox = new HighlightableRTB();
tabControl1.TabPages["Test"].Controls.Add(customTextBox);
I'm trying to do a notepad with chrome-like tabs on it. I have a "New Page" button on my page. When I click on it, it creates a new tabpage with a richtexbox on it. The richboxes are created like this
public void yeni()
{
//create a new tabpage
TabPage newPage = new TabPage("Not-" + (tabControl1.TabPages.Count + 1));
//create a new richtexbox
RichTextBox rtb = new RichTextBox();
int rtbname = tabControl1.TabPages.Count + 1;
rtb.Name = "richTextBox" + rtbname.ToString();
rtb.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right | AnchorStyles.Left | AnchorStyles.Top);
rtb.BorderStyle = BorderStyle.None;
rtb.Width = 778;
rtb.Height = 395;
rtb.Location = new Point(0, 4);
rtb.HideSelection = false;
rtb.Font = new Font("Lucida Console", 9.75f);
rtb.ForeColor = Color.Maroon;
//add rtb to the tabpage
newPage.Controls.Add(rtb);
tabControl1.TabPages.Add(newPage);
//make the new created tab the selected one
tabControl1.SelectedTab = tabControl1.TabPages[tabControl1.TabPages.Count - 1];
//selectedRtb.Text = null;
openFileDialog1.FileName = null;
}
Now I create a RichTextBox and the name of that rtb is richTextBox*indexofthetabhere*. So if I'm working on the second tabpage, the name of the rtb is "richTextBox2". Now what I'm trying to do is I want an textchanged event for the richtextbox on the selected tabpage. I'm getting the selected richtextbox with this code here.
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
selectedone = "richTextBox" + (tabControl1.SelectedIndex+1).ToString();
selectedRtb = (RichTextBox)tabControl1.SelectedTab.Controls[selectedone];
textBox2.Text = selectedone;
}
Now here I get the selected tab index and i get the rtb name then I get the selected rtb as "selectedRtb". Now I can't make a textchanged event for this. I don't know what to do actually. I tested if the above code was working and yes I get the rtb names right. But I can't use them because I don't know how to do.. Thanks for the help.
public void yeni()
{
//....
RichTextBox rtb = new RichTextBox();
rtb.Name = "richTextBox" + selectedTabPageIndex.ToString();
rtb.TextChanged += rtb_TextChanged;
//....
}
void rtb_TextChanged(object sender, EventArgs e)
{
RichTextBox rtb = (RichTextBox)sender;
if (rtb.Name == "richTextBox" + selectedTabPageIndex.ToString())
{
//rtb is selected page richtextbox
//......
}
}
You don't know how to create events? Or you can't access something while knowing it's name (use reflection)?
Alright I solved my problem. Here is the answer;
selectedRtb.TextChanged += (bs, be) =>
{
//whatever you want to do
};
Simply added this to my code after I created the rtb, and it worked. Thanks to everyone who helped.
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.