Custom User Control Not Initialized in Auto-Generated Code - c#

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).

Related

C# Custom EventHandler

i have a custom event handler in the page, and it is called by the user controls of it.
everything is fine, except there is error display in the code ( red highlighted), but the program can be compiled, and able to run with no apparent error.
but i want to fix (or understand) the reason why the visual studio showed error for that
the error is
the code is
-PAGE
Operator_agentcontrol2 agentcontrol = (Operator_agentcontrol2)Page.LoadControl("~/operator/agentcontrol2.ascx");
agentcontrol.displayLevel = (int)Common.WinLose_Level.lvChild4 + 10 + (Panel_agents.Controls.Count * 10);
agentcontrol.AppendProcess += Append_UC_Progress;//Error line
the event in the page-
public void Append_UC_Progress(object sender, EventArgs e)
{
Common.WinLose_ProgressStage wps = (Common.WinLose_ProgressStage)e;
progress.AppendProgress(wps);
SaveProgressVS();
}
-USER CONTROL
public partial class Operator_agentcontrol2 : System.Web.UI.UserControl
{
public event EventHandler<Common.WinLose_ProgressStage> AppendProcess;
}
Thanks
---Update---
I have tried to follow https://msdn.microsoft.com/en-us/library/db0etb8x(v=vs.85).aspx for the custom event handler.
but then i got this error
---Update---
Eventually I found that actually my scenario doesn't require to use something like EventHandler
i have changed the code in user control
public partial class Operator_agentcontrol2 : System.Web.UI.UserControl
{
public event EventHandler AppendProcess;
}
By doing this the error is gone, and the user control still able to call the Page's function successfully with an object Common.WinLose_ProgressStage.
As far as I can see, two errors are being reported...
1 - It cannot find a suitable overload for Append_UC_Progess (which takes a Common.WinLose_ProgressStage as an argument)
2 - The assembly containing Common.WinLose_ProgressStage is not referenced.
What I would suggest is happening, is that once it is all compiled the assembly containing Common.WinLose_ProgressStage gets pulled in (perhaps by another referenced assembly), and thus it is noticed that it inherits from EventArgs. It can therefore find a suitable overload of Append_UC_Progess and it all resolves ok.
In order to get rid of the error, I would suggest explicitly referencing the assembly containing Common.WinLose_ProgressStage, so that Visual Studio can see the inheritance tree at design time.

Manually calling System.GC.Collect() changes action of Windows Forms application

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.

How do I communicate with a control of a Form from another class?

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.

Visual Studio blocked on design mode after extending a user control

I have a project in Visual Studio 2010 (converted from 2008), and I have created a User control, like this:
namespace Common.Controls
{
public partial class Panel_BaseMap : UserControl
{
public Panel_BaseMap()
{
//Some properties initialization here, just like = new X();
InitializeComponent();
}
private void BaseMapPanel_Load(object sender, EventArgs e) {
//Here, a new Thread is initialized and started.
}
}
}
I don't have any problem with this, it is opened in Design Mode without any problem. But I have created a new UserControl that extends to the first one, like this:
using Common.Controls;
namespace BC.controls
{
public partial class MapPanel : Panel_BaseMap
{
public MapPanel()
{
InitializeComponent();
}
}
}
Well, in the very moment I try to open this new control on design mode, Visual Studio gets totally blocked, and I have to force it to close because it doesn't respond. I have tried many things, like for example:
public MapPanel()
{
if (!this.DesignMode)
InitializeComponent();
}
Still blocked. I have opened a second instance of Visual Studio, then on the first one "Debug --> Attach to process --> devenv" and I have put a breakpoint on the Load method and on both constructors on the second instance. The result: both instances totally blocked.
Can anyone help me, please?
Thank you very much in advance!
Ok I've found the problem.
Some code was executed by the designer, and that code crashed the application. It was inside a try-catch, and inside the catch I logged the error with a method that tried to load an encripted file with this: Directory.GetFiles(Application.StartupPath, "*.xml"). The problem is that Application.StartupPath is not my application path, but "C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\Microsoft.Data.ConnectionUI.xml". So, when I tried to decrypt it, it throwed another exception, that was logged with the same method... so infinite loop!

EF in a UserControl can't see the app.config?

I just created a user control.
This control also makes use of my static Entity Framework class to load two comboboxes.
All is well and runs without a problem. Design and runtime are working.
Then when I stop the application all the forms that contain my UserControl don't work any more in design time. I just see two errors:
Error1:
The specified named connection is either not found in the configuration, not intended to be used with the EntityClient provider, or not valid.
Error 2:
The variable ccArtikelVelden is either undeclared or was never assigned.
(ccArtikelVelde is my UserControl)
Runtime everything is still working
My static EF Repositoy class:
public class BSManagerData
{
private static BSManagerEntities _entities;
public static BSManagerEntities Entities
{
get
{
if (_entities == null)
_entities = new BSManagerEntities();
return _entities;
}
set
{
_entities = value;
}
}
}
Some logic happening in my UserControl to load the data in the comboboxes:
private void LaadCbx()
{
cbxCategorie.DataSource = (from c in BSManagerData.Entities.Categories
select c).ToList();
cbxCategorie.DisplayMember = "Naam";
cbxCategorie.ValueMember = "Id";
}
private void cbxCategorie_SelectedIndexChanged(object sender, EventArgs e)
{
cbxFabrikant.DataSource = from f in BSManagerData.Entities.Fabrikants
where f.Categorie.Id == ((Categorie)cbxCategorie.SelectedItem).Id
select f;
cbxFabrikant.DisplayMember = "Naam";
cbxFabrikant.ValueMember = "Id";
}
The only way to make my forms work again, design time, is to comment out the EF part in the UserControl (see above) and rebuild.
It's very strange, everything is in the same assembly, same namespace (for the sake of simplicity).
Anyone an idea?
Looks like you're somehow executing database code in design mode. To prevent this, hunt down the control and method causing this, and use:
if (DesignMode)
return
Also, it's a very bad idea to cache the database context statically. It will cause problems with multithreading, and also when you're doing inserts and deletes. The database context is meant to be used for a single "unit of work", is adding 2, and removing 3 other objects and calling SaveChanges once.
I faced the same problem,
In my case , I have added some database codes in user control loading event which were using some libraries, which weren't loaded till runtime.
Hence it is advisable, not to write any database code in user control load event.
Hope , this helps you !
this error show if you call the function "LaadCbx()" on constructor of userControl.
because the initialization on entity framework exist into this function.
the solution is to call this function "LaadCbx()" in the constructor of the parent form.

Categories

Resources