abstract class CustomControl : UserControl
{
protected abstra开发者_StackOverflow社区ct int DoStuff();
}
class DetailControl : CustomControl
{
protected override int DoStuff()
{
// do stuff
return result;
}
}
I dropped a DetailControl in a form. It renders correctly at runtime, but the designer displays an error and won't open because the base user control is abstract.
For the moment, I'm contemplating the following patch, which seems pretty wrong to me, as I want the child classes to be forced to implement the method.
class CustomControl : UserControl
{
protected virtual int DoStuff()
{
throw new InvalidOperationException("This method must be overriden.");
}
}
class DetailControl : CustomControl
{
protected override int DoStuff()
{
// do stuff
return result;
}
}
Anyone has a better idea on how to work my way around this problem?
What we want
First, let's define the final class and the base abstract class.
public class MyControl : AbstractControl
...
public abstract class AbstractControl : UserControl // Also works for Form
...
Now all we need is a Description provider.
public class AbstractControlDescriptionProvider<TAbstract, TBase> : TypeDescriptionProvider
{
public AbstractControlDescriptionProvider()
: base(TypeDescriptor.GetProvider(typeof(TAbstract)))
{
}
public override Type GetReflectionType(Type objectType, object instance)
{
if (objectType == typeof(TAbstract))
return typeof(TBase);
return base.GetReflectionType(objectType, instance);
}
public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args)
{
if (objectType == typeof(TAbstract))
objectType = typeof(TBase);
return base.CreateInstance(provider, objectType, argTypes, args);
}
}
Finally we just apply a TypeDescriptionProvider
attribute to the Abstract control.
[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<AbstractControl, UserControl>))]
public abstract class AbstractControl : UserControl
...
And that's it. No middle control required.
And the provider class can be applied to as many Abstract bases as we want in the same solution.
You can use a TypeDescriptionProviderAttribute to provide a concrete design-time implementation for your abstract base class. See http://wonkitect.wordpress.com/2008/06/20/using-visual-studio-whidbey-to-design-abstract-forms/ for details.
Another way to solve this is using pre-processing directives.
#if DEBUG
public class UserControlAdmonEntidad : UserControl, IAdmonEntidad
#else
public abstract class UserControlAdmonEntidad : UserControl, IAdmonEntidad
#endif
{
...
#if DEBUG
public virtual object DoSomething()
{
throw new NotImplementedException("This method must be implemented!!!");
}
#else
public abstract object DoSomething();
#endif
...
}
See this link for more information regarding this topic: Inheriting a Form from an Abstract Class (and Making it Work in the Designer)
The same solution was also mentioned in this MSDN forum thread, in a briefer way: UserControl, Inherited Control, Abstract class, (C#)
Maybe is not the cleaner solution, but it's still the shortest I have found.
Even though this question is years old, I'd like to add what I've found.
If you don't want to touch your abstract base class, you can do this hack:
abstract class CustomControl : UserControl
{
protected abstract int DoStuff();
}
class BaseDetailControl : CustomControl
{
protected override int DoStuff()
{
throw new InvalidOperationException("This method must be overriden.");
}
}
class DetailControl : BaseDetailControl
{
protected override int DoStuff()
{
// do stuff
return result;
}
}
This way, your form inherits from a non-abstract base form and it's displayed in the designer! And you keep your abstract form, but only one more level up in the inheritance. Strange, isn't it?
I couldn't make work the solution of 'Nicole Calinoiu'. But there is an other easy way directly in visual studio:)
- Create new project
- Add new element 'userControl' and add one button for example
- Add new element 'userControl' Inhereted UserControl then select the inhereted userControl.
More details here : 'http://www.codeproject.com/Articles/20845/How-to-derive-from-a-parent-form
The following is a generic solution that works for me, mostly. It is based on the article from another answer. Sometimes it will work, and I can design my UserControl
, and then later I'll open the file and it will give the "The designer must create an instance of type 'MyApp.UserControlBase' but it cannot because the type is declared as abstract." I think I can fix it by cleaning, closing VS, reopening VS, and rebuilding. Right now it seems to be behaving. Good luck.
namespace MyApp
{
using System;
using System.ComponentModel;
/// <summary>
/// Replaces a class of <typeparamref name="T"/> with a class of
/// <typeparamref name="TReplace"/> during design. Useful for
/// replacing abstract <see cref="Component"/>s with mock concrete
/// subclasses so that designer doesn't complain about trying to instantiate
/// abstract classes (designer does this when you try to instantiate
/// a class that derives from the abstract <see cref="Component"/>.
///
/// To use, apply a <see cref="TypeDescriptionProviderAttribute"/> to the
/// class <typeparamref name="T"/>, and instantiate the attribute with
/// <code>SwitchTypeDescriptionProvider<T, TReplace>)</code>.
///
/// E.g.:
/// <code>
/// [TypeDescriptionProvider(typeof(ReplaceTypeDescriptionProvider<T, TReplace>))]
/// public abstract class T
/// {
/// // abstract members, etc
/// }
///
/// public class TReplace : T
/// {
/// // Implement <typeparamref name="T"/>'s abstract members.
/// }
/// </code>
///
/// </summary>
/// <typeparam name="T">
/// The type replaced, and the type to which the
/// <see cref="TypeDescriptionProviderAttribute"/> must be
/// applied
/// </typeparam>
/// <typeparam name="TReplace">
/// The type that replaces <typeparamref name="T"/>.
/// </typeparam>
class ReplaceTypeDescriptionProvider<T, TReplace> : TypeDescriptionProvider
{
public ReplaceTypeDescriptionProvider() :
base(TypeDescriptor.GetProvider(typeof(T)))
{
// Nada
}
public override Type GetReflectionType(Type objectType, object instance)
{
if (objectType == typeof(T))
{
return typeof(TReplace);
}
return base.GetReflectionType(objectType, instance);
}
public override object CreateInstance(
IServiceProvider provider,
Type objectType,
Type[] argTypes,
object[] args)
{
if (objectType == typeof(T))
{
objectType = typeof(TReplace);
}
return base.CreateInstance(provider, objectType, argTypes, args);
}
}
}
I just make the abstract base class into a concrete one by defining the "abstract" methods as virtual, and throwing an exception in them, just in case any naughty derived classes try to call Base implementation.
e.g.
class Base : UserControl
{
protected virtual void BlowUp()
{
throw new NotSupportedException("This method MUST be overriden by ALL derived classes.");
}
class Derived : Base
{
protected override void BlowUp()
{
// Do stuff, but don't call base implementation,
// just like you wouldn't (can't actually) if the Base was really abstract.
// BTW - doesn't blow up any more ;)
}
The main practical difference between this and a real abstract base class is you get run time errors when calling the base implementation - whereas if the Base was actually abstract, the compiler would disallow an accidental calls to the Base class implementation. Which isn't a big deal for me and allows me to use the designer without worrying about more complex and time consuming work arounds suggested by others...
PS - Akuma - you should be able to edit your abstract UI class in the designer. I don't have time to check this right now, but it is my understanding that the designer only needs to instantiate the BASE class. As long as the base of the class you are designing is concrete, it doesn't matter what the designed class is.
I resolved this issue in UWP in my custom control.
My Case
public abstract class BaseModel : DependencyObject
{
...
}
public class MainModel : BaseModel
{
public bool ShowLabel
{
get{ return (bool)GetValue(ShowLabelProperty); }
set{ SetValue(ShowLabelProperty, value) }
}
public static readonly DependencyProperty ShowLabelProperty =
DependencyProperty.Register("ShowLabel",typeof(bool), typeof(MainModel), new PropertyMetadata(false));
}
Declaration
< MyCustomControl:MainModel ShowLabel=True />
Solution
Just override a dummy style in the generic resources.
<Style TargetType="local:MainModel" />
Regards,
Samuel
I was new to this in UWP and it was driving me nuts. I didn't think of an abstract base class for a UserControl. I went in a different direction. I created a non-xaml Helper class ... HBase
. Each View, say VContract
, had a corresponding Helper called HContract
. All the speciality code for each view was lodged there. The conversations that would have been between the ViewModel VMContract
and View VContract
now pass through HContract
. We can enforce how HWhatever
behaves with IHBase
. It's not truly an answer to the OP's question, but does show an alternative approach. All the Views are now basically shells. Whether you x:Bind to the VContract
or HContract
is a decision for you to make. I chose the VContract
way and in the end I think that was a mistake.
The UWP problem with exceptions in Design Mode is now easily fixed with:
if (false == Windows.ApplicationModel.DesignMode.DesignModeEnabled)
{
HContract = new HContract(this);
// Put code here that fails in Design mode but must at run time
}
精彩评论