web-dev-qa-db-ja.com

Visual Studioデザイナーでの抽象UserControl継承

abstract class CustomControl : UserControl 
{
    protected abstract int DoStuff();
}

class DetailControl : CustomControl
{
    protected override int DoStuff()
    { 
        // do stuff
        return result;
    }
}

DetailControlをフォームにドロップしました。実行時に正しくレンダリングされますが、ベースユーザーコントロールは抽象的であるため、デザイナーはエラーを表示して開きません。

とりあえず、子クラスにメソッドの実装を強制させたいので、次のパッチを考えています。これはかなり間違っているようです。

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;
    }
}

誰もがこの問題を回避する方法についてより良いアイデアを持っていますか?

32
asmo

TypeDescriptionProviderAttributeを使用して、抽象基本クラスに具体的な設計時実装を提供できます。詳細は http://wonkitect.wordpress.com/2008/06/20/using-visual-studio-whidbey-to-design-abstract-forms/ を参照してください。

19
Nicole Calinoiu

欲しいもの

まず、最終クラスと基本抽象クラスを定義しましょう。

public class MyControl : AbstractControl
...
public abstract class AbstractControl : UserControl // Also works for Form
...

これで、必要なのは説明プロバイダーだけです。

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);
    }
}

最後に、TypeDescriptionProvider属性をAbstractコントロールに適用するだけです。

[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<AbstractControl, UserControl>))]
public abstract class AbstractControl : UserControl
...

以上です。ミドルコントロールは必要ありません。

また、プロバイダークラスは、同じソリューションで必要なだけの抽象ベースに適用できます。

46
jucardi

これを解決する別の方法は、前処理ディレクティブを使用することです。

#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

    ...
  }

このトピックの詳細については、このリンクを参照してください。 抽象クラ​​スからフォームを継承する(およびデザイナーで機能させる)

このMSDNフォーラムスレッドでも、同じ解決策が簡潔に説明されていました。 serControl、Inherited Control、Abstract class、(C#)

多分よりクリーンなソリューションではないかもしれませんが、それでも私が見つけた最短のものです。

この質問は何年も前のものですが、私が見つけたものを追加したいと思います。

抽象基本クラスに触れたくない場合は、これを行うことができます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;
    }
}

このように、フォームは非抽象ベースフォームを継承し、デザイナーに表示されます!そして、あなたはあなたの抽象的な形を保ちますが、継承のレベルは1つだけ高くなります。奇妙ですね。

2
Andrew

「ニコール・カリノウ」の解決策を仕事にすることができませんでした。しかし、ビジュアルスタジオに直接、他の簡単な方法があります:)

  1. 新しいプロジェクトを作成する
  2. 新しい要素「userControl」を追加し、たとえば1つのボタンを追加します
  3. 新しい要素 'userControl'Inhereted UserControlを追加してから、継承されたuserControlを選択します。

詳細はこちら: ' http://www.codeproject.com/Articles/20845/How-to-derive-from-a-parent-form

2
Julian50

以下は主に私のために働く一般的な解決策です。これは、別の回答の article に基づいています。時々それはうまくいき、私は自分のUserControlを設計でき、その後ファイルを開いて「デザイナーはタイプ 'MyApp.UserControlBase'のインスタンスを作成する必要がありますが、それはできません。タイプは抽象として宣言されています。」 VSをクリーンアップして閉じ、VSを再度開いて、再構築することで修正できると思います。現在、動作しているようです。幸運を。

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

私はUWPでこれを初めて体験しましたが、それは私を狂わせるものでした。 UserControlの抽象基本クラスについては考えていませんでした。私は別の方向に行きました。非xamlヘルパークラスを作成しました... HBase。各ビュー、たとえばVContractには、HContractという対応するヘルパーがありました。各ビューのすべての専門コードがそこに提出されました。 ViewModel VMContractとView VContractの間の会話はHContractを通過します。 HWhateverIHBaseとどのように動作するかを強制できます。これは、OPの質問に対する真の答えではありませんが、代替アプローチを示しています。すべてのビューは基本的にシェルです。 VContractまたはHContractにx:Bindするかどうかは、あなたが決めることです。私はVContractを選択しましたが、結局それは間違いだったと思います。

デザインモードでの例外を含むUWPの問題は、次のようにして簡単に修正できます。

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

いたずらな派生クラスがBase実装を呼び出そうとした場合に備えて、「抽象」メソッドを仮想として定義し、それらに例外をスローすることで、抽象基本クラスを具体的なものにしています。

例えば.

    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  ;)
        }

これと実際の抽象基本クラスの主な実用的な違いは、基本実装を呼び出すときにランタイムエラーが発生することです。一方、Baseが実際に抽象の場合、コンパイラは、Baseクラス実装への偶発的な呼び出しを許可しません。これは私にとって大したことではなく、他の人から提案されたより複雑で時間のかかる作業を心配することなく、デザイナーを使用することができます...

PS-Akuma-デザイナーで抽象UIクラスを編集できるはずです。現時点でこれを確認する時間はありませんが、設計者はBASEクラスをインスタンス化するだけでよいことは理解しています。設計しているクラスのベースが具体的である限り、設計したクラスが何であるかは問題ではありません。

0
Tim

カスタムコントロールのUWPでこの問題を解決しました。

私のケース

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));

}

宣言

< MyCustomControl:MainModel ShowLabel=True />

ソリューション

ジェネリックリソースのダミースタイルをオーバーライドするだけです。

<Style TargetType="local:MainModel" />

よろしく、

サミュエル

0
Samuel