I believed that manually calling System.GC.Collect() only effect performance or memory usage of application. But in this example, calling System.GC.Collect() changes application's action.
using System;
using System.Windows.Forms;
public class Form1 : Form
{
public Form1()
{
this.Click += Form1_Click;
}
private void Form1_Click(object sender, EventArgs e)
{
CreateMyEventHandler()(sender, e);
//System.GC.Collect();
}
private EventHandler CreateMyEventHandler()
{
return (sender, e) =>
{
var cm = new ContextMenu(new[]
{
new MenuItem("Menu item", (sender2, e2) =>
{
Console.WriteLine("Menu item is clicked!");
})
});
cm.Show(this, ((MouseEventArgs)e).Location);
};
}
}
static class Program
{
static void Main()
{
Application.Run(new Form1());
}
}
Save above code into a file "Program.cs" and run it as following.
csc Program.cs
.\Program.exe
It opens window as figure below.
Clicking the window opens context menu, and clicking the menu item prints text massage "Menu item is clicked!" to the console, as expected.
However, with uncommenting the line commented out in above example:
System.GC.Collect();
The action of application changes. Clicking menu item prints nothing.
This was unexpected for me. So why it changes? And how do I prevent unexpected change of this kind in real application?
This example is confirmed with Visual Studio 2013.
It is a premature collection problem. You can see that for yourself by adding this class:
class MyContextMenu : ContextMenu {
public MyContextMenu(MenuItem[] items) : base(items) { }
~MyContextMenu() {
Console.WriteLine("Oops");
}
}
And using MyContextMenu instead of ContextMenu. Note how you see "Oops" when you call GC.Collect().
There is a technical reason for this, ContextMenu is a .NET 1.x class that wraps the native menus built into the OS. It does not derive from the Control class so does not participate in the normal way controls are kept alive. Which is through an internal table inside the Winforms plumbing that ensures that Control objects stay referenced as long as their native Handle exists. This same kind of table is missing for Menu class. It gets extra confusing because the .NET wrapper class is gone but the native Windows menu still exists, so seems to operate correctly.
Back in the .NET 1.x days programmers were used to using the designer to create context menus. Which works fine, they stay referenced through the components collection. Which is how you could fix this problem:
var cm = new ContextMenu(...);
this.components.Add(cm);
cm.Show(this, ((MouseEventArgs)e).Location);
Albeit that you now keep adding context menus, that isn't pretty either. Assigning the form's ContextMenu property is another workaround, probably the one you prefer. But, really rather best to retire this ancient 1.x component and use ContextMenuStrip instead, it doesn't have this problem:
var cm = new ContextMenuStrip();
cm.Items.AddRange(new[]
{
new ToolStripMenuItem("Menu item", null, (sender2, e2) =>
{
Console.WriteLine("Menu item is clicked!");
})
});
cm.Show(this, ((MouseEventArgs)e).Location);
Nobody is keeping a reference of your context menu and its menu item.
If you are forcing the garbage collector to run, it will detect that and remove them.
If you don't force the garbage collector, these items will be garbage collected too, but later, which is why you see the printing.
Related
This is a tray-icon-only Windows Forms application. I'm trying to use argument to control something and change the text on the form for showing the status information.
But I found when I use argument to call it during it's running, the things I want to change are null (NotifyIcon() and MenuItem()), seems it ran a different application when I using arguments. I also tried Invoke() but there is no this definition in NotifyIcon().
Here is the code I wrote:
static void Main(string[] args)
{
if (args.Length > 0)
{
Arg_Call(args[0]);
}
if (new Mutex(true, "{XXX}").WaitOne(TimeSpan.Zero, true))
{
Init_Tray();
Application.Run();
}
}
private static NotifyIcon trayicon;
private static void Init_Tray()
{
trayicon = new NotifyIcon() { Icon = new Icon(#"D:\projects\Icon.ico"), Text = "Waiting", Visible = true };
trayicon.Visible = true;
Application.Run();
}
private static void Arg_Call(string args)
{
trayicon.Invoke((MethodInvoker)delegate {
trayicon.Text = "OK";
}); //from: https://stackoverflow.com/a/661662/8199423
}
Where am I wrong? How to and what is the best way to change the NotifyIcon.Text property in the running form via command-line-arguments?
I am sorry I was unable to adequately explain why your question is a duplicate of the existing "single-instance-application" questions. I will try to reiterate the train of thought here:
You wrote "How to and what is the best way to change the texts in the running form via command-line-arguments?"
Your requirement involves a currently-running process, which is presenting the NotifyIcon in the task tray, and the desire to use the command-line to modify that currently-running process's state.
It is a simple fact that when you type anything on the command line, it starts a whole new process. That process is necessarily different from the process that is already running, and which is presenting the NotifyIcon in the task tray.
Putting all of the above together, we have the conclusion that you want a new process that you start on the command line to interact with an existing process. And the simplest way to achieve that goal is to use the built-in single-instance-application support found in .NET. This is because the support for single-instance-applications includes automatic passing of the new command line arguments to the previous running program. Hence, the duplicate.
As I mentioned earlier, you should try to develop the skill to generalize and see how seemingly new problems are really just old problems in disguise and which you or someone else already knows how to solve.
In the same way that all problem solving can be summarized as "break the large problem down into smaller problems, repeat as necessary until all of the smaller problems are problems you already know how to solve", programming is very often not a matter of solving new problems, but rather of recognizing how your current problem is really a problem you already know how to solve.
All that said, I have the impression that you're still having difficulty figuring out how to apply that information to your specific scenario. So, perhaps this is an opportunity to illustrate the validity of the philosophy I espouse, by showing you how your seemingly different problem really is the problem I claim it is. :)
So, let's start with your original scenario. I am not using the code you posted, because it's mostly code that isn't needed. It seemed simpler to me to start from scratch. To do that, I wrote a little TrayManager class that encapsulates the actual NotifyIcon part of the functionality:
class TrayManager : IDisposable
{
private readonly NotifyIcon _notifyIcon;
public TrayManager()
{
_notifyIcon = new NotifyIcon
{
ContextMenu = new ContextMenu(new[]
{
new MenuItem("Exit", ContextMenu_Exit)
}),
Icon = Resources.TrayIcon,
Text = "Initial value",
Visible = true
};
}
public void Dispose()
{
Dispose(true);
}
public void SetToolTipText(string text)
{
_notifyIcon.Text = text;
}
protected virtual void Dispose(bool disposing)
{
_notifyIcon.Visible = false;
}
private void ContextMenu_Exit(object sender, EventArgs e)
{
Application.ExitThread();
}
~TrayManager()
{
Dispose(false);
}
}
The above hard-codes the context menu for the icon. Of course, it a real-world program, you'd probably want to decouple the menu from the above class, for greater flexibility.
The simplest way to use the above would look something like this:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (TrayManager trayManager = new TrayManager())
{
Application.Run();
}
}
}
So, how do we modify the above so that when you run the program again, you can change the Text property of the NotifyIcon with the command-line arguments you type? That's where the single-instance application comes in. As seen in the duplicate I marked earlier, What is the correct way to create a single-instance application?, one of the simplest ways to accomplish this is to use the Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase class, which has built right in support for single-instance applications and a mechanism for delivering new command line arguments to the existing process.
The one little draw-back is that this class was designed for Winforms programs, with the assumption that there will be a main form. To use it will require creating a Form instance. For a program without the need for an actual form, this means creating a Form instance that is never shown, and making sure that it's never shown does require a little bit of finagling. Specifically:
class TrayOnlyApplication : WindowsFormsApplicationBase
{
public TrayOnlyApplication()
{
IsSingleInstance = true;
MainForm = new Form { ShowInTaskbar = false, WindowState = FormWindowState.Minimized };
// Default behavior for single-instance is to activate main form
// of original instance when second instance is run, which will show
// the window (i.e. reset Visible to true) and restore the window
// (i.e. reset WindowState to Normal). For a tray-only program,
// we need to force the dummy window to stay invisible.
MainForm.VisibleChanged += (s, e) => MainForm.Visible = false;
MainForm.Resize += (s, e) => MainForm.WindowState = FormWindowState.Minimized;
}
}
The only thing in the above that gives us the single-instance application behavior we want is the setting of IsSingleInstance = true;. Everything else is there just to satisfy the requirement that some Form object is present as the MainForm, without actually showing that object on the screen.
Having added the above class to the project, we can now "connect the dots". The new Program class looks like this:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (TrayManager trayManager = new TrayManager())
{
TrayOnlyApplication app = new TrayOnlyApplication();
app.StartupNextInstance += (s, e) => trayManager
.SetToolTipText(e.CommandLine.Count > 0 ? e.CommandLine[0] : "<no value given>");
app.Run(args);
}
}
}
You'll note two changes:
In addition to the TrayManager, which handles the NotifyIcon, we now also create the TrayOnlyApplication object, subscribing to its StartupNextInstance event so that we can receive the command line arguments given to any new instance, and use that to set the Text property of the NotifyIcon object (by passing that to the method created specifically for that purpose).
Instead of using Application.Run() to run the require message-pump loop to handle window messages, we use the Run() method our TrayOnlyApplication class inherited from the WindowsFormsApplicationBase class. Either of these methods handle message pumping while the program is running, and return control to the caller when the Application.ExitThread() method is called, so both approaches to message pumping work with the code in the TrayManager.
Now, the above example is simply a slight modification of the original version that didn't enforce single-instance operation. You might notice that it has the arguable deficiency that it always creates the tray icon, whether or not it's the first instance to run. Subsequent instances will run, create the tray icon, then immediately dismiss the icon and exit.
The WindowsFormsApplicationBase provides a mechanism to avoid this, the Startup event. While the StartupNextInstance event is raised in any instance of the application that is run when an instance already is running, the Startup event is raised only when no other instance is already running. I.e. in the instance where you actually want to do things, like show the tray icon.
We can take advantage of that event to defer creation of the NotifyIcon until we know whether we actually need it or not:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
TrayManager trayManager = null;
TrayOnlyApplication app = new TrayOnlyApplication();
// Startup is raised only when no other instance of the
// program is already running.
app.Startup += (s, e) => trayManager = new TrayManager();
// StartNextInstance is run when the program if a
// previously -run instance is still running.
app.StartupNextInstance += (s, e) => trayManager
.SetToolTipText(e.CommandLine.Count > 0 ? e.CommandLine[0] : "<no value given>");
try
{
app.Run(args);
}
finally
{
trayManager?.Dispose();
}
}
}
Note that here, we need to write the try/finally explicitly instead of using the using statement, because the using statement requires initialization of the variable when it's declared, and we want to defer initialization until later, or never, depending on which instance is being run.
(Unfortunately, there's no way to defer creation of the dummy window in the TrayOnlyApplication class, since it's required just to call the Run() method, which requires a valid Form object be already set, and the determination as to which instance is being run happens in that call, not before.)
And that's all there is to it. The above shows, clearly I hope, exactly how the single-instance application techniques available to you directly solve the problem you are asking for help with. By providing a mechanism for a newly-run instance of your program to communicate the command line arguments passed to it, to the already-running instance of the same program, that newly-run instance can cause the already-running instance to perform whatever work it needs to (such as changing the tool-tip text for the tray icon, for example).
Naturally, any similar mechanism will achieve the same result. The only important thing is to have the newly-run instance detect an existing instance, and communicate with it. It just happens that the WindowsFormsApplicationBase class provides that functionality pre-made. There are lots of other ways to do the same thing, each with their own pros and cons.
I have an application that I am deploying using ClickOnce. I am using the default InstallUpdateUpdateSyncWithInfo() method provided here. I made two changes though; I made the method public and static as I am calling it from a static class. I know bad practices. This is some lazy code just to try out ClickOnce.
Everytime the application updates it loads two instances, the old one and the new one.
Other than that though I am calling the method in my app.xaml.cs like this:
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
MainWindow window = new MainWindow();
CheckForUpdates.InstallUpdateSyncWithInfo();
window.Show();
}
}
I thought if I call Window.Show() after checking for an Update it would call the Application.Restart() method in InstallUpdateUpdateSyncWithInfo() before the old version could load, but this is not the case.
Does anyone know how I can prevent two instances of my application from loading after the application is updated?
There was another post on Stack Overflow which from the title, I thought would directly address this question, but I did not see how the poster modified his code to prevent two instances from loading.
There's no need to write the auto-update code yourself. First, I would remove your update code.
Next right-click on your C# project and select Properties. Then go to Publish and click Updates.... Tick the checkbox so your application checks for updates and ClickOnce will handle the rest.
A little new to C#, and approaching something beyond me. Apologies for length.
I have a Windows Form application in Visual Studio C# Express, using the default classes VS spawns. I want to start and stop a Marquee style progressBar from a class other than the default Form1 in which it is declared.
These seems surprisingly difficult, I am sure I am missing something important.
My project has the usual classes that Visual Studio auto generates:
Form1.cs, Form1.Designer.cs , Program.cs .
I added myClass.cs that wants to talk the load bar.
I add progressBar1 bar to my form using the designer, setting Style:Marquee.
In Form1.cs' Form() constructor, I write
this.progressBar1.Visible = false;
This works. Intellisense 'sees' progresBar1.
code in Form1.cs can see and control progressBar1 declared in Form1.Designer.cs.
this makes sense to me.
But the functions which need to start and stop the load bar must live in myClass.cs.
I want to be able to code like this, within myClass.cs:
public void myFunction(){
Form1.progressBar1.visible=true
//do stuff that takes a bit of time
Form1.progressBar1.visible=false
}
This does not work. Intellisense cannot 'see' progresBar1 when typing code in myClass.cs.
In fact, intellisense cannot 'see' anything in Form1.cs from within myClass.cs.
No public propeties or functions added to Form1 ever become visible to intellisense.
This does not make sense to me, I am confused.
This seems like something you would want to do often and easily.
Some searching indicates that this blocking of external access to Form controls is by design. Something to do with 'decoupling' your logic code from GUI code, which makes sense in principal.So clearly there is an expected approach, yet an clear example is hard to find. I can only find examples of loadbars controlled from entirely within the Forms that declare them, or terse half-examples about creating and registering Events or using Invoke or other things I know too little about. There are many apparent solutions but none that I can see clearly apply to me, or that I am able to implement, in my ignorance.
I think I could do it if my Form were an instance.
[EDIT] nope. instance or not, Form1 controls never become exposed outside of Form1.cs
So, How do I to start and stop a Marquee style progressBar from a class other than the default Form1 in which it is declared, in the proper way?
Is there a clear and useful example somewhere?
You can't access your properties this way:
Form1.progressBar1
because Form1 is a type (not an instantiated object). The only methods or properties you can access with this approach have to be marked as static.
To answer your question of how to communicate, you probably want to use the event approach that you mentioned. First you need an event in your logic class:
public event Action<int> UpdateProgress;
Which is called just like a function:
if (UpdateProgress != null)
UpdateProgress(10);
This declares a new event using the Action generic delegate, which means the listening function has to return void and take one int as a parameter.
Then in your forms code, you'll have:
MyClass logic = new MyClass();
private void SomeFunction
{
logic.UpdateProgress += UpdateProgressBar;
}
private void UpdateProgressBar(int newProgress)
{
progressBar1.BeginInvoke(new Action(() =>
{
progressBar1.Value = newProgress;
}));
}
This creates a new instance of your logic class, and assigns the function "UpdateProgressBar" to be called whenever your logic class raises the UpdateProgressBar event. The function itself uses Dispatcher.BeginInvoke because your logic class is likely not running on the UI thread, and you can only do UI tasks from that thread.
There is a lot going on here, so please let me know if I can clarify anything for you!
I would create a model that has properties matching your form, and pass that around.
So you would make a new class like this...
using Windows.Forms;
public class Form1Model {
public ProgressBar progressBar { get; set; }
}
Then when you want to get to your other class holding that function you would create an instance of Form1Model, fill it, and call your function
var fm = new Form1Model {
progressBar = this.progressBar1;
};
otherClass.MyFunction(fm);
now you would have to change your function to accept the new model
public void MyFunction(Form1Model fm){
// do stuff
}
Another option is just making the function take an instance of the form, and not creating a model, but then you are going to be passing a lot of extra bits you probably won't care about
public void MyFunction(Form1 form){
// do stuff
}
Then on your form you would call the function like this
otherClass.myFunction(this);
I would recommend the first way over the second, you can control what data is being passed around
You are trying to access the type Form1 instead of the forms instance. I'll show you, how you can access the instance below.
I assume that Form1 is the applications main form that stays open as long as the application runs. When you create a WinForms application VS creates this code in Program.cs:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
A simple way to make your main form accessible throughout the application is to make it accessible via a public static property. Change the code like this
static class Program
{
public static Form1 MainForm { get; private set; }
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainForm = new Form1();
Application.Run(MainForm);
}
}
In Form1 create a property that exposes the progress bar's visibility:
public bool IsProgressBarVisible
{
get { return this.progressBar1.Visible; }
set { this.progressBar1.Visible = value; }
}
Now you can make the progress bar visible from any part of the program like this:
Program.MainForm.IsProgressBarVisible = true;
Another way of accessing the main form is, since it is always opened as the first form:
((Form1)Application.OpenForms(0)).IsProgressBarVisible = true;
However, it requires the form to be casted to the right type, since OpenForms returns a Form.
And don't forget: A Form is just a class like any other class. You can do almost everything you can make with other classes. So, communicating with forms is not very different than communication with other objects, as long as you are not using multithreading.
I'm new to C# (switching from Java) and i'm having a bit of trouble understanding GUI in C#
Ill just paste the Code i have and let that explain the most part.
Main Class:
Frame frame;
keepRunning = true;
public GraphicsComponent()
{
frame = new Frame();
frame.Show();
}
public void run()
{
while (keepRunning)
{
Console.WriteLine("Running");
}
}
public static void Main()
{
GraphicsComponent gameComponent = new GraphicsComponent();
gameComponent.run();
}
using the frame.Show() method, it appears and disappears for a quick moment. but the "running" loop is run.
The other way i have seen is another method called ShowDialoge(), that one keeps the window open but blocks the rest of the code from running until it is closed.
The way i want to use it is basically how GUIs work in Java. Where i can reference an object (and it's components) as needed in the rest of my code.
Thanks heaps!
Edit: There is alot of confusion, so here is my Frame class:
public partial class Frame : Form
{
public Frame()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
A normal way to kick off a winforms application is by file->new winforms project. WHen you do this you get "Program.cs", which contains your Main method - the entry point into your application. From there you have code like this:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ConfigForm());
}
In this example I kick off a new "ConfigForm"" that is a Form, and contains in it's ctor some code that looks like this:
public ConfigForm()
{
InitializeComponent();
// Other init work here
}
I create the form by right-clicking in the project and adding a new form.
You might want to look into the Application.Run method, see here. You can specify a "Main Form" which is going to be the main form that runs for the duration of the application, and when closed ends the application, or you can specify other options if you want. Perhaps the simplest solution is to start a main form, and since the form is live for the duration of the application, you can kick off other long-running code inside that form. If it's code that you want to be executing all the time performing some sort of background operation then you might want to fire it off in it's own thread. However, threading is hard so if you're new to c# / .net I'd try to stay away from threading unless you need it.
This has happened many times before, but I never bothered to figure out why, and now I am tired of it:
For instance, I derive a class from RichTextBox or Panel, I rebuild my project to have the class added to the VS designer toolbox, and then I drag & drop the custom user control to a Form. Everything works fine, and I can run my project...
The problem comes when I edit properties of the Form or the custom user control through the designer. Sometimes, the designer removes the initialization line from its code-behind, causing an exception in the designer and the executable because the control remains uninitialized.
In other words, the following line is removed from say, Form1.Designer.cs:
this.customRichTextBox1=new CustomRichTextBox();
No other line is removed from the code-behind, so the attributes of the custom control are still set, although the variable stays uninitialized.
My solution has always been to manually initialize my user control in the designer code-behind, but the designer eventually removes it again.
I believe that this does not happen when I build a Custom UserControl through the designer (but I am not completely sure of this). It only happens when I define something like the following manually:
class CustomRichTextBox:RichTextBox{}
This is so annoying. What am I doing wrong?
As #Cody requested, here are the steps to reproduce the problem. I am using VS2010, but I've had this problem since 2005, I think.
Step 1. Create new Windows Forms Application, any Framework
Step 2. Add the following class below your main Form class: (It just happens that this is the control that is causing me this problem this time.)
class CustomRichTextBox : RichTextBox
{
Timer tt = new Timer();
internal CustomRichTextBox()
{
tt.Tick += new EventHandler(tt_Tick);
tt.Interval = 200;
}
protected override void OnTextChanged(EventArgs e)
{
tt.Stop();
tt.Start();
}
void tt_Tick(object sender, EventArgs e)
{
System.Diagnostics.Trace.WriteLine("Hello world!");
}
}
Step 3. Press F6 to rebuild.
Step 4. Add the CustomRichTextBox control to your Form by dragging and dropping from the Toolbox.
Step 5. If you wish, you may press F5 to test the application, but it should work. Close the running application.
Step 6. Press F6 to rebuild, and at this point, the designer should crash with the following message: "The variable 'customRichTextBox1' is either undeclared or was never assigned." (In one case, the whole VS completely crashed, but the error is usually contained within the designer.)
Step 7. To correct the issue, go into the code-behind and initialize the variable, but next time you rebuild, the initialization line will be gone.
Thanks to everyone who tried answering my question and who posted comments that helped me diagnose and solve the problem.
The problem occurs when using an "internal" keyword with the control's constructor. Changing it to "public" fixes the problem. The reason for this behavior might be that the Designer's own classes cannot see the constructor because they are not within the namespace of my class unless it is marked public. This all makes sense, and I will use the public keyword from now on.
The class does not need to be in its own individual file or be the first declared class in the file as other answers suggested.
The following class works well because the constructor's keyword was changed to public.
class CustomRichTextBox : RichTextBox
{
Timer tt = new Timer();
public CustomRichTextBox()
{
tt.Tick += new EventHandler(tt_Tick);
tt.Interval = 200;
}
protected override void OnTextChanged(EventArgs e)
{
tt.Stop();
tt.Start();
}
void tt_Tick(object sender, EventArgs e)
{
System.Diagnostics.Trace.WriteLine("Hello world!");
}
}
Is your build set to Debug or it is Release?
I suppose that it is release as I think compiler optimizes the code and remove designer generated line.
Have you tried putting the control code in its own file? I've had problems even with the form designer in the past when the designer code was not int he first class in the file.
I had a similar problem that this posted helped me solve. I have a CustomControl that extends ComboBox, that class contained an internal private class YearItem. I've tried to highlight only the code needed to understand the problem and the solution.
public class YearsCbo : ComboBox //Inherits ComboBox
{
public YearsCbo() {
fill();
}
private void fill() { // <<<=== THIS METHOD ADDED ITEMS TO THE COMBOBOX
for(int idx = 0; idx < 25; idx++) {
this.Items.Add(new YearItem());
}
}
// Other code not shown
private class YearItem {} // <<<=== The VS designer can't access this class and yet
// it generated code to try to do so. That code then fails to compile.
// The compiler error rightfully says it is unable to access
// the private class YearItem
}
I could drag/drop that control YearsCbo onto a form and it worked correctly, but after I returned and edited the form the VS designer generated code that would not compile. The offending code something like this:
Dim YearItem1 As my.ns.YearsCbo.YearItem = New my.ns.YearsCbo.YearItem()
Dim YearItem2 As my.ns.YearsCbo.YearItem = New my.ns.YearsCbo.YearItem()
// This was repeated 25 times because in my constructor I created 25 of these
Me.YearsCbo1.Items.AddRange(New Object() {YearItem1, 2, 3, ..., YearItem25 });
Notice that the designer generated code which tried to access the private class. It didn't need to do that but for some reason it did.
Through trial and error, and this post: How to tell if .NET code is being run by Visual Studio designer came up with a solution:
I added a property to tell if I am running in the designer.
public bool HostedDesignMode
{
get
{
if (System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime)
return true;
else
return false;
}
}
I also changed the constructor so that it doesn't call fill() so when the designer runs, there are no items in the ComboBox so the designer doesn't feel the need to manually create those items.
The "fixed" code is shown below:
public class YearsCbo : ComboBox //Inherits ComboBox
{
public YearsCbo() {
if ( ! HostedDesignMode ) {
fill();
}
}
private class YearItem {} // <<<=== Now the VS Designer does not try to access this
}
This code was written using VS2012 Premium on Win7x64 OS (in case it matters).