web-dev-qa-db-fra.com

Héritage abstrait UserControl dans le concepteur Visual Studio

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?

29
asmo

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.

17
Nicole Calinoiu

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.

43
jucardi

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.

6

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?

2
Andrew

Je ne pouvais pas faire fonctionner la solution de 'Nicole Calinoiu'. Mais il y a un autre moyen facile directement dans Visual Studio :)

  1. Créer un nouveau projet
  2. Ajouter un nouvel élément 'userControl' et ajouter un bouton par exemple
  3. Ajouter un nouvel élément 'userControl' Inhereted UserControl puis sélectionnez le userControl hérité.

Plus de détails ici: ' http://www.codeproject.com/Articles/20845/How-to-derive-from-a-parent-form

2
Julian50

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);
        }
    }
}
2
Carl G

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               
}
0
Paulustrious

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.

0
Tim

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

0
Samuel