I have data for several days. I want to plot that data, one day in one tab page. The tab page can change dynamically corresponding with the number of the day.
Previously, I always make a static page in the design mode. I use ZedGraph control to plot the data. I decided how many pages that I need. Then it means the number of pages is static.
Now, I need more dynamically. I want the number of tab pages can change dynamically. Each page has similar controls inside it. How to do that? I have no clue about it.
First you create a new class of type UserControl. These are meant to act as containers for controls. Then you put everything on it you need in your typical tabpage including the ZedGraph control..
Also write your logic there.
Now, when you need another page you create one and add a new instance of the UC class you have created.
You should think a little about dependencies with the rest of you application, including the deleting of the pages; a page controller class comes to mind that can handle these things, if necessary.
You could also subclass a tabpage but going the UC way gives go an additional layer of independency; you could place the same UC on a form or inside a container control..
Related
Ok, this is going to be a 1000ft long question, but there's a lot to cover so here goes:
I am creating a paged items control, the purpose of which is to display very large collections in a paged format. I've created a repository on GitHub which can be found here. I have removed any styling for simplicity.
Upon starting the application, it looks like this:
This is pretty straightforward really, there's navigation buttons, an items per page selector but that isn't really important. The problem here is when you click the button "Open New Window".
This will open a new MainWindow, but on the first window, the collection disappears, as shown below:
The image above shows the old window in front, as you can see, there is no list of content as there is on the new window.
So, after smashing my head against a wall for a couple of hours, I am in need of assistance. I'll provide an overview of how the project is structured.
AnagramPagedItemsControl
The control being used for displaying the content is a custom control called AnagramPagedItemsControl, it is responsible for handling navigation between pages. I think the key property here is the PagedCollection.
The PagedCollection dependency property holds the collection which is bound to the Models property in the TestItemsViewModel.
TestItemsViewModel
This is the DataContext of the MainWindow, each window instance should create it's own view model. The CreateTestItems() method is responsible for creating the list of test items.
LazyPagedCollection
The purpose of this collection is to encapsulate the logic of a paged observable collection, it only loads pages when they are needed, hence the laziness.
It exposes methods like NextPage which are called in the AnagramPagedItemsControl when the user clicks on the various navigation buttons. The view model can also call navigation on the LazyPagedCollection, this allows the view model to call navigation without having to go through the view to do it.
TL;DR
When I create a new Window, the content of the previous window disappears. The problem is almost certainly with the control however I am stuck as to how to fix the problem.
This is quite a large problem to look at so I'd be very grateful for anyone who can look into it. Again, the source code is here, please feel free to suggest alternatives or pick out anything that I may have overlooked.
Had some time to spare, so:
The problem is the setter for the CollectionView property in the style for AnagramPagedItemsControl in generic.xaml.
This does not instantiate a new ListBox every time the style is applied; it will just create the one ListBox, the first time the style is created, and use that value over, and over again. So in effect, every instance of MainWindow shares the same ListBox.
You can see this by setting the Tag property of PART_CollectionView to (for instance) "1" in SetupBindings(ItemsControl PART_CollectionView). When you open a new window, you'll see that PART_CollectionView.Tag contains the same value you previously assigned.
EDIT: I needed to skip control creation during post back -- see my answer below.
I'm working on a very basic front end to a simple tool and I wanted to present some data in a much more sorted and useful way, instead of making one huge wall of text. I found this tutorial on building a simple tabbed interface using MultiView, but have run into a bizarre problem. I can't use Ajax tabs because of legal hissy fits over 3rd party software.
My webpage is a basic ASP.NET page with a user control plopped in the middle of it. In this control's ascx file, I defined the Menu (empty) and the MultiView (also empty) so that I can dynamically populate the tabs with content driven from an external file.
When the default page's OnInitComplete function is called, I call through to the user control to load the data file, then build out the tabs and the view content based on the loaded data. I tried doing this from PageLoad, PreInit, and CreateChildControls, but I kept getting an errors saying that I was setting the the MultiView's active view index at an invalid time (and also that there were 0 views despite the fact I just added a bunch of them):
ActiveViewIndex is being set to '0'. It must be smaller than the
current number of View controls '0'. For dynamically added views, make
sure they are added before or in Page_PreInit event.
But OnInitComplete appears to work just fine, so I went with that.
I iterate over the loaded data (multiple lists of strings), and for each list, I add a MenuItem with the list's title to the Menu and a View to the MultiView. The View is populated with a table->row->cell as in the above tutorial. In the cell, I add the list title and a CheckBoxList data bound to the list of strings.
So far so good, but when I click on a tab (or one of the checkboxes, etc) and there is a postback or something like that (the screen flashes as the site redraws itself), there is now a duplicate set of MenuItems immediately after the original. Each time I click on a tab or checkbox, another set of menu items are added.
I clear the MenuItem's Items list prior to building the controls and I verify that the controls hierarchy is structurally as expected after the control construction. Yet when one of my callbacks is called, my MenuItem list magically has some items added to it. None of my other controls appear affected at all. As a hack, I can remove the duplicates manually in my menu's OnMenuItemClick event, but I'd have to do the same in any of the callbacks I receive. Obviously I'd rather prevent this from happening. This has me stumped and I haven't been able to find anything online about it. Why would one set of controls have some content duplicated, yet every other control maintain its state correctly? My code is really simple so there isn't a way to add additional menu items without also adding the views. Anyway, there are a correct number of items prior to clicking on the tab/checkbox, an additional set immediately following in the callback.
This is my first time using ASP.NET, so I'm learning as I go. :) Thanks!
My problem was that I was not testing for postback before creating the controls. The code below is working for me.
In my user control's code behind:
protected void OnInitComplete( EventArgs e )
{
if( !Page.IsPostBack )
{
CreateMyControls();
}
}
I currently am working on a WinForm project in which there are several different tabs. Within each tab there are various controls such as buttons, sub-tabs, text-boxes, ect...
I need to consolidate the overall application which involves taking certain controls from one tab and moving them to another. When I first tried doing so, I simply copy and pasted the controls. As you can imagine this didn't work due to the fact that I didn't move the properties with the controls, I really just created NEW ones on a different tab. Therefore when I complied the code, nothing worked because there was no code assigned to the new controls.
When I tried it again, this time I CUT and paste which also maintains the same properties as the old controls (specifically the reference name in the code), so as far as I can tell, the code should identify the controls by name, and apply the same actions. However, when I compile the code, the application successfully builds but the controls do not perform any actions.
At this point I am not sure what to do...
Use the Document Outline.
View... Other Windows... Document Outline.
Select the required component and drag it from one tab page to the other in the tree control. I did this and the actions are preserved in the new tab page.
Drag the item out of the tab control and onto the form itself. Change to the other tab. Then drag the item into that tab. It is essentially 2 drag moves, but since you do not ever cut, all code linking is maintained. If your tab control takes up the entire form, simply make it smaller while you do the preceding steps and then make it large again when you are done.
When you "cut" the controls, you sever the connections between the controls and their respective events. When you "paste" them again, they're not hooked up to the events anymore, so they don't appear to do anything.
The "event" methods should still be present in your code, but you'll have to manually go through and subscribe each event to each control again (via the Properties window).
Alternatively, revert those changes, then open the .Designer.cs file and look for something like this:
this.tabPage1.Controls.Add(this.dataGridView1);
Which (for example) places dataGridView1 inside tabPage1.
If you wanted to move the DataGridView to another TabPage, you could just change this.tabPage1 in the above code to this.tabPage2.
this.tabPage2.Controls.Add(this.dataGridView1);
Then you can flip back over to the designer view and move the control around to wherever you want it within the TabPage.
I just tested it. What is happening when you cut and paste your controls, you losing the wiring of the events. What you need to do after cut and paste is to go to control properties-events, find the event in question and on the right, select a method that you want to handle that event.
This will cut them from the first TabPage and paste them on the second, i think you can do this as often as you want. And with a small change you can make it a truly copy.
hope it helps
private void ControlsToTabPage(TabPage from, TabPage to)
{
Control[] ctrlArray = new Control[from.Controls.Count];
from.Controls.CopyTo(ctrlArray, 0);
to.Controls.AddRange(ctrlArray);
}
I'm working on a WinForms C# UserControl that will function like a Delphi PageControl (a panel that swaps between multiple pages, but only one page is visible at a time). I want the user to be able to drop components onto a page at design time.
I have it mostly working, but am having trouble with the serialization. I'm using a List for my pages and am serializing out this list like so:
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<Panel> Pages { get { return _pages; } set { _pages = value; } }
And I allow the user to interact with the panels using a custom Designer descended from ParentControlDesigner. I have the Designer call a SetDesigner method of the PageControl and the PageControl stores a reference to the designer so that it can call EnableDesignMode on any panels it creates.
My problem is, EnableDesignMode's name parameter. I'm not sure what to put there. These panels are elements of a List and don't have names that can be referenced by pageControl1.Name. I've tried setting the names to "Pages[0]", "Pages[1]", etc. but that crashes during serialization.
Any ideas on how I should handle this? Long story short: How can I have a dynamic list of child panels that the user can interact with at design time and have the designer remember the settings and child controls on these panels?
Edit
To be more clear about what I'm trying to do, I want to create a UserControl that acts as a Panel, but has multiple "pages". When the user changes its PageIndex property, it switches to an entirely different Panel and the user can add different controls. Something that could be used for a wizard or something where one panel could have multiple views.
I am looking to create a windows form. This form is to display groups of data separately and I was aiming to do so in the same form window rather than have multiple windows open.
For example each group of data is defined by a Job#. A user will want to review X different Job#'s at a time (Range would usually be 1-5, but could be more). I would like to have 4 dataGridViews for each Job# plus various identifying and summed data in text boxes. Initial Example Concept
I was looking into using TabPages/Tab Controls. My initial idea was to have a user click the different tabs to view the data for those jobs that they have pre-selected. However these tabs don't seem to behave like classes from what I can see. Is there perhaps a better way to go about this or some way to have the tabs act like classes? So that each tab has a Job TextBox, 4 Different DataGridViews, etc. So that I can easily create and display any number of jobs?
For example each Tab would have 4 dataGridViews, maybe 8 Text Boxes, Standardized Labels and a Standardized layout.
So would using tabs be a good idea? Using some other WinForm control?
There are at least 2 solutions here:
Create a custom Panel holding all the controls you want (TextBox, DataGridView, ...), design it so that it looks best to you. Then add each of that Panel to each of TabPage of your TabControl.
Create a new custom TabPage and add the custom TabPage to your TabControl.TabPages instead of the standard TabPage.
I think the second approach can be done if you can initialize everything using code (not by drag-n-drop) because to get the drag-n-drop supported, you may need to write a custom TabControl. So the first approach is feasible and OK. You can replace the Panel by a Form, set the Form.TopLevel = false, you can add that form to any container control. Using Form, you can benefit the easiness of drag-n-drop to design and organize your controls.
Here is a little of code which may help you figure out:
public class TabPageClient : Form {
public TabPageClient(){
InitializeComponent();
Dock = DockStyle.Fill;
TopLevel = false;
FormBorderStyle = FormBorderStyle.None;
}
//.... other code
//I think this is important
//You should define some Properties and methods to control the data flowing into and out from your TabPageClient.
//You can also support some method to add/remove the controls dynamically.
}
Take the drag-n-drop requirement into account, I think using UserControl may be better:
public class TabPageClient : UserControl {
public TabPageClient(){
InitializeComponent();
Dock = DockStyle.Fill;
}
}
For UserControl, you should place the class in a separate *.cs file, compile your project and then you can see it in the ToolBox window (at the very top). Just drag and drop it like as other controls.
Personally, I prefer grid-detail views. Something we make heavy use of in our software. The form has a SplitContainer in vertical alignment.
In the top panel, you add a list of some kind (ListBox, ListView, DataGridView...any control into which you can load a list and then react to selection).
In the bottom panel, you have yet more options. Simplistically, you could use a TableLayoutPanel and then setup rows/columns to provide whatever arrangement of embedded controls you like, such as your array of grids to display data. I suppose in keeping with your model, you'd have a single ColumnStyle and four (4) RowStyles. Then in each row, add a grid.
This way, you keep it all in one place...one "screen", if you like (no tabs to flip through). You react to the selection in the list to decide what data to display in the grids. You then just need a bit of code (a class I would hope) that interfaces between the class(es) that provide the data, and the form controls that display it (the grids).
An additional benefit here is that with the list presentation, you can have a lot more than five (5) jobs in play at any one time. In fact, with scrolling, as many as you like (not limited to the number of tabs before the display goes to pot).
Also, you would be re-using the grids. Simply refreshing the displayed data for the list item selected. You could even pre-load and cache it all if freshness is not an issue.
Also consider binding directly to data source if that's an option.