7

つまり、バジリオンを含むフォームを含むC#WinFormsプロジェクトがありますUserControl。それぞれが、独自の特定のメンバーに加えて、UserControlすべてのメソッド、プロパティなどを自然に公開します。UserControl

UserControlこれらの処理の複雑さを軽減する1つの方法は、インターフェイスを介してアクセスすることだと私は考えてきました。したがって、ドラッグアンドドロップUserControlでフォームに配置する代わりに、コンストラクターで次のようにします。

public class MyGiantForm
{
    ICustomerName cName;

    public MyForm()
    {
        InitializeComponent();

        var uc = new SomeCustomerNameUserControl();
        this.Controls.Add(uc);
        cName = uc;
    }
}

SomeCustomerNameUserControlを実装しICustomerName、当然、ICustomerName私が本当に気にかけている特定のプロパティを含みます(たとえば、FirstNameおよびLastName)。このようにして、私はメンバーUserControlを通して参照することができcName、すべてのメンバーに悩まされる代わりに、のUserControlメンバーだけを取得しICustomerNameます。

すべてうまくいっていますが、問題は、このようにするとSomeCustomerNameUserControl、Designerに表示されないことです。私がこれを行う方法を知っている人はいますが、それでもUserControlフォームのデザイン面に表示されますか?

編集:これを行う1つの方法は、それほど複雑ではありませんが、コントロールをベースフォームに配置することです。デフォルト(C#の場合)では、コントロールメンバーはプライベートです。次に、インターフェイスを介して公開する各コントロールのプロパティを作成します。

ただし、もっと複雑な場合でも、これを行う他の方法に興味があります。IDesignerHostでそれを行う方法はいくつかあるようですが、適切な例が見つかりません。

4

8 に答える 8

6

あなたが望むことを達成する方法があります-あなたが見たくないメンバーを隠す-しかし、カスタムインターフェースを使用して他の人の協力を必要とせずに、それを自動的に適用させます。これを行うには、表示したくないすべてのメンバーを再導入し、それらに属性のタグを付けます。

これは、たとえば、基本クラスのプロパティが特定の子孫にとって何の意味も持たない場合にWindowsフォームが行うことです。たとえば、ControlにはTextプロパティがありますが、たとえばTabControlではTextプロパティは無意味です。そのため、TabControlはTextプロパティをオーバーライドし、そのオーバーライドに「ちなみに、プロパティグリッドまたはIntellisenseにTextプロパティを表示しないでください」という属性を追加します。プロパティはまだ存在しますが、表示されないため、邪魔になりません。

[EditorBrowsable(EditorBrowsableState.Never)]属性をメンバー(プロパティまたはメソッド)に追加すると、Intellisenseはそのメンバーをコード補完リストに表示しなくなります。私があなたの質問を正しく理解しているなら、これはあなたが達成しようとしている大きなことです:アプリケーションコードが誤ってメンバーを使用するのを難しくします。

プロパティの場合は、プロパティグリッドからプロパティを非表示にする[Browsable(false)]と、デザイナーがプロパティの値を.designer.csファイルに書き込まないようにする[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]も追加することをお勧めします。

これらは、メソッド/プロパティを誤って使用することを非常に困難にします。ただし、それでも保証はありません。保証が必要な場合は、[Obsolete]属性もスローし、「警告をエラーとして扱う」を使用してビルドします。これで問題は解決します。

ベースメンバーが仮想の場合は、おそらくそれをオーバーライドし、オーバーライドでベースを呼び出すだけにします。オーバーライドされたメンバーは、通常のイベントの過程で基本クラスによって呼び出される可能性があるため、例外をスローしないでください。一方、ベースメンバーが仮想でない場合は、「オーバーライド」ではなく「新規」を使用する必要があります。実装でベースを呼び出すか、単に例外をスローするかを決定できます。誰も使用しないでください。とにかくあなたの再紹介されたメンバーなので、それは問題ではないはずです。

public class Widget : UserControl
{
    // The Text property is virtual in the base Control class.
    // Override and call base.
    [EditorBrowsable(EditorBrowsableState.Never)]
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Obsolete("The Text property does not apply to the Widget class.")]
    public override string Text
    {
        get { return base.Text; }
        set { base.Text = value; }
    }

    // The CanFocus property is non-virtual in the base Control class.
    // Reintroduce with new, and throw if anyone dares to call it.
    [EditorBrowsable(EditorBrowsableState.Never)]
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Obsolete("The CanFocus property does not apply to the Widget class.")]
    public new bool CanFocus
    {
        get { throw new NotSupportedException(); }
    }

    // The Hide method is non-virtual in the base Control class.
    // Note that Browsable and DesignerSerializationVisibility are
    // not needed for methods, only properties.
    [EditorBrowsable(EditorBrowsableState.Never)]
    [Obsolete("The Hide method does not apply to the Widget class.")]
    public new void Hide()
    {
        throw new NotSupportedException();
    }
}

