Fix embedded resources for a generic UserControl - c#

During a refactoring, I added a generic type parameter to MyControl, a class derived from UserControl. So my class is now MyControl<T>.
Now I get an error at runtime stating that the embedded resource file MyControl`1.resources cannot be found. A quick look with .NET Reflector shows that the resource file is actually called MyControl.resources, without the `1.
At the start of the MyControl<T>.InitializeComponent method there is this line which is probably the one causing problems:
System.ComponentModel.ComponentResourceManager resources =
new System.ComponentModel.ComponentResourceManager(
typeof(MyControl<>));
How do I force the ComponentResourceManager to use the embedded resource file MyControl.resources? Other ways to resolve this issue are also welcome.

Turns out you can override the resource filename to load by inheriting from ComponentResourceManager like this:
using System;
using System.ComponentModel;
internal class CustomComponentResourceManager : ComponentResourceManager
{
public CustomComponentResourceManager(Type type, string resourceName)
: base(type)
{
this.BaseNameField = resourceName;
}
}
Now I can make sure that the resource manager loads MyControl.resources like this:
System.ComponentModel.ComponentResourceManager resources =
new CustomComponentResourceManager(typeof(MyControl<>), "MyControl");
This seems to work.
edit: the above line is overwritten if you use the designer, because it is in the
generated code region. I avoid the designer and make use of version control tools to revert any unwanted changes, but the solution is not ideal.

In addition to Wim's technique, you can also declare a non-generic base control that has the same name as your generic class, and have your generic control/form derive from that non-generic base class.
This way you can trick both the designer and the compiler into using the resource file from your generic class, and you get permanent designer support once the base class is setup without having to fiddle in the .designer file everytime you rebuild :
// Empty stub class, must be in a different file (added as a new class, not UserControl
// or Form template)
public class MyControl : UserControl
{
}
// Generic class
public class MyControl<T> : MyControl
{
// ...
}
The only requirements are to have exactly the same name for your generic class and its base class, and that the base class must be in another class file, otherwise the designer complains about not finding one of the two classes.
PS. I tested this with forms, but it should work the same with controls.

On my Visual Studio 2008 I have this error:
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MyControl));
Using the generic type 'WindowsFormsApplication1.UserControl1' requires '1' type arguments.
Notice that in my case code was generated without parentheses, <>, after the class name.
It is becoming interesting, see ImageList autogenerates non-compiling code in a Generic User Control.
What they said:
Posted by Microsoft on 7/6/2005 at 2:49 PM
This is an interesting bug. You've hit upon a generic scneario that we do not support in the Windows Forms designer. We will not be able to add support for this in the Whidbey (my note: Visual Studio 2008?) release. We will consider this for a future version. As a workaround, you can use the designer to create a none generic UserControl with a public Type property and then create a generic class that inherits from it and passes T into the base classes Type property.
I suppose this control cannot be designed in the Visual Studio forms designer either.

The simplest and easiest workaround is to make a dummy class for the autogenerated typeof(). You do not need to inherit from it or even expose it to the outside:
// Non-generic name so that autogenerated resource loading code is happy
internal sealed class GridEditorForm
{
}
(In my experience, the time required getting the designer to work around generics was not worth the ideal coolness generics can provide. I won't be using generic windows forms or controls again.)

Related

UserControl with generic control - broken in designer

I have a WinForm-UserControl with a generic type for a control. To create the instance, I changed the Designer code:
public class TimeBarForm<T> : where T : TimeBarPanel
{
protected void InitializeComponent()
{
// ...
this.m_timeBarPanel = (T)Activator.CreateInstance(typeof(T));
// ...
}
}
This works fine at compile time but on design time it's broken. On the TimeBarForm:
Failed to parse method 'InitializeComponent'. The parser reported the following error
'Type parameters are not suppported Parameter name: typeSymbol'.
Please look in the Task List for potential errors.
The derived classes just show the default designer for an empty user control.
I also tried to pass the type in the constructor but VS complains that I should not touch autogenerated code (It dosn't like the if-conditions). I want a generic UserControl where I can decide about the specialization of an abstract class/control in a derived type and I should still be able to use the designer in the base class. I'm open to other suggestions to solve this as this might not be the best solution. I'm not very used to UserControl-design.
VS 2015/ .Net 4.6
I've done a somewhat dirty workaround but I can use the designer for the base and derived classes. I removed the generic and replaced the Activator-class with a call to the constructor. When I'm done with designing the base class I coment this line. The derived classes call the constructor to pass the instance:
public TimeBarForm(TimeBarPanel timeBarPanel)
{
this.m_timeBarPanel = timeBarPanel;
InitializeComponent();
}
To make the designer for the derived classes happy, a second constructor provides a default instance:
public TimeBarForm()
{
this.m_timeBarPanel = new TimeBarPanel();
InitializeComponent();
}
Not pretty but I can live with it.

Extending a class with a new nested class

first question so I'm open to advice on effectively participating in the StackOverflow community as well as pertaining to the question.
I'm working on a text-based UI in C#. I have an abstract window class and an abstract control class, each of which implements common functionality for the types that inherit them (e.g. pop-up windows or text box controls). Currently, within a program that might implement the library, a developer would have to create window objects and control objects, and then add the controls to their respective windows, and the windows to a window manager class, like this:
var mainWindow = new MainWindow(...);
var textBox1 = new TextBox(...);
mainWindow.AddControl(textBox1);
WindowManager.Add(mainWindow);
This works, but it's a bit clunky. Since a control should never have to exist outside of a window, I was hoping to implement the control types as nested types. However, to maintain extensibility of the program, I'd like for there to be a way to extend the window class with new control types. My question is this: Should I use reflection, or rely on developers using container classes to extend the window class? Alternatively, is there a better way to structure the program than how it's currently laid out?
I've also considered using generics, e.g.:
public abstract class Window : DrawableObject, IWindow
{
public void AddControl <T>(object[] constructorArgs) where T : class, IControl
{
}
}
I'm aiming for ease of implementation without sacrificing extensibility/loose coupling. Thanks in advance for any thoughts!
EDIT: Should clarify, the primary reason for this is to fix some weirdness with how Windows and Controls cooperate. Each control has a parentWindow property which is used to access the window on which a control resides, for various purposes like creating an exit button for a particular window, etc.
Right now, this property is passed to the constructor, but that seems redundant to me since after doing so you have to add the control to the window's control list. I'd like to find a way to set this property when the control is added to a window instead, but restrict this action to when the control is added only, to prevent potential problems if the parentWindow property is changed outside of this context.
The way you coded AddControl method:
public void AddControl <T>(object[] constructorArgs)
where T : class, IControl
{
}
You intend developers to just provide type and your AddControl method will create an instance of it using constructorArgs. This method itself implicitly forces you to use reflection. Anything else does not stand a chance. Because To Add control of type T, Creating Instance of Control of type T is necessary. Since your Window class does not have a clue about T reflection is the only solution.
To facilitate other approaches, you might want to consider few overloads of AddControl.
public virtual T AddControl <T>()
where T : class, new(),IControl
{
//now you can create instance no reflection required
var control = new T();
this.Controls.Add(control);
return control;
}
public void AddControl <T>(T control)
where T : class, IControl
{
}
public abstract void AddControl <T>(object[] constructorArgs)
where T : class, IControl;
Creating an abstract method passes onus of implementation on child class and creating new instance of T can be handled the assuming type of T is known there or at-least all cases of known types of what T might be are handled.
It's a wide scope topic and I guess subjective as well. The best use of OOP is to achieve a design which fits your logical objective whatever that maybe.

Add generics to form

When I add a generic type to a form in C# I get errors like
InitializeComponent does not exist in the current context
and the same for all of my components.
Here is the code:
public partial class Form1<T> : Form
{
public Form1()
{
InitializeComponent();
}
}
First and foremost, Form1 is a partial class, it exists in more than one file.
Since you changed the Form1.cs file to have a generic Form1<T>, you also need to change the corresponding Form1.Designer.cs file to have a generic class.
Otherwise the C# compiler looks upon the two files as containing two distinct classes, one Form1<T> and one Form1. While they technically have the same name, they're distinct. You can declare a generic and a non-generic type with the same name at the same time.
So change both files.
Now, having said that, you cannot have generic forms. When you succeed in getting the compiler to compile the code (with the above described change) you will find that the form designer in Visual Studio is no longer happy with you and is unable to visually design the form.
As mentioned in the comments to this answer, if you can live without the form designer then this won't be a showstopper.
However, if you cannot live without the visual form designer then this cannot be fixed, other than to remove the generic parameters to Form1 that you added in the first place.
So you should try to find a different way to do what you want.
InitializeComponent() is a method that Visual studio generates automatically for the forms created using visual designer. This method is located in your_form_name.Designer.cs class that is "visual" part of your form where controls placed on form defined.
Probably you've just copy-pasted it from another form created in such a way. If your custom class doesn't has "visual" part like Form1.Designer.cs class in your solution - then just delete this method call from constructor. Otherwise make sure your "visual" class part has this method.

How do I use nested namespaces for custom controls?

I have defined a user control, ExpressionControl, under the namespace:
TriggerEditor.UserControls
I have a form, "IfEditor", under the namespace:
TriggerEditor.Forms
When I add an instance of my control to the form (naming it expCondition), the following code is generated in the Designer to create the control:
this.expCondition = new TriggerEditor.UserControls.ExpressionControl();
This results in the following compilation error:
The type name 'UserControls' does not exist in the type 'TriggerEditor.Forms.TriggerEditor'
I don't understand why it is looking in "TriggerEditor.Forms.TriggerEditor"; that's not what the code says. Now, I can resolve this error manually by modifying the line, removing the "TriggerEditor." that preceeds "TriggerControls", as such:
this.expCondition = new UserControls.ExpressionControl();
This satisfies the compiler, however I obviously don't want to do this every time I add an instance of my ExpressionControl to a form. How do I avoid this situation?
It looks like you might have a TriggerEditor class within the TriggerEditor.Forms namespace; is this the case?
If so, because the TriggerEditor class is within your current namespace, you are "closer" to this class, and therefore it is looking for a subclass.
Avoid using the same name for a class and a namespace!?
Apparently, in the namespace TriggerEditor.Forms, you have a class named TriggerEditor in the same namespace as your IfEditor class.
So, when looking for TriggerEditor.UserControls.ExpressionControl, the compiler looks in the TriggerEditor class (which is in the same namespace, so closer) instead of the TriggerEditor namespace...
Normally, to avoid that, you would use a namespace alias qualifier, but being in a Designer generated class, you can't really control that.

Adding Interfaces to DataContext Classes

I am using Linq-To-SQL and I would like to attach an interface to each of my entities.
I can edit the designer.cs file and accomplish this.
However, when I make a changes to the dbml through the UI it rewrites the whole designer.cs and I lose my changes.
Am I just going to have to deal with it, or is there a way to get around it?
I am doing this in the designer.cs file(the IMatchable is a custom interface of mine):
public partial class Error : INotifyPropertyChanging, INotifyPropertyChanged, IMatchable
{
...
}
Don't edit the designer file; the beauty of partial classes is that you can create a separate file with just
public partial class Error : IMatchable
{ }
(assuming that we are using implicit interface implementaion by virtue of having properties that match the required interface)
Small word of caution though: if you are using VS2008 and you have MyClasses.dbml and MyClasses.designer.cs, do not call this file MyClasses.cs - there is a bug in VS2008 that makes this a nuisance (you have to keep moving the using directives inside the namespace, or the code-generator breaks) - fixed in VS2010 though.
Also, if it was a single interface, that every type in your model implemented, you can cheat by specifying that at the object base-type in the DBML. The designer doesn't show this option, but if you edit the DBML manually it works fine.

Categories