abstract class CustomControl : UserControl
{
protected abstract int DoStuff();
}
class DetailControl : CustomControl
{
protected override int DoStuff()
{
// do stuff
return result;
}
}
J'ai déposé un DetailControl dans un formulaire. Le rendu est correct au moment de l'exécution, mais le concepteur affiche une erreur et ne s'ouvre pas car le contrôle utilisateur de base est abstrait.
Pour le moment, je réfléchis au correctif suivant, qui me semble plutôt faux, car je veux que les classes enfant soient forcées d'implémenter la méthode.
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;
}
}
Tout le monde a une meilleure idée sur la façon de résoudre ce problème?
Vous pouvez utiliser un TypeDescriptionProviderAttribute pour fournir une implémentation au moment du design concrète pour votre classe de base abstraite. Voir http://wonkitect.wordpress.com/2008/06/20/using-visual-studio-whidbey-to-design-abstract-forms/ pour plus de détails.
Ce que nous voulons
Premièrement, définissons la classe finale et la classe abstraite de base.
public class MyControl : AbstractControl
...
public abstract class AbstractControl : UserControl // Also works for Form
...
Maintenant, tout ce dont nous avons besoin est d’un fournisseur de description Description .
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);
}
}
Enfin, nous appliquons simplement un attribut TypeDescriptionProvider
au contrôle Abstract.
[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<AbstractControl, UserControl>))]
public abstract class AbstractControl : UserControl
...
Et c'est tout. Aucun contrôle moyen requis.
Et la classe de fournisseur peut être appliquée à autant de bases abstraites que nous le souhaitons dans la même solution.
Une autre solution consiste à utiliser des directives de prétraitement.
#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
...
}
Consultez ce lien pour plus d'informations sur ce sujet: Héritage d'un formulaire d'une classe abstraite (et son utilisation dans le concepteur)
La même solution a également été mentionnée dans ce fil de discussion MSDN, plus brièvement: UserControl, Inherited Control, Abstract class, (C #)
Ce n’est peut-être pas la solution la plus propre, mais c’est toujours la plus courte que j’ai trouvée.
Même si cette question a plusieurs années, j'aimerais ajouter ce que j'ai trouvé.
Si vous ne voulez pas toucher votre classe de base abstraite, vous pouvez faire ceci pirater :
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 cette façon, votre formulaire hérite d'un formulaire de base non abstrait et il est affiché dans le concepteur! Et vous conservez votre forme abstraite, mais seulement un niveau supplémentaire dans l'héritage. Etrange, n'est ce pas?
Je ne pouvais pas faire fonctionner la solution de 'Nicole Calinoiu'. Mais il y a un autre moyen facile directement dans Visual Studio :)
Plus de détails ici: ' http://www.codeproject.com/Articles/20845/How-to-derive-from-a-parent-form
Ce qui suit est une solution générique qui fonctionne pour moi, principalement. Il est basé sur le article d'une autre réponse. Parfois, cela fonctionnera et je pourrai concevoir ma UserControl
, puis j'ouvrirai le fichier. Le concepteur devra créer une instance de type 'MyApp.UserControlBase', mais ce ne sera pas possible car ce type est déclaré abstrait . " Je pense que je peux résoudre ce problème en nettoyant, en fermant le VS, en le rouvrant et en le reconstruisant. À l'heure actuelle, il semble se comporter. Bonne chance.
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);
}
}
}
J'étais nouveau à ceci dans UWP et cela me rendait fou. Je n'ai pas pensé à une classe de base abstraite pour un UserControl. Je suis allé dans une direction différente. J'ai créé une classe Helper non-xaml ... HBase
. Chaque vue, disons VContract
, avait un assistant correspondant appelé HContract
. Tout le code de spécialité pour chaque vue y était logé. Les conversations qui auraient eu lieu entre ViewModel VMContract
et View VContract
passent maintenant par HContract
. Nous pouvons imposer le comportement de HWhatever
avec IHBase
. Ce n'est pas vraiment une réponse à la question du PO, mais montre une approche alternative. Toutes les vues sont maintenant essentiellement des coquillages. Que vous x: Liez-vous à VContract
ou HContract
est une décision à prendre. J'ai choisi la méthode VContract
et à la fin je pense que c'était une erreur.
Le problème UWP avec les exceptions en mode Conception est maintenant facilement résolu avec:
if (false == Windows.ApplicationModel.DesignMode.DesignModeEnabled)
{
HContract = new HContract(this);
// Put code here that fails in Design mode but must at run time
}
Je viens de transformer la classe de base abstraite en une classe concrète en définissant les méthodes "abstraites" comme virtuelles et en leur lançant une exception, juste au cas où des classes dérivées coquines essaient d'appeler l'implémentation Base.
par exemple.
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 principale différence pratique entre cette classe et une vraie classe de base abstraite est que vous obtenez des erreurs d'exécution lors de l'appel de l'implémentation de base - alors que si la base était réellement abstraite, le compilateur interdirait les appels accidentels à l'implémentation de la classe de base. Ce qui n’a pas d’importance pour moi et me permet de faire appel au concepteur sans me soucier des solutions plus complexes et fastidieuses suggérées par d’autres ...
PS - Akuma - Vous devriez pouvoir modifier votre classe d’interface utilisateur abstraite dans le concepteur. Je n’ai pas le temps de vérifier cela pour le moment, mais j’ai bien compris que le concepteur n’a besoin que d’instancier la classe BASE. Tant que la base de la classe que vous concevez est en béton, peu importe la classe.
J'ai résolu ce problème dans UWP dans mon contrôle personnalisé.
Mon cas
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));
}
Déclaration
< MyCustomControl:MainModel ShowLabel=True />
Solution
Il suffit de remplacer un style factice dans les ressources génériques.
<Style TargetType="local:MainModel" />
Cordialement,
Samuel