はい、これはかなりの作業ですが、1回だけ行う必要があります...メンバーごと、クラスごと...うーん、ええ。しかし、それらの基本クラスのメンバーが実際にあなたのクラスに当てはまらず、そこにいると混乱が生じる場合は、努力する価値があるかもしれません。

于 2009-05-05T04:27:21.263 に答える
6

SomeCustomerNameUserControlが次のように定義されている場合:

class SomeCustomerNameUserControl : UserControl, ICustomerName
{
}

このコントロールをデザイナ(someCustomerNameUserControl1を作成します)にドロップして、必要なときにいつでもこれを行うことができます。

ICustomerName cName = someCustomerNameUserControl1;

何かが足りないかもしれませんが、それはとても簡単だと思います。

于 2009-04-24T01:36:52.107 に答える
4

'ICustomerNameをUserControlの変数にアクセスするための唯一のオプションにしたい。アイデアは、開発者がそれをキャストするために「ただ覚えている」必要がないということです。

あなたが抱えている問題は、あなたのフォームとそれがホストするコントロールの2つの完全に異なる用途があるということです。Visual Studioやwinformsに組み込まれている、これをきちんと解決するトリックはありません。可能かもしれませんが、コントロールと対話する2つの方法を分離するためのはるかにクリーンでオブジェクト指向の方法があります。

これらのオブジェクトがUserControlから継承するという事実を隠し、それらをIDoSomeThingYouShouldDealWithとして扱いたい場合は、プレゼンテーションの問題を処理するロジック(デザイナー+ UIロジック)をビジネスロジックから分離する必要があります。

フォームクラスは、ユーザーコントロール、ドッキング、アンカーなどのコントロールを正しく処理する必要があります。ここでは特別なことは何もありません。ICustomerName.FirstName=などを処理するために必要なすべてのロジックを完全に別のクラスに配置する必要があります。このクラスは、フォントやレイアウトを気にせず、知りません。顧客名を提示できる別のインスタンスがあることを知っているだけです。または、「生年月日を選択する」コントロールとしてのDateTimeなど。

これは本当に下手な例ですが、私は今行かなければなりません。あなたはここでより詳細にカバーされたアイデアを得ることができるはずです:

public interface ICustomerName
    {
        void ShowName(string theName);
    }

    public partial class Form1 : Form, ICustomerName
    {
        public Form1()
        {
            InitializeComponent();
        }

        #region ICustomerName Members

        public void ShowName(string theName)
        {
            //Gets all controls that show customer names and sets the Text propert  
            //totheName
        }

        #endregion
    }

    //developers program logic into this class 
    public class Form1Controller
    {
        public Form1Controller(ICustomerName theForm) //only sees ICustomerName methods
        {
            //Look, i can't even see the Form object from here
            theForm.ShowName("Amazing Name");
        }
    }
于 2009-05-08T20:49:09.777 に答える
3

デザイナを使用してUserControlを追加した後、[プロパティ]ウィンドウでGenerateMemberをfalseに設定して、メンバーの生成を抑制することができます。

次に、コンストラクターで他の手法を使用して、cName参照を割り当てることができます。例:

foreach(Control control in this.Controls)
{
    cName = control as ICustomerName;
    if (cName != null) break;
}

その場合、cNameがUserControlへの唯一の参照になります。

于 2009-05-03T19:36:41.610 に答える
1

インターフェイスを実装するフォームのコントロールを返すことができる拡張メソッドを作成できます。

public static class FormExtensions
{
    public static IDictionary<string, T> GetControlsOf<T>(this Form form) 
           where T: class
    {
        var result = new Dictionary<string, T>();
        foreach (var control in form.Controls)
        {
            if ((control as T) != null)
                result.Add((control as T).Tag, control as T);
        }
        return result;
    }
}

次に、フォームで、次の方法で好きな場所に呼び出すことができます。

this.GetControlsOf<ICustomerName>()["NameOfControlHere"];

複数のユーザーコントロールを返す場合は、何らかの方法でそれを処理する必要があります。たとえば、インターフェイスにTagプロパティを追加して、各ユーザーコントロールなどを一意に追跡します。

public partial class UserControl1 : UserControl, ICustomerName
{
     public string Tag { get { return this.Name; } }
}

次に、デザイナからフォームにユーザーコントロールをドラッグアンドドロップできます。タグは常にコントロールの名前を返します。これにより、IDictionaryのインターフェイスを介してコントロールに直接アクセスできます。開発者は、コントロールの名前に必要な一意の識別子を入れることができ、それがインターフェイスに引き継がれます。

また、このアプローチでは、ソリューションのすべてのフォームでこれを使用できることにも注意してください。

あなたがする必要がある他の唯一のことはあなたのGenerateMemberをfalseに設定することです。

于 2009-05-08T19:07:15.543 に答える
0

ボブが言ったように行うこともできますが、コンストラクターですべてのメンバー変数を割り当てると、1つの場所に配置されます。

于 2009-04-24T06:49:43.667 に答える
0

メディエーターパターンを実装したいようです。バジリオンの各UserControlを直接処理する代わりに、メディエーターを介してそれらと対話します。各メディエーターは、各コントロールから見たいスリムなインターフェイスを定義します。これにより、デザインがより明確で簡潔になるため、全体的な複雑さが軽減されます。たとえば、コントロールの1つで使用可能な20のプロパティと50のメソッドは必要ありません。代わりに、本当に気になる2つのプロパティと5つのメソッドを定義するそのコントロールのメディエーターを扱います。すべてが引き続きデザイナーに表示されますが、アプリの他の部分はこれらのコントロールと相互作用しません。つまり、メディエーターと相互作用します。

このアプローチの大きな利点の1つは、メンテナンスが大幅に簡素化されることです。実装が悪いためにMyCrappyUserControlを書き直す必要があると判断した場合は、そのコントロールのメディエータークラスを更新するだけで済みます。コントロールと相互作用する他のすべてのクラスは、メディエーターを介して相互作用し、変更されません。

最終的には規律に帰着します。あなたとあなたのチームは、コントロールを直接打つ代わりに、メディエーター/インターフェース/その他を使用するのに十分な規律を持っている必要があります。チームが規律のスケールの下限にある場合は、リーダープログラマーによる肩越しのコードレビューを実施します。

于 2009-05-08T09:03:13.680 に答える
-3

MyUserControlが次のように定義されていると仮定します。

class MyUserControl : UserControl, IMyInterface
{
    // ...
}

次に、フォームに次のようなものが必要です。

public class MyForm : Form
{
    IMyInterface cName;

    public MyForm()
    {
        InitializeComponent();

        cName = new MyUserControl();
        Controls.Add((UserControl)cName);
    }
}

このように、cNameはユーザーコントロールのこのインスタンスにアクセスする唯一の方法です。

于 2009-05-10T16:22:23.230 に答える