c# - una - Herencia abstracta de UserControl en el diseñador de Visual Studio
polimorfismo c# (9)
Aunque esta pregunta tiene años, me gustaría agregar lo que encontré.
Si no quieres tocar tu clase base abstracta, puedes hacer este truco :
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;
}
}
De esta manera, su formulario se hereda de un formulario base no abstracto y se muestra en el diseñador. Y mantienes tu forma abstracta, pero solo un nivel más arriba en la herencia. Extraño, ¿no es así?
abstract class CustomControl : UserControl
{
protected abstract int DoStuff();
}
class DetailControl : CustomControl
{
protected override int DoStuff()
{
// do stuff
return result;
}
}
Dejé caer un DetailControl en un formulario. Se procesa correctamente en el tiempo de ejecución, pero el diseñador muestra un error y no se abrirá porque el control de usuario base es abstracto.
Por el momento, estoy contemplando el siguiente parche, que me parece bastante incorrecto, ya que quiero que las clases de niños se vean obligadas a implementar el método.
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;
}
}
¿Alguien tiene una mejor idea sobre cómo resolver este problema?
La siguiente es una solución genérica que funciona para mí, en su mayoría. Se basa en el http://wonkitect.wordpress.com/2008/06/20/using-visual-studio-whidbey-to-design-abstract-forms/ de otra respuesta. A veces funcionará, y puedo diseñar mi UserControl
, y luego abriré el archivo y aparecerá el mensaje "El diseñador debe crear una instancia del tipo ''MyApp.UserControlBase'', pero no puede porque el tipo está declarado como abstracto. . " Creo que puedo arreglarlo limpiando, cerrando VS, volviendo a abrir VS y reconstruyendo. En este momento parece que se está comportando. Buena suerte.
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);
}
}
}
No pude hacer funcionar la solución de ''Nicole Calinoiu''. Pero hay otra manera fácil directamente en el estudio visual :)
- Crear nuevo proyecto
- Agregue el nuevo elemento ''userControl'' y agregue un botón por ejemplo
- Agregue un nuevo elemento ''UserControl'' El control de usuario heredado y luego seleccione el control de usuario heredado.
Más detalles aquí: '' http://www.codeproject.com/Articles/20845/How-to-derive-from-a-parent-form
Otra forma de resolver esto es utilizando directivas de preprocesamiento.
#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
...
}
Vea este enlace para obtener más información sobre este tema: Herencia de un formulario de una clase abstracta (y cómo hacerlo funcionar en el diseñador)
La misma solución también se mencionó en este hilo del foro de MSDN, de manera más breve: UserControl, control heredado, clase abstracta, (C #)
Tal vez no sea la solución más limpia, pero sigue siendo la más corta que he encontrado.
Puede usar un TypeDescriptionProviderAttribute para proporcionar una implementación concreta en tiempo de diseño para su clase base abstracta. Consulte http://wonkitect.wordpress.com/2008/06/20/using-visual-studio-whidbey-to-design-abstract-forms/ para obtener más información.
Resolví este problema en UWP en mi control personalizado.
Mi caso
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));
}
Declaración
< MyCustomControl:MainModel ShowLabel=True />
Solución
Simplemente anule un estilo ficticio en los recursos genéricos.
<Style TargetType="local:MainModel" />
Saludos,
Samuel
Simplemente convierto la clase base abstracta en una concreta definiendo los métodos "abstractos" como virtuales, y lanzando una excepción en ellos, en caso de que alguna clase derivada traviesa intente llamar a la implementación base.
p.ej
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 ;)
}
La principal diferencia práctica entre esto y una clase base abstracta real es que se obtienen errores en el tiempo de ejecución al llamar a la implementación base, mientras que si la Base fuera realmente abstracta, el compilador no permitiría llamadas accidentales a la implementación de la clase Base. Lo que no es un gran problema para mí y me permite usar el diseñador sin preocuparme por las tareas más complejas y lentas sugeridas por otros ...
PS - Akuma: debería poder editar su clase de interfaz de usuario abstracta en el diseñador. No tengo tiempo para comprobarlo ahora mismo, pero tengo entendido que el diseñador solo necesita crear una instancia de la clase BASE. Siempre que la base de la clase que está diseñando sea concreta, no importa cuál sea la clase diseñada.
Yo era nuevo en esto en UWP y me estaba volviendo loco. No pensé en una clase base abstracta para un UserControl. Fui en una dirección diferente. Creé una clase que no es xaml Helper ... HBase
. Cada Vista, digamos VContract
, tenía un Ayudante correspondiente llamado HContract
. Todo el código de especialidad para cada vista fue alojado allí. Las conversaciones que habrían sido entre ViewModel VMContract
y View VContract
ahora pasan a través de HContract
. Podemos hacer cumplir cómo se comporta IHBase
con IHBase
. No es realmente una respuesta a la pregunta del OP, pero muestra un enfoque alternativo. Todas las vistas son ahora básicamente conchas. Ya sea que usted: x: Bind to VContract
o HContract
es una decisión que debe tomar. Escogí el modo VContract
y al final creo que fue un error.
El problema de UWP con excepciones en el modo de diseño ahora se soluciona fácilmente con:
if (false == Windows.ApplicationModel.DesignMode.DesignModeEnabled)
{
HContract = new HContract(this);
// Put code here that fails in Design mode but must at run time
}
Lo que nosotros queremos
Primero, definamos la clase final y la clase abstracta base.
public class MyControl : AbstractControl
...
public abstract class AbstractControl : UserControl // Also works for Form
...
Ahora todo lo que necesitamos es un proveedor de Descripción .
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);
}
}
Finalmente, simplemente aplicamos un atributo TypeDescriptionProvider
al control Abstract.
[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<AbstractControl, UserControl>))]
public abstract class AbstractControl : UserControl
...
Y eso es. No se requiere control intermedio.
Y la clase de proveedor se puede aplicar a tantas bases abstractas como queramos en la misma solución.