I'm a newbie in c# and probably going to ask a very easy question, but I've not been able to find anything on the web to help.
I have a tabControl with a TabPage which is containing a TextBox object; this object, when the event "Text changed" is invoked, will perform the change of the parent tabPage's name.
The textbox where I typed "text changed by me" has a method which is managing changing the name of the tabPage:
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (this.textBox1.Text != "")
this.tabControl2.SelectedTab.Text = this.textBox1.Text;
else
this.tabControl2.SelectedTab.Text = "(no name)";
}
Into the current page menu is contained a control to add a new page, which runs this method when the user click on it:
private void addNewPageToolStripMenuItem_Click(object sender, EventArgs e)
{
int numPagine;
string strPagine;
numPagine = this.tabControl2.TabCount;
strPagine = numPagine.ToString();
this.tabControl2.TabPages.Add("new page" + strPagine);
}
...and here is the output, which is expected since I'm just asking to add a new empty tabPage:
So, my question is: how can I make possible that when the user is clicking on "Add new page", rather than creating an empty new tabPage the program is rather creating a page like the first one (i.e. containing a textbox into the same position which has a method to change the text of the parent tabPage that I have just created?
Here is an example.
//..
// create the new page
TabPage tpNew = new TabPage("new page..");
// add it to the tab
this.tabControl2.TabPages.Add(tpNew);
// create one labe with text and location like label1
Label lbl = new Label();
lbl.Text = label1.Text;
lbl.Location = label1.Location;
// create a new textbox..
TextBox tbx = new TextBox();
tbx.Location = textBox1.Location;
tpNew.Controls.Add(lbl);
tpNew.Controls.Add(tbx);
// add code to the new textbox via lambda code:
tbx.TextChanged += ( (sender2, evArgs) =>
{
if (tbx.Text != "")
this.tabControl2.SelectedTab.Text = tbx.Text;
else
this.tabControl2.SelectedTab.Text = "(no name)";
} );
For more complicated layout you may want to consider creating a user control..
You also may want to create the first page with this code; the, of course with real values for text and positions!
For creating a UserControl you go to the project tag and right click Add-UserControl-UserControl and name it, maybe myTagPageUC. Then you can do layout on it like on a form. A rather good example is right here on MSDN
The problem is that is has no connection to the form, meaning you'll have to code all sorts of references to make it work..
I'm not really sure if you may not be better off writing a complete clonePage method instead. It could work like the code above, but would loop over the Controls of the template page and check on the various types to add the right controls..
It really depends on what is more complicated: the Layout or the ties between the pages and the form and its other controls..
Related
I have a C# Form application that has a TabControl in the main form. This TabControl is used to display multilple TabPages that contain a CustomControl. This CustomControl is just a Panel with a few buttons and a PictureBox.
Here is a picture of my app when it starts up. As you can see the tab control (the white area) is empty:
If the user clicks the "Add Image" button they are presented with an OpenFileDialog to select the image then the addImage method is called with the selected file:
private void doAddImage()
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = Constants.Global.IMAGE_FILE_FILTER();
if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string imageFileName = openFileDialog.FileName;
addImage(imageFileName);
}
}
private void addImage(string imageFileName)
{
// Create a new bitmap and image preview custom control. Then create a new tab
// page and add the custom control to the tab page.
Bitmap bitMap = new Bitmap(imageFileName);
ImagePreviewCustomControl previewControl = new ImagePreviewCustomControl(bitMap);
previewControl.Dock = DockStyle.Fill;
TabPage tabPage = new TabPage(Path.GetFileNameWithoutExtension(imageFileName));
tabPage.Controls.Add(previewControl);
// Insert the new tab page to the right of the currently selected tab page
int selectedTabIndex = imagesTabControl.SelectedIndex;
imagesTabControl.TabPages.Insert(selectedTabIndex + 1, tabPage);
imagesTabControl.SelectedIndex = selectedTabIndex + 1;
}
As you can see, in the addImage method I create the Bitmap, CustomControl, and TabPage and then insert it into the TabControl.
I start my application, click the "Add Image" button, everything works just fine.
Here is a picture with a tab page added:
While I am testing my app I don't want to have to add an image manually using the OpenFileDialog every time so in my constructor I just call addImage with some fixed image file name that I want to test with:
public ImageViewerApp()
{
InitializeComponent();
addImage(#"C:\MyImages\Calculator-3-icon.png");
}
The problem I am having is that when I try to add the image in my constructor it doesn't show up in the TabControl. The application starts up blank (like the first picture).
As stated above when the application is already running and I click the "Add Image" button it gets added just fine.
I found a property in the TabControl class called Created which states:
"Gets a value indicating whether the control has been created"
So to try and figure out what's going on I write the value of Created to the console just before I call addImage in the constructor. (I have a custom console for debugging my Form applications.)
public ImageViewerApp()
{
InitializeComponent();
TestConsole.WriteLine(imagesTabControl.Created);
addImage(#"D:\Development\Work\Other\Stardock\Start8\_downloaded\Calculator-3-icon.png");
}
The value of Created just before the call to addImage in the constructor is:
False
I put another console output inside the addImage method:
private void doAddImage()
{
TestConsole.WriteLine(imagesTabControl.Created);
OpenFileDialog openFileDialog = new OpenFileDialog();
...
...
}
The value of Created after the app has started and the user presses the "Add Image" button is:
True
Why is it that the TabControl is not Created inside my constructor (even after the InitializeComponent() call) and the once the application is running it is Created?
=UPDATE========================================================================
Based on the suggestion by Hans Passant I have added the following code to my addImage method:
int selectedTabIndex = -1;
if (imagesTabControl.TabCount > 0)
{
selectedTabIndex = imagesTabControl.SelectedIndex;
}
else
{
selectedTabIndex = imagesTabControl.SelectedIndex + 1;
}
imagesTabControl.TabPages.Insert(selectedTabIndex, tabPage);
imagesTabControl.SelectedIndex = selectedTabIndex;
This doesn't work.
===============================================================================
=UPDATE2=======================================================================
int selectedTabIndex = imagesTabControl.SelectedIndex;
if (imagesTabControl.TabCount == 0) selectedTabIndex = -1;
imagesTabControl.TabPages.Insert(selectedTabIndex, tabPage);
imagesTabControl.SelectedIndex = selectedTabIndex;
This causes the following Exception:
{"InvalidArgument=Value of '-1' is not valid for 'index'.\r\nParameter name: index"}
===============================================================================
=UPDATE3=======================================================================
I tried the folllowing code:
int selectedTabIndex = imagesTabControl.SelectedIndex;
if (imagesTabControl.TabCount == 0) selectedTabIndex = -1;
imagesTabControl.TabPages.Insert(selectedTabIndex + 1, tabPage);
imagesTabControl.SelectedIndex = selectedTabIndex + 1;
This one doesn't throw an exception but again no tab page added after calling
addImage in the constructor.
===============================================================================
=UPDATE4=======================================================================
I have kindof given up on adding an image in the constructor. So instead I am using an enum RunMode and a variable RUN_MODE of that type. Then, if RUN_MODE == RunMode.TESTI call a method to add a random image when I click the button. (The OpenFileDialog is not used. I just parse through all the image files in the fixed directory IMAGE_DIRECTORY.
enum RunMode { NORMAL, TEST }
private static string IMAGE_DIRECTORY = #"D:\\Work\Images";
...
...
private void doAddImage()
{
if (RUN_MODE == RunMode.TEST)
{
addRandomImage();
return;
}
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = Constants.Global.IMAGE_FILE_FILTER();
if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string imageFileName = openFileDialog.FileName;
addImage(imageFileName);
}
}
private void addRandomImage()
{
string[] allFiles = Directory.GetFiles(IMAGE_DIRECTORY);
List<string> imageFileNames = new List<string>();
foreach (string file in allFiles)
{
bool isImageFile = Constants.Global.IMAGE_EXTENSIONS.Contains(Path.GetExtension(file));
if (isImageFile)
{
imageFileNames.Add(file);
}
}
int randomIndex = new Random().Next(imageFileNames.Count);
addImage(imageFileNames.ElementAt(randomIndex));
}
This works. Now when I click the "Add Image" button during TEST_MODE I skip the
OpenFileDialog and just add a random image.
I would like to understand the issues with TabControl but at this point I just
need to continue development. My current solution works great.
As I person who like to understand everything I would like to use other people's
suggestions so I will keep monitoring this question for a solution.
===============================================================================
JonP's answer gave me the idea to just wait for the window handle to be created before inserting the tab, so I tried some events occuring between Form construction and Tab Control display.
I found it to work with both the Load or Shown events:
Right-click on the Form (the root, not child controls) in the Designer view > Properties > Events (flash icon) > Behavior > enter a method name for the Load or Shown event and confirm. To generate a Load event callback you can also double-click on the Form itself. This should generate something like this:
this.Load += new System.EventHandler(this.Form1_Load);
// or
this.Shown += new System.EventHandler(this.Form1_Shown);
Setup the tabs in the callback:
private void Form1_Load(object sender, EventArgs e)
{
// Add image (this will call imagesTabControl.TabPages.Insert(selectedTabIndex + 1, tabPage))
// This must be done on Load event because Insert requires
// the window handle, which is not ready in the constructor
addImage(#"path_to_image.png");
}
I have had this problem too and have found a workaround; I think it must be a bug with Insert():
Don't use Insert(), it usually does nothing, use Add() instead; this reliably adds a TabPage to the end of the collection.
After adding it swap it with the tab position where you actually want it.
imagesTabControl.TabPages.Add(tabPage);
// Now swap the two tabs:
imagesTabControl.TabPages[imagesTabControl.TabCount - 1] = imagesTabControl.TabPages[selectedTabIndex + 1];
imagesTabControl.TabPage[selectedTabIndex + 1] = tabPage;
Your mileage may vary of course :-)
Stop Press! An even better fix is to read the class's Handle member before calling Insert():
var handle = imagesTabControl.Handle;
Insert() works perfectly after you do that. Obvious isn't it???? The help page for Handle has this possible relevant Remark showing that the object actually does something when you read Handle:
The value of the Handle property is a Windows HWND. If the handle has not yet been created, referencing this property will force the handle to be created.
You could remove the TabControl from the designer and then instead just manually create the TabControl programmatically and add it to the Form immediately after InitializeComponent(). Then after you create the TabControl, call addImage(). Something like:
InitializeComponent();
TabControl tc = new TabControl();
tc.Location = new Point(10, 10);
tc.Size = new Size(100, 100);
tc.Visible = true;
tc.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right | AnchorStyles.Left | AnchorStyles.Top);
this.Controls.Add(tc)
addImage("c:\pathToImage\image.bmp");
I have a problem, im making me own custom SharePoint webpart.
everything is going well, but the problem is that i can't figure out how to change the location of the textboxes and labels.
anyone knows how i can change the locations?
I am trying to accomplish it in C#.
problem SOLVED.
With the help of component ids. set position of that particular component.
"How to change the location of the textboxes and labels"
In this example i'm using a Button (Action performed on Button Click) and i am also adding how to Generate a TextBox and a Label (When you press this Button).
Just because this is usually a common process within setting locations to a control.
private void button1_Click(object sender, EventArgs e)
{
// Settings to generate a New TextBox
TextBox txt = new TextBox(); // Create the Variable for TextBox
txt.Name = "MyTextBoxID"; // Identify your new TextBox
// Create Variables to Define "X" and "Y" Locations
var txtLocX = txt.Location.X;
var txtLocY = txt.Location.Y;
//Set your TextBox Location Here
txtLocX = 103;
txtLocY = 74;
// This adds a new TextBox
this.Controls.Add(txt);
// Now do the same for Labels
// Settings to generate a New Label
Label lbl = new Label(); // Create the Variable for Label
lbl.Name = "MyNewLabelID"; // Identify your new Label
// Create Variables to Define "X" and "Y" Locations
var lblLocX = lbl.Location.X;
var lblLoxY = lbl.Location.Y;
//Set your Label Location Here
lblLocX = 34;
lblLoxY = 77;
// Adds a new Label
this.Controls.Add(lbl);
}
}
Note: This is just an example and will not work after postback.
I hope this answers to your and everyone's question.
I'm looking for solution for my problem. I want to change location for tabcontrol's TabButtons or add control assigned to tabpage but outside TabControl. TabPages are added dynamically. Example:
Form1___________ _ [] X
_______________________
Some TabPage content
Tab1 | Tab2 | Tab3 | < >
TextBox assigned to Tab's
________________________
So if I change tabs by clicking on Tab1,Tab2,Tab3 TabPage + TextBox content should change depending on Tab. The first idea was to put TabButtons on bottom and add ArrayList what contains TextBox content, catch TabControl change tab event and change TextBox content, but there was an issue with editing and adding that content. In few words: I wan't to put TabButtons between 2 controls(for example between two textboxes).Do you have any ideas?
If I understand what you're asking for... You want when you click on a tab, it controls two different things? Like two different text boxes?
If that is true, you should be able to do it like this.
foreach (thing in your ArrayList)
{
TabPage tabPage = new TabPage("Name of tab"); // Add a new tab page
RichTextBox rtb = new System.Windows.Forms.RichTextBox();//RTF box
TextBox tb = new System.Windows.Forms.TextBox(); //Text box
//Set up size, position and properties
rtb.LoadFile("some/Path/to/a/file");
//set up size, position of properties
tb.Text = "Some text I want to display";
tabPage.Controls.Add(rtb); //Add both boxes to that tab
tabPage.Controls.Add(tb);
tabControl1.TabPages.Add(tabPage); //Add that page to the tab control
}
Only thing you should have to mess around with is the layout. And make sure to have the tabControl added with the designer.
you can create your own textbox class which inherits from textbox class :
class MyOwnTextBox:TextBox
{
public int parent_tab;
}
So you can add your textbox by assigning a parent_tab id to them . so in tab button click event , you can do something like that :
foreach(MyOwnTextBox txt in this.Controls)
{
if(txt.parent_tab==1) txt.visible=false;
}
You could also place the tabs on the left or the right side of your tab control. That is not perfect, but would come closer to your idea than placing them above or below the tab control.
You can add a new tab page dynamically like this
tabControl1.TabPages.Add("My new Tab");
I'm not sure I understand exactly what your trying to do. If you want to change the tab from another object, just use:
TabController.SelectTab(0);
If you want to remove a TabPage and add it to another, use:
TabController.Controls.Remove(TabPage1);
TabController2.Controls.Add(TabPage1);
Edit: From further read, I think you want something like this:
this.TabController.ControlAdded += AddLinksToBottomOfTabs;
public void mainSettingsTabController_ControlAdded(object sender, ControlEventArgs e)
{
//Create label with e.Control.Name as the title and
//add it to wherever you want it added.
}
For my C# Windows Form Application, I have created a flowlayoutpanel that contains several panels. Inside the panel, I have a button "Clear" for each and every single panel.
How do I write the event handler for the code for the button "Clear" such that once I have click the button, the panel would sort of be "Removed" from the flowlayoutpanel.
This is a short part of the code of the adding of panels to the flowlayoutpanel.
nFlowPanel.Controls.Add(createNotificationPanel());
nFlowPanel.Controls.Add(createNotificationPanel());
nFlowPanel.Controls.Add(createNotificationPanel());
nFlowPanel.Controls.Add(createNotificationPanelImpt());
nFlowPanel.Controls.Add(createNotificationPanelImpt());
and this is the code for the button "Clear"
Button btnClear = new Button
{
Text = "Clear",
Name = "btnClear",
Location = new Point(416, 17)
};
p.Controls.Add(btnClear);
btnClear.Click += new EventHandler(buttonClear_Click);
So what should i write in the following method to have the effect of removing e.g. the second panel that was added in the first part of code I have written?
void buttonClear_Click(object sender, EventArgs e)
{
throw new NotImplementedException();
}
EDIT
the code for creating my panel is
var p = new Panel
{
BorderStyle = BorderStyle.FixedSingle ,
Size = new Size(506,100),
Name = "notifyPanel"
};
and the code for creating my FlowLayoutPanel is
var nFlowPanel = new FlowLayoutPanel
{
FlowDirection = FlowDirection.TopDown,
WrapContents = false,
AutoScroll = true,
Size = new Size(530, 377),
Location = new Point(13, 145)
};
and the code for my button clear is
void buttonClear_Click(object sender, EventArgs e)
{
var button = (Control)sender;
var panel = button.Parent.Controls["notifyPanel"];
panel.Dispose();
}
however it gives the error
Object reference not set to an instance of an object.
on the panel.Dispose() line.
anyone can help?
The Controls.Remove() method is very dangerous, it doesn't dispose the control. Which will live on, moved to the so-called parking window, using up both Windows and managed resources. After a bit less than 10,000 times doing this it crashes your program when Windows is no longer willing to let you create any more windows.
Call the control's Dispose() method instead. That also automatically removes the control from its container.
void buttonClear_Click(object sender, EventArgs e)
{
var panel = nFlowPanel.Controls["notifyPanel"];
panel.Dispose();
}
You can do it like this:
nFlowPanel.Controls.Remove((sender as Button).Parent);
I will suggest you to use List for this. Before adding Panels in the FlowLayoutpanel, add them in the List. Then just remove the indexed panel from the flowlayoutpanel.
Panel pnlTemp = (panel)list[index];
nFlowPanel.Controls.Remove(pnlTemp);
To get the index of the button you have to add your buttons also to your list and after clicking any button, search the button in the list and get the index of the button where it is saved in the list. If my code is unclear, let me know. but I feel your task is that complex. I am not sure but this link may be of some help.
Hope it helps.
I would like to be able to add multiple label controls to a panel and display them on an onlick event. The code I have does what I want it to the first time, then the label is simple replaced with a new label on the second onlick event and so on.
Here is what I have so far:
private void createTaskLabel(Guid GoalID, string Goal)
{
Label taskLabel = new Label();
taskLabel.ID = GoalID.ToString();
taskLabel.Text = Goal.ToString();
TaskPanel.Controls.Add(taskLabel);
}
So, for instance, this creates a new label with a uniqueID (handled elsewhere) and places it within the panel and displays it. When the onlick fires again, the same label is simply replaced instead of a new one appearing below it.
Dynamically created controls are not persisted after a postback. You need to keep track of how many controls you have generated and regenerate ALL of them each time for this to work how you want. Basic implementation:
List<string> LabeIDList = new List<string>();
override SaveViewState(..)
{
if (LabelIDList.Count>0) {
ViewState["LabelDIList"]=LabelIDList;
}
base.SaveViewState(..)
}
override LoadViewState()
{
base.LoadViewState();
if (ViewState["LabelIDList"]!=null) {
LabelIDList = (List<string>)ViewState["LabelIDList"];
}
}
override void OnLoad(..)
{
foreach (string id in LabelIDList)
{
// Make a function essentially like your code in createTaskLabel,
// you can use it there too
RecreateControl(id);
}
}
private void createTaskLabel(Guid GoalID, string Goal)
{
...
// save the ID you just created
LabelIDList.Add(taskLabel.ID);
}
I just realized that these are Labels you're creating - so actually you should probably be keeping track of the Text instead of the ID. If the actual ID is important for some reason then keep track of them both, use a List<Tuple<string,string>> for that. More typical situation is creating input controls, though, in which case ASP.NET will persist the data that a user entered as long as you re-create them on or before OnLoad (assuming ViewState enabled).
Dim lbl As New Label
Dim lbl1 As New Label
Dim txt As New TextBox
Dim txt1 As New TextBox
lbl.Text = "Name"
lbl1.Text = "class"
Me.Controls.Add(lbl)
Me.Controls.Add(txt)
Me.Controls.Add(lbl1)
Me.Controls.Add(txt1)
Have you look at Repeater control? It might make it a bit easier to implement I think.
At least you don't need to worry about the label control creations yourself.