I'm building a simple WinForms GUI that utilizes multiple (~50) of the same custom tool. I have the custom tools imported just fine and is part of my main form. My problem is I'd like a elegant way of handling each custom tool property (ie BackColor, Text, Visible, Enable, etc).
The tool is simple; it has a couple labels on it and each has some strings I use for storing values. I want to be able to control these labels on the custom tool at will without typing up huge amounts of code.
I have a textbox on my Form1 Design that controls the number of custom tools I want enabled and visible, but I cannot figure out a good method to control it. Do I explicitly put 50 custom tools into an array of some sort? If so, how? Since it's a custom tool, I cannot make CustomToolName[] customTools = new CustomToolName[50] for example. I've racked my head around this and can't figure out a way without making a really ugly and long solution.
Any suggestions? How would you do this?
EDIT: The List suggestion works. I'm able to control them by using a List<UserControl> CustomTool = new List<UserControl>(); and then adding each custom tool to the list. A small test of CustomTool[0].Visible = false; worked. I can control them with for loops as well. Thanks a lot.
This a classic usage of Polymorphism!!! Create an array or list of Base class and put all your controls inside. Run throuhg the list with loop and use properties. If you need to get sepecial properties that only specific classes have, verify type of current object. Something like that:
List<Control> controls = new List<Control>();
//Add controls
foreach (Control item in controls)
{
item.Visible = true;
if (item is MyCustomButton)
{
((MyCustomButton)item).CustomProperty = "123";
}
}
Related
I am quite new in C# programming. I would need your help in a question about creating own TextBox class (inheriting from the original one) in C#.
How could I create/inherit my own class from the generic TextBox class? My goal would be, that once I create 1 object from my class, the following attributes should be set automatically to all of the instances:
AutocompleteMode
AutocompleteSource
AutocompleteCustomSource
I would avoid to reuse my coding part every time, once I want to use the same text box, but in different forms and/or Tabs.
Thanks in advance,
Matyas
public class MyTextBox : TextBox
{
public MyTextBox()
{
// Change the background color
this.BackColor = Color.BurlyWood;
this.Font = new Font("Verdana", 16);
}
}
Build your project.
After that, look in your toolbox:
I didn't help you answer the auto complete, I have to make dinner now, but I wanted to give you the head start.
Search for auto complete tutorials, and it shouldn't be that hard for you to put a list box below your textbox. For this you want to create a custom User Control, and handle the keydown event of the textbox to filter your list.
Edit:
You also don't have to inherit from TextBox if you don't need to. What you can also do is combine two controls, like I do here with my LabelTextBoxControl, LabelComboBoxControl, LabelCheckBoxControl, etc. I didn't want to have to always drag two controls on to a form, so I combined a Label and other base controls.
If you want to see a sample open source project that has these type of user controls, (for Windows Forms) it is located here:
https://github.com/DataJuggler/DataJuggler.Win.Controls
P.S. > Always ignore the down voters on this site. This is how I learned to create user controls. Create what you want.
I have a tabbed interface - where the tabs will contain multiple nested controls. In particular, Panels containing panels, containing Grids, etc. Even in some cases additional Tab controls. (Yes it can be a little messy)
However, now I need to create some of these tabs dynamically at run time - and still reference the controls on them. So - for example lets say I have something like
Tab
Panel
TabControl
Tab
Grid
Graph/Chart
Panel
Buttons
I need to create multiple new tabs for each result DataTable I need to display(I do not know how many I have to create until Run Time - or I would just do this all at Design time).
I understand I will create these controls at RunTime - and I can do it easily for one additional instance - but then I start running into naming issues. I cannot add three GridControls (all called DataGrid) to the same form - and because C# is strongly typed I cannot create the variable names at Runtime. AND I also need to be able to reference these controls at some times.
So - I looking for possible solutions. I could create arrays for each type of control I am hoping to create. There are over 15 controls (some nested) for each tab - so it could get a little messy. But it would allow me to access each control - as I could use the Tag Property of the Tab page to allow me to access the various arrays. If I created the arrays to hold a max of 50 elements - it should be more than enough (I do not see users creating more then 5 or 10 of these extra tabs).
As I am somewhat new to C# and Visual Studio - I am wondering how others would handle this sort of task? How would you go about creating multiple instances of embedded (nested) controls on your form - knowing that you will have to reference them elsewhere in the code??
Any thoughts and pointers would be much appreciated.
Thank you
Bradley
Since there's an arbitrary amount of tabs, you'll need to manage them programmatically.
So - I looking for possible solutions. I could create arrays for each type of control I am hoping to create. There are over 15 controls (some nested) for each tab - so it could get a little messy. But it would allow me to access each control - as I could use the Tag Property of the Tab page to allow me to access the various arrays. If I created the arrays to hold a max of 50 elements - it should be more than enough (I do not see users creating more then 5 or 10 of these extra tabs).
That would indeed become messy, and is a recipe for bugs. Tag is a very useful property in the right circumstances, but generally you should avoid using it unless you have a good reason. You might inadvertently introduce cyclic references that leak memory. I find it's most suited for storing related metadata such as an ID(however, avoid storing value types in it, as they become boxed).
If you haven't already, I suggest adopting the same naming convention you use with variables for the names you set in the designer. As backing variables reflect those names, it should help you avoid name conflicts with properties, and keep your code more consistent. My team and I use the convention mSomeField for private fields and designer set names and it has served us well in avoiding most name conflicts over the years.
Start by designing a new UserControl that will serve as the content of the most common tab category, adding as much common layout as possible. Design additional UserControls for each additional tab category. Add accessors for each nested control you need to expose.
Note that the following code uses some C#6/7 features.
In practice, here's what that could look like:
// Code-behind for the UserControl representing the content of the 1st tab category.
class Cat1TabContent : UserControl
{
//...
// These all refer to controls created and named from the designer.
public Panel PrimaryPanel => primaryPanel;
public TabControl TabManager => tabManager;
public Panel SecondaryPanel => secondaryPanel;
//...
}
Managing tabs(in the main form and all other controls that have a TabControl):
//...
private List<UserControl> tabContents = new List<UserControl>();
void AddTab<T>() where T : UserControl, new()
{
var tab = new TabPage();
var tabContent = new T();
tabContents.Add(tabContent);
tab.Controls.Add(tabContent);
tabControl.Controls.Add(tab);
}
void RemoveTab(TabPage tab)
{
if (tab == null) throw new ArgumentNullException(nameof(tab));
if (tab.Controls.Count != 0 && tab.Controls[0] is UserControl tabContent) {
tabContents.Remove(tabContent);
}
tabControl.Controls.Remove(tab);
}
//...
Accessing nested controls:
// Form >> Tab Control >> 1st Tab >> 1st Child.
// This assumes AddTab<Cat1Content>() was called.
var firstContent = (Cat1Content)tabContents[0];
// Content >> PrimaryPanel; Enumerates over nested controls.
foreach (var child in firstContent.PrimaryPanel.Controls) {
// ...
}
// Content >> Tab Control; Enumerates over nested tabs.
foreach (var child in firstContent.TabManager.Controls) {
// This is safe, TabControl throws an exception if you try to add anything that isn't a TabPage.
var tab = (TabPage)child;
// ...
}
My program will prompt the user for a number, i.e. 25. The program will then start the "main form" with 25 controls (textbox). The 25 (or whatever number) of textboxes (or whatever control) will need to be formatted evenly. I will also need to be able to retrieve the text (or another property if I use another control) in order, from left to right and up to down. What is the best method of approaching this?
Using WPF MVVM. In a .XAML file, create a DataTemplate with the DataType of a ViewModel that will provide the binding for your TextBoxs, lets call this the TextboxViewModel. Then using a ItemsControl element with an ItemsSource of TextboxViewModel. You'll be able to instantiate as many TextBoxs as you want and be able to get the result by browsing through your list of TextboxViewModel.
Supposing you are using Windows Forms here.
Dynamically create the X controls and add them to the Controls collection of your form. To ease the access to them you can store their reference in a List and set some event handlers too, depending on your needs. You just need to calculate their positions while you add them.
If WinForms, this is exactly what the FlowLayoutPanel is for. Just add the controls to it and they will arrange themselves automatically, wrapping down to the next row as needed. As Mihai already suggested, you could also keep reference to those controls in a List.
Another option would be to use a TableLayoutPanel. It's a little more difficult to learn and use, but is much more flexible and powerful.
I am writing what i will call a survey system. The surveys will consist of a series of questions, split into sections. The survey definitions will be stored in a database. The questions can be of various types, that have different input methods and validation (e.g.textbox, numeric textbox, selectlist etc.) I am creating a set of user controls that map to different question types. I would like to completely abstract the survey rendering logic from the, survey definition logic. The way i thought i would do this is to use a dumb rendering control that simply takes a collection of controls and renders them out in a for each loop a super simplified version of what I am doing.
private void RenderControls()
{
foreach (UserControl CurrentControl in this.ControlsToRender)
{
MyPlaceholder.Controls.Add(CurrentControl)
}
}
This works in the sense that the correct controls get added to the place holder,however nothing renders. Presumably this is because it does not have the markup in the asxc file.
If i do something like this
private void RenderControls()
{
foreach (UserControl CurrentControl in this.ControlsToRender)
{
MyPlaceholder.Controls.Add(this.LoadControl("path to ascx file")
}
}
It will create the correct number of controls, but i don't want to instantiate the controls here because i would need to to set various properties based on cconditional logic i want to abstract away from this pure rendering layer.
what i want to do is instantiate the controls else where, set the relevant properties and then simply pass a collection of these into the rendering control to render out.
Is there a way to do this with user controls? Would i have to create custom controls instead?
.ascx usercontrols are difficult to make into distributable, portable components. There are ways of doing it, but they have a lot of restrictions and requires several steps. In the end it's not worth it.
Your best bet would be to make custom controls that encapsulate their own rendering.
You could do either, but it sounds to me like you might want to use custom controls.
You could use user controls by exposing properties on the user control.
MyPlaceholder.Controls.Add(CurrentControl)
by the time you get to the above line, the control has already been instantiated. just set those properties on instantiation and you should be set.
alternatively you could set the properties anytime before render
So what I am trying to do is to have a set UI with certain controls, and I want to have some class instance to fill in the values.
For instance:
Classes that can populate the UI:
Sharpen, Blur, Smear, ... : FilterBase
So whenever the user creates an instances of the above classes, I want to automatically fetch the values based on the UI controls, by using the class instance.
The UI has items like:
.Name (TextBox)
.Amount (NumericUpDown)
.Decay (NumericUpDown)
So if the current instance is s = new Sharpen(), I want to get s.Name to fill out UI.Name.
How do I do this in the best possible way? Elegancy, clarity, performance wise, etc.
I am gonna use reflection, but how do I store the instance of the object (Sharpen, ...) and be type safe? Should I use an System.Object instead? Or T?
public partial class ParamsUI
{
T CurrentFilter (?)
...
}
Also if I use T, how will I know what to cast it to?
Is there a better way?
Since this is using Windows Forms, the most flexible option would probably be to just use the Windows Forms PropertyGrid directly.
You can do a lot of work to customize the look and feel of this. See Getting the Most Out of the .NET Framework PropertyGrid Control for details.
Edit:
If you want to have a very flexible, easy option, and WPF is an option (hosted within an ElementHost), you could consider using WPF.
With this, you could host a UserControl containing nothing but a resource dictionary and a ContentControl.
You could then make a custom XAML file for each item you want to edit, and setup a data template in the resources specifying the mapping of which user control (xaml) to use for each element you want to edit. You can then just set the element to the user control's "DataContext" in code, and it will automatically wire everything up and "just work".