I have a project to which I delegate the function of creating a library of (static)? classes used in all my other projects. It is referenced via solution in these cases.
For instance, I have an extension which creates checkboxes within a given GroupBox's panel, and that works great:
public static void PreencheCheckboxesPanel(this Panel p, List<CheckBox> listaCheckBoxs) {
var count = 0;
listaCheckBoxs.ForEach(
i => {
i.Location = new Point(10, 10 + ((count) * 25)); //"dynamic" and not-so-effective resizing here
i.AutoSize = true;
count++;
});
p.Controls.AddRange(listaCheckBoxs.ToArray());
}}
Problem is, I need to insert a static checkbox on the top of the list, which will receive a method to (un)?check all the checkboxes below.
So my code will become
internal static CheckBox CKB_ancora = new CheckBox(){};
public static void PreencheCheckboxesPanel(this Panel p, List<CheckBox> listaCheckBoxs) {
var count = 0;
if (adicionaAncora) {
CKB_ancora.Text = textoAncora;
CKB_ancora.CheckedChanged += (sender, args) => {
ChecaCheckBoxes(p, CKB_ancora.Checked);
};
listaCheckBoxs.Insert(0, CKB_ancora);
}
listaCheckBoxs.ForEach(
i => {
i.Location = new Point(10, 10 + ((count) * 25)); //"dynamic" and not-so-effective resizing here
i.AutoSize = true;
count++;
});
p.Controls.AddRange(listaCheckBoxs.ToArray());
}}
where ChecaCheckBoxes is another
public static void ChecaCheckBoxes(this Panel b, bool checkStatus = true) {
var listaCheckBoxs = (from Control c in b.Controls where c is CheckBox select (CheckBox)c).ToList();
listaCheckBoxs.ForEach(
i => {
i.Checked = checkStatus;
});
}
and CKB_ancora needs to be a solution-wide recognized object.
The reason? I have another extension named GetSelectedCheckBoxes which will be used to return all the checked ... ah... checkboxes within the groupbox. And, in order to make sure that the "anchor" (I call it like this, since I don't have a name to a (un)?check-all checkbox) won't be returned as well.
If I run this code, it will compile, but... will run accross an InvalidOperationException at Application.SetCompatibleTextRenderingDefault, right at Main(); Apparently, a control cannot be created/instantied before this method is run at mainpoint, which is the exact definition of "static".
Question: Knowing that I NEED a way to keep this particular check solution-wide visible... How do I do it?
Unfortunately, you have not provided a good, minimal, complete code example, and lacking enough context it is very hard to provide good, specific advice.
That said, it seems that somewhere in this static class of yours, you have a field named CKB_ancora, and you are probably initializing it using a field initializer, possibly like this:
private CheckBox CKB_ancora = new Checkbox();
And having done this, you are finding that when the class is initialized (typically on the first time something in the class is accessed at runtime), that happens too soon and an exception is thrown.
Assuming that's correct, then it seems to me that most obvious and simplest "fix" is to initialize the object lazily. For example:
private Lazy<CheckBox> _ckb_ancora =
new Lazy<CheckBox>(() => new CheckBox());
private CheckBox CKB_ancora { get { return _ckb_ancora.Value; } }
That will wrap the object storage in a property, which in turn uses an instance of Lazy<T> to defer initialization until the first time any code actually tries to access it.
Now, that said, I'm not very enamored of your approach here, with a static member that is used in some instantiated object. What if someone using your library wants to use the code with two or more Panel instances? A Control (including a CheckBox) can't be a child of more than one other Control at a time, so having a single static instance of the Control is just not going to work.
IMHO, it would probably be better to instead use some identifying feature such as the Name or Tag property of the CheckBox to handle the control appropriately (e.g. such as filtering it out of enumerations).
For example:
public static void PreencheCheckboxesPanel(this Panel p, List<CheckBox> listaCheckBoxs) {
var count = 0;
if (adicionaAncora) {
CheckBox CKB_ancora = new CheckBox();
CKB_ancora.Text = textoAncora;
CKB_ancora.Name = "CKB_ancora";
CKB_ancora.CheckedChanged += (sender, args) => {
ChecaCheckBoxes(p, CKB_ancora.Checked);
};
listaCheckBoxs.Insert(0, CKB_ancora);
}
listaCheckBoxs.ForEach(
i => {
i.Location = new Point(10, 10 + ((count) * 25)); //"dynamic" and not-so-effective resizing here
i.AutoSize = true;
count++;
});
p.Controls.AddRange(listaCheckBoxs.ToArray());
}}
And, for example:
public static void ChecaCheckBoxes(this Panel b, bool checkStatus = true) {
var listaCheckBoxs = b.Controls
.OfType<CheckBox>().Where(c => c.Name != "CKB_ancora").ToList();
listaCheckBoxs.ForEach(
i => {
i.Checked = checkStatus;
});
}
That way, when you go to retrieve the list of CheckBox controls, the special one you added at the beginning is ignored.
Even with that, I think the code is still pretty fragile. The above would work better, but IMHO it would probably be even better if you weren't using static members for all of this in the first place. I.e. instead you should design some mechanism that allows you to relate an instance of the helper class to the Panel object it's helping with, so that you can in fact initialize and store per-instance information, without running into problems of order of execution, as well as of limitations of use of the code with just a single client.
Without a better code example, I don't see any good way to offer any specific advice along those lines though.
Related
Sorry for my verry badly written title, I'm a beginner programmer and I just started on a c# winforms app. In one function I create an object of some type and then in other functions I iterate through a list of that type of objects, however I'm switching the type of control I'm using and when I do, I have to change the type declaration of my object in over twenty places. Is there a way to create a variable that holds that type and than define all my objects off that variable so I only have to specify the type once and then change that variable. Because I'm using winforms controls as my class types all the functions I call are all then same no matter what type my objects are, so all I need to do is change the type declaration and that's it, sorry if this is a stupid question and any help would be appreciated.
Here is a snippet of my code for context:
private void function1(object sender, EventArgs e) //not my actual function because the real function has lots of other unrelated code
{
ListView PlaceType = new ListView(); // these ListView types i would like to replace with a placeholder if possible
ListView listview = new ListView();
int count2 = autolayoutGroups.Controls.OfType<ListView>().ToList().Count();
listview.Size = new System.Drawing.Size(150, 100);
listview.BackColor = normalColor;
listview.BorderStyle = BorderStyle.Fixed3D;
listview.ForeColor = System.Drawing.Color.Black;
listview.Name = "Group" + count2;
listview.MouseDown += Select;
}
private void function2(object sender, EventArgs e)
{
List<ListView> list_of_groups = autolayoutGroups.Controls.OfType<ListView>().ToList(); // a place where I need some type of placeholder
foreach (ListView l in list_of_groups)
{
// do something here
}
}
private void function3(object sender, EventArgs e) // I have several functions like this
//and if I change the control I'm using I have to change the types in every function
{
List<ListView> list_of_groups = autolayoutGroups.Controls.OfType<ListView>().ToList(); // a place where I need some type of placeholder
foreach (ListView l in list_of_groups)
{
// do something here
}
}
If I understand your problem correctly, you currently have some code where you use a ListView, and you want the same code, but instead of ListView you want some other class, for instance a DataGridView, or a ComboBox. Of course this other class must also have the methods that you used on the ListView.
In C# this concept is called a generic. You have generic classes and generic methods.
You define the generic by typing an identifier instead of the part that you want to replace with another type.
In your case: you want to replace ListView by DataGridView. In function1 you create a ListView, and set some properties. First we'll put this creation in a separate method, you will have something like this:
private ListView CreateListView()
{
ListView listview = new ListView();
int count2 = autolayoutGroups.Controls.OfType<ListView>().Count();
listview.Size = new System.Drawing.Size(150, 100);
listview.BackColor = normalColor;
listview.BorderStyle = BorderStyle.Fixed3D;
listview.ForeColor = System.Drawing.Color.Black;
listview.Name = "Group" + count2;
listview.MouseDown += Select;
return listView;
}
private void function1(object sender, EventArgs e)
{
ListView createdListView = this.CreateListView();
// TODO: do something with the created ListView
}
(Small optimization, out of scope of the question): to calculate count2, don't create a List of all ListViews, and then Count them; use Count() on the IEnumerable<ListView>.
To change CreateListView such that it can create anything of type TMyType, define the generic method like this:
private TMyType Create<TMyType>()
{
TMyType createdObject = new TMyType();
int count2 = autolayoutGroups.Controls
.OfType<TMyType>()
.Count();
createdObject.Size = new System.Drawing.Size(150, 100);
createdObject.BackColor = normalColor;
createdObject.BorderStyle = BorderStyle.Fixed3D;
createdObject.ForeColor = System.Drawing.Color.Black;
createdObject.Name = "Group" + count2;
createdObject.MouseDown += Select;
return createdObject ;
}
So all I did was, that whenever I save ListView, I replaced it with TMyType, the type that should be created.
Usage:
ListView createdListView = this.Create<ListView>();
DataGridView createdDataGridView = this.Create<DataGridView>();
ComboBox createdComboBox = this.Create<ComboBox>();
There is only one problem. You'll have to tell the compiler that TMyType has a default constructor (you want to do new TMyControl()), and that is has methods like Size, BackColor, ForeColor, etc.
If TMyType would be a class derived from Control, then you would be certain that it has the desired constructor and knows all methods that you need to use.
To say that a generic type is derived from a certain type, you use the following structure:
private TMyType Create<TMyType>() where TMyType: Control
{
// because you are certain the TMyType is derived from Control
// you can use all methods of class Control
}
This answers your question: create a generic method
Some other things about generics
Another example: If you want to inform the compiler that the generic type implements IComparable:
private T Process<T>(T input) where T: IComparable {...}
Or multiple:
private T Process<T>(T input) where T: IComparable, IEquatable {...}
And finally if you want to require that the generic type has a default constructor:
private T Process<T> () where T: new
It's not really clear what the goal is but I suppose you can make a property that returns your commonly used list:
private List<ListView> AutolayoutGroupControls =>
autolayoutGroups.Controls.OfType<ListView>().ToList()
Then you can
foreach(var lv in AutolayoutGroupControls)}
...
}
But it doesn't offer much; if you change that prop to return something else you still have a stack of changes to make. If your loops always do the same thing put it into a method and call it from N event handlers
Say I have the elements with the ID's of "Input1", "Input2" and "Input3".
Is there a way to loop through them rather then having to write:
Input1.Value = 1;
Input2.Value = 1;
Input3.Value = 1;
in jquery you can just refrence an element like $('#Input'+i) and loop through i, something similar would be very useful in ASP code behind.
Edit: Duh, I searched again for finding all "x" controls on page and came up with the following source code:
foreach(Control c in Page.Controls)
{
if (c is TextBox)
{
// Do whatever you want to do with your textbox.
}
}
Kind of ... based on your example naming scheme you can do something like the following:
private void Button1_Click(object sender, EventArgs MyEventArgs)
{
string controlName = TextBox
for(int i=1;i<4;i++)
{
// Find control on page.
Control myControl1 = FindControl(controlName+i);
if(myControl1!=null)
{
// Get control's parent.
Control myControl2 = myControl1.Parent;
Response.Write("Parent of the text box is : " + myControl2.ID);
}
else
{
Response.Write("Control not found");
}
}
}
This will let you loop through numerically named controls but otherwise it is somewhat clunky.
If you know the parent container you can loop though its .Controls() property. If you start at the Page level and work recursively, you can eventually reach all controls on the page.
See the answer from this question for more details.
I like to keep things strongly typed, so I store them in a list. This makes the code more resilient to refactoring and there's no need to cast. It takes a slight bit more upfront work to put all your controls into the list, but to me it's often worth it.
I'm not sure what type your controls are, so I'm going to pretend they're of type Input.
var InputControls = new List<Input>(){Input1, Input2, Input3};
foreach(var input in InputControls)
{
input.Value = 1;
}
Problem: Program flow is not going to the child class implementation of ValidateDynData when I call ValidateDynData in my parent class.
I create my instance of my class using reflection. When I invoke a method in the child class from another project, it winds up in the correct method in the child class (and not parent's same-name method), so it seems like that is set up correctly.
This is what the reflection part looks like in my other project/class:
**Note 3/7/2013: I added more info so you can get the general feel for this. It gets the number of boxes, loops thru the number of boxes, and for each box, creates a control and adds a tab to the form. This is the main CTool visual studio project and is a class in the project, which is a form. When I press a button on the form, with the info (selected) on which child class I'm going to be creating later, it goes to this method , CreatTabs():
cb = new CB();
int errValue = cb.FindUsbHid(ref HWndBoxID); //see how many boxes there are
if (errValue == 0 && HWndBoxID[0, 1] != 0)
{
for (int i = 0; i < cb.cbInfos.Length; i++)
{
if (controls[i] == null)
{
CB cb1 = new CB(); //need one for each box found or concurrent programming will fail
errValue = cb1.FindUsbHid(ref HWndBoxID); //need to do for each box to get all info
/////////////////////////////////////////
if (errValue == 0)
{
_assembly = Assembly.LoadFrom(programDll);
_type = _assembly.GetType("CrWriter.PC");
_objectInstance = Activator.CreateInstance(_type);
_parameters = new Object[] { cb1, programDll, templateArr, itsDll, cert, i, cb1.cbInfos[i].boxID };
controls[i] = new Control();
//The following lands in my child's GetPC method
//My parent also has a method called GetPC and that is called from the child.
//Then, most of the program flow is in the parent until I need to call ValidateDynData,
//discussed below
controls[i] = (Control)_type.InvokeMember("GetPC", BindingFlags.Default | BindingFlags.InvokeMethod, null, _objectInstance, _parameters);
controls[i].Dock = DockStyle.None;
this.Controls.Add(controls[i]);
TabPage newPage = new TabPage(string.Format("{0}:{1}", cb1.cbInfos[i].usbHandle, cb1.cbInfos[i].boxID));
Console.WriteLine("frmUserForm::CreateTabs - Making new tab with cb.cbInfos[i].usbHandle:" + cb1.cbInfos[i].usbHandle + " and cb.cbInfos[i].boxID:" + cb1.cbInfos[i].boxID);
InitializeControls(controls[i]);
tabCtrlMain.Size = controls[i].Size;
tabCtrlMain.Width += 20;
tabCtrlMain.Height += 100;
this.Width = tabCtrlMain.Width + 20;
this.Height = tabCtrlMain.Height + 50;
newPage.Controls.Add(controls[i]);
tabCtrlMain.TabPages.Add(newPage);
} //no err for this cb
} //controls not null
} //for all cbInfo's
}//if no err in general finding out how many cb's
this.ResumeLayout();
}
Since my Invocation of GetPC lands in the child class and not the parent, it must be created correctly. So I'm not sure why it's not landing in the correct ValidateDynData method. Maybe I need to cast my object to the programDll somehow. When I run the program and inspect the _objectInstance it could be a problem:
variable..................................................value
base: {GenericCrWriter.GenericPC} ......CrWriter.PC
baseInst: ................................................GenericCrWriter.GenericPC
But then, the _assembly is referring to Ko/PC and not Generic/GenericPC.
Also, my _assembly.GetType looks good. My Generic/parent doesn't have anything named CrWriter.PC
I'm trying to use the child class method instead of the parent class for some child class cases. For some reason, I get to the parent class method, but it never gets to the override in the child. Any ideas why? I've been referring to Calling child class method from parent
but it's not getting to the child's method.
In my PC.cs of the child class (Ko):
**Note 3/8/2013: PC.cs is in the Ko visual studio project. **this contains a form that is displayed
**Note 3/7/2013: This is a separate visual studio project named after the child, let's call it Ko. The important class here is PC.cs. It doesn't do much except pass data to the parent, provide it's custom textBoxes and their names, validate data entered later in the parent's form. Most of the flow is in the parent, otherwise. I'm adding GetPC, setProgramName, setDTF methods.
public partial class PC : GenericPC
{
String childDllName = ""; //I just added this recently but it doesn't seem useful
GenericPC baseInst = new GenericPC();
public Control GetPC(USB_Comm.CB cbInst, string dllSel, TemplateHApp.Templates.TEMPL[] templ, string dll, SC.SC.SITE c0, int slaveIndex, int BoxID)
{
childDllName = dll;
//call parent class methods
setProgramName();
setDTF();
ProcessDynData();
return baseInst.GetPC(cbInst, dllSel, templ, dll, cert0, slaveIndex, BoxID);
}
public void setProgramName()
{
Console.WriteLine("Ko did stuff");
//Update label on form
var f = new F(); //F is a class in child class containing more info on it
string temp = f.GetProgramName();
baseInst.setProgramName(temp); //this is displayed on parent's form
}
public void setDTF()
{
var f = new F();
string temp = f.DTF();
baseInst.setDTF(temp); //this is displayed on parent's form
}
private void ProcessDynamicData()
{
Console.WriteLine("Ko PC::ProcessDynamicData");
Label lbl_dynData0 = new Label();
Label lbl_dynData1 = new Label();
lbl_dynData0.Text = "AT .";
lbl_dynData1.Text = "VL .";
lbl_dynData0.Location = new Point(57, 25);
lbl_dynData1.Location = new Point(57, 45);
Label[] lbl_dynData_Arr = new Label[4];
lbl_dynData_Arr[0] = lbl_dynData0;
lbl_dynData_Arr[1] = lbl_dynData1;
TextBox tb_dynData0 = new TextBox();
TextBox tb_dynData1 = new TextBox();
tb_dynData0.Location = new Point(67, 25);
tb_dynData1.Location = new Point(67, 45);
tb_dynData0.Size = new Size(151,22);
tb_dynData1.Size = new Size(151, 22);
TextBox[] tb_dynData_Array = new TextBox[4];
tb_dynData_Array[0] = tb_dynData0;
tb_dynData_Array[1] = tb_dynData1;
PC pc = this; //Tried adding this to get past problem but it's not turned out useful
//I think the way I access parent class from child is the problem of why flow in
//parent class isn't reaching overridden method in child when called:
baseInst.addDynamicDataTextBoxes(tb_dynData_Array, lbl_dynData_Arr, childDllName, pc);
}
public override void ValidateDynData(TextBox[] tb_dynData_Array, ref int result)
{ //I added more info here, but it's probably too much info 3/7/2013
Console.WriteLine("Ko PC::ValidateDynData");
result = -610;
//AT
if ((Convert.ToInt16(tb_dynData_Array[0].Text) >= 1) && (Convert.ToInt16(tb_dynData_Array[0].Text) <= 99))
result = 0;
//VL
if (result == 0)
if ((Convert.ToInt16(tb_dynData_Array[1].Text) >= 69) && (Convert.ToInt16(tb_dynData_Array[1].Text) <= 100))
result = 0;
else
result = -610;
}
In my GenericPC.cs of the parent class:
**Note 3/8/2013: GenericPC is in the Generic visual studio project.
**Note 3/7/2013 When the child class calls the parent class to initialize important data, the parent class shows it's form and fields (I think resume layout shows it). Next, we enter data on the form, including Ko's custom data, then we hit a button on the form (btn_Lock_Config_Click) and it needs to process and validate it's data. I added more methods to get the feel for flow. There are a ton more methods in parent than child (not shown), including try/catch, etc.
//base
public partial class GenericPC : UserControl
{
//class variables (wave your hands..too much info)
public Control GetPC(USB_Comm.CB cbInstance, string dllSelected, TemplateHApp.Templates.TEMPL[] template, string dll, SC.SC.SITE c0, int slaveIndex, int boxID)
{
cb = cbInstance;
SlaveIndex = slaveIndex;
createControls();
itsDll = dll;
templateArr = template;
return this; //return the control for the control array
}
//called from child class
public void setProgramName(string name)
{
Console.WriteLine("Generic setProgramName slaveIndex:" + SlaveIndex);
lbl_Program_Name.Text = name;
}
//called from child class
public void setDTF(string theDTF)
{
Console.WriteLine("Generic setDTF slaveIndex:" + SlaveIndex);
lbl_Program_Name.Text += " ";
lbl_Program_Name.Text += theDTF;
lbl_Program_Name.Refresh();
}
public void addDynamicDataTextBoxes(TextBox [] tb_dynData, Label [] lblTitle, String childName, Object child)
{
childHasDynamicData = true; //somebody's knocking
itsChildName = childName; //child name isn't turning out to be useful here
itsChild = child; //child isn't turning out to be useful here
Console.WriteLine("Generic addDynamicDataTextBoxes slaveIndex:" + SlaveIndex);
//Display what child wants
for (int i = 0; i < tb_dynData.Length; i++)
{
//assumes calling code knows real estate and planned for it
gb_dynamicData.Controls.Add(lblTitle[i]);
gb_dynamicData.Controls.Add(tb_dynData[i]);
}
itsEnteredDynamicData = tb_dynData; //nothing entered yet
}
private void btn_Lock_Config_Click(object sender, EventArgs e)
{
int status = 1;
Console.WriteLine("Generic btn_Lock slaveIndex:" + SlaveIndex);
//it does some flagging and data checking, etc.
status = processDynamicData();
}
private int processDynData()
{
int returnCode = 0; //I'm setting it to desired value for example
//processes data, puts it into data arrays, etc,
if ((returnCode >= 0) && childHasDynamicData)
ValidateDynData(itsEnteredDynamicData, ref returnCode);
//start here for problem...it never calls child method, as intended
}
public virtual void ValidateDynData(TextBox[] tb_dynData_Array, ref int result)
{
Console.WriteLine("Generic::ValidateDynData passing off to child to validate special data");
}
Any ideas why it's not going to the child class implementation of ValidateDynData when I call ValidateDynData in my parent class? This is the only area in my code where I am trying to have a child class override a parent implementation, so I'm not sure if I'm doing something wrong?
I checked the correct version of Generic.dll is referenced in the child's project/class. I did a clean build in the child class. Any other binaries that should be checked? Is there something wrong with my reflection? Is there something wrong with my virtual/override use for ValidateDynData?
Update:
I've been looking at the code some more, and I get flow into the parent class by creating an instance of the parent/base class. So I think that's why I'm not getting into ValidateDynData that is overridden in the child class when I call it in parent. Is there another way to get to the parent's method without creating an instance of the parent?
GenericPC baseInst = new GenericPC();
return baseInst.GetPC(cbInst, dllSel, templ, dll, cert0, slaveIndex, BoxID);
**Update 3/7/13:
It's also possible that the problem is that I press a button on the parent's form which starts a new thread and by doing this, it doesn't know about child class, so that's why flow doesn't get to child when I call ValidateDynData method.
Short answer: Just delete all that awful code and start over.
Longer answer:
// (1)
public partial class PC : GenericPC
{
// (2)
GenericPC baseInst = new GenericPC();
public Control GetPC(…)
{
…
// (3)
return baseInst.GetPC(cbInst, dllSel, templ, dll, cert0, slaveIndex, BoxID);
}
public override void ValidateDynData(TextBox[] tb_dynData_Array, ref int result)
{
// (4)
…
}
}
Comments to marked lines of code:
At this point you declare PC as a descendant of GenericPC. So far so good.
Here you declare and instantiate a completely disparate instance of GenericPC which has nothing to do with the instance of PC you are working with.
You call GetPC, a method of an instance of PC, which in turns call GetPC in that completely disparate instance of GenericPC; nothing in common with the original PC instance!
Finally, you expect control flow to end up the original PC instance; but that won't ever happen when, effectively, you all the time call methods of some silly GenericPC instance!
My recommendation is reading a book about object-oriented programming, that provides samples in C#. It seems you are even missing the point of inheritance, one of the basic concepts in OOP.
To fix it, you need to remove the declaration of baseInst, and replace all calls to baseInst's methods with the base keyword. Then your code will actually call methods declared in the ancestor class within the same instance. Also most methods shall be declared as virtual in GenericPC and you have to override them in PC.
public partial class PC : GenericPC
{
public override Control GetPC(…)
{
…
return base.GetPC(cbInst, dllSel, templ, dll, cert0, slaveIndex, BoxID);
}
public override void ValidateDynData(TextBox[] tb_dynData_Array, ref int result)
{
…
}
}
I am using a datasource to populate my datagridview with the data. However, im trying to find a way for the user to be able to hide columns that he does not want to see.
I am able to hide and show columns before the program runs using:
[Browsable(false)]
public string URL
{
get
{
return this._URL;
}
set
{
this._URL = value;
this.RaisePropertyChnaged("URL");
}
}
I cannot seem to figure out how to change the [Browsable(false)] at run time.
Any ideas how I could accomplish this?
Basically, I want to bind an "on/off" to a menu.
Apologies if im not using the right terminology when explaining my problem, I am self taught and started a few weeks ago - so still very newbie :)
Edit:
Cant hide the column because when i run my update function all columns appear again. Here is my function for updating:
private void UpdateResults()
{
Invoke(new MethodInvoker(
delegate
{
this.dgvResults.SuspendLayout();
this.dgvResults.DataSource = null;
this.dgvResults.DataSource = this._mySource;
this.dgvResults.ResumeLayout();
this.dgvResults.Refresh();
}
));
}
At run time, you can just specify the column as being invisible:
dgv.Columns["ColumnName"].Visible = false;
The way to do this properly at runtime is to provide a custom ITypedList implementation on the collection, or provide a TypeDescriptionProvider for the type, or (for single-object bindings, not lists), to implement ICustomTypeDescriptor. Additionally, you would need to provide your own filtered PropertyDescriptor implementation. Is it really worth it? In most cases: no. It is much easier to configure the grid properly, showing (or not) the appropriate columns by simply choosing which to add.
Indeed, as others had mention the purpose of BrowsableAttribute is different, but I understand what you want to do:
Let's suppose that we want to create a UserControl than wraps a DataGridView and gives the user the ability to select which columns to display, allowing for complete runtime binding. A simple design would be like this (I'm using a ToolStrip, but you can always use a MenuStrip if that's what you want):
private void BindingSource_ListChanged(object sender, ListChangedEventArgs e) {
this.countLabel.Text = string.Format("Count={0}", this.bindingSource.Count);
this.columnsToolStripButton.DropDownItems.Clear();
this.columnsToolStripButton.DropDownItems.AddRange(
(from c in this.dataGrid.Columns.Cast<DataGridViewColumn>()
select new Func<ToolStripMenuItem, ToolStripMenuItem>(
i => {
i.CheckedChanged += (o1, e2) => this.dataGrid.Columns[i.Text].Visible = i.Checked;
return i;
})(
new ToolStripMenuItem {
Checked = true,
CheckOnClick = true,
Text = c.HeaderText
})).ToArray());
}
In this case, bindingSource is the intermediary DataSource of the dataGrid instance, and I'm responding to changes in bindingSource.ListChanged.
I have this for loop:
int iPanelNumber = 1;
foreach (string[] Persons in alItems)
{
Panel pPanelContainer = new Panel();
pPanelContainer.Width = contentPanel.Width;
pPanelContainer.Height = 50;
pPanelContainer.BackColor = Color.FromArgb(
Convert.ToInt32(aWhiteContentBackgroundColors[0]),
Convert.ToInt32(aWhiteContentBackgroundColors[1]),
Convert.ToInt32(aWhiteContentBackgroundColors[2]));
pPanelContainer.Name = "PanelContainer" + iPanelNumber.ToString();
pPanelContainer.Visible = false;
pPanelContainer.Location = new Point(0, 0);
}
So as you can see, i have given the panels i create the name "PanelContainer1", "PanelContainer2" etc...
But how can i reach these panels?
I certainly could not reach them by writing:
PanelContainer1.visible = true;
Anyone got an idea?
Thanks in advance
Easiest way is probably to add a List<Panel> field to your class and store references to all panels in that list, e.g:
class MyClass
{
private List<Panel> _panels = new List<Panel>();
void MethodWhichCreatesThePanels()
{
//..
foreach (string[] Persons in alItems)
{
Panel pPanelContainer = new Panel();
_panels.Add(pPanelContainer);
...
}
}
Then you can access each panel later using an index:
Panel aPanel = _panels[i];
Martin's answer is pretty much what you are looking for, but it appears you are in confusion over what the .Name property of the panel control does.
What it doesn't do is set the name of the variable.
What it does do is the following (from MSDN: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.name.aspx)
The Name property can be used at run time to evaluate the object by name rather than type and programmatic name. Because the Name property returns a String type, it can be evaluated in case-style logic statements (Select statement in Visual Basic, switch statement in Visual C# and Visual C++).
You can't reference the Panel by its name because there's no field in the form (or local variable) with that name. Those fields are defined in the partial class file for the Form that the Form Designer generates; they aren't (and can't be) created at runtime.
This doesn't mean you can't access it by its name; you just can't access it by using its name as a variable name in your code.
The most obvious way to do this is to add the Panel to its containing control's Controls collection after creating it. Since you've set Visible to false, this won't have any visible effect on your form. So in your code, you'd add something like
contentPanel.Add(p);
You can then reference it by name:
contentPanel.Controls["PanelContainer1"].Visible = true;
If for some reason you don't want to add it to the Controls collection yet (there are plenty of reasons you might not), the next approach is to create an instance of a collection class of some kind and add the Panel to that collection. Since you want to be able to reference it by name, the most obvious choice would be a dictionary, e.g.:
Dictionary<string, Panel> panels = new Dictionary<string, Panel>;
...
panels.Add(p.Name, p);
And again, you can then reference it by name:
panels["PanelContainer1"].Visible = true;
...though in this case, the Panel wouldn't actually become visible, because it's not in the Controls collection of a visible container control.
Just as an aside: if it's within your power to do so, you should put an end to using type prefixes on your variable names. There are still shops that use this convention, but it's been generally abandoned by the community of C# programmers.