2

PageItemsがモデル オブジェクトである WPF アプリケーションがあります。

私のメイン ViewModel にはPageItemViewModelsの ObservableCollection があり、それぞれが一致する PageItem モデル オブジェクトから構築されます。

PageItemViewModelは、抽象クラスBaseViewModelから継承して、INotifyPropertyChanged 機能を取得します。

PageItemViewModelは、必要なプロパティがあることを確認するためにIPageItemViewModelも実装します。

最終的には約 50 ページになるので、不要なコードを削除したいと思います。

  • 解決済み(以下を参照) : PageItemViewModel クラスにIdCode と Title を継承させて、各クラスに実装する必要がないようにする方法はありますか? これらのプロパティを必要としない他のViewModelがそれを継承するため、BaseViewModelに配置することはできません。また、IPageItemViewModelに配置することはできません。これは単なるインターフェイスであるためです。これには C# がサポートしていない複数の継承が必要であることを理解しています
  • 解決済み (以下を参照) : switchステートメントを取り除く方法はありますか。たとえば、代わりにリフレクションを使用するなどです。

以下は、 WPFアプリケーションにあるコードを示すスタンドアロンのコンソール アプリケーションです。

using System.Collections.Generic;

namespace TestInstantiate838
{
    public class Program
    {
        static void Main(string[] args)
        {
            List<PageItem> pageItems = PageItems.GetAll();
            List<ViewModelBase> pageItemViewModels = new List<ViewModelBase>();

            foreach (PageItem pageItem in pageItems)
            {
                switch (pageItem.IdCode)
                {
                    case "manageCustomers":
                        pageItemViewModels.Add(new PageItemManageCustomersViewModel(pageItem));
                        break;
                    case "manageEmployees":
                        pageItemViewModels.Add(new PageItemManageEmployeesViewModel(pageItem));
                        break;
                    default:
                        break;
                }
            }
        }
    }

    public class PageItemManageCustomersViewModel : ViewModelBase, IPageItemViewModel
    {
        public string IdCode { get; set; }
        public string Title { get; set; }

        public PageItemManageCustomersViewModel(PageItem pageItem)
        {

        }
    }

    public class PageItemManageEmployeesViewModel : ViewModelBase, IPageItemViewModel
    {
        public string IdCode { get; set; }
        public string Title { get; set; }

        public PageItemManageEmployeesViewModel(PageItem pageItem)
        {

        }
    }

    public interface IPageItemViewModel
    {
        //these are the properties which every PageItemViewModel needs
        string IdCode { get; set; }
        string Title { get; set; }
    }

    public abstract class ViewModelBase
    {
        protected void OnPropertyChanged(string propertyName)
        {
            //this is the INotifyPropertyChanged method which all ViewModels need
        }
    }

    public class PageItem
    {
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    public class PageItems
    {
        public static List<PageItem> GetAll()
        {
            List<PageItem> pageItems = new List<PageItem>();
            pageItems.Add(new PageItem { IdCode = "manageCustomers", Title = "ManageCustomers"});
            pageItems.Add(new PageItem { IdCode = "manageEmployees", Title = "ManageEmployees"});
            return pageItems;
        }
    }

}

リファクタリング: インターフェイスを抽象クラスに変更

using System;
using System.Collections.Generic;

namespace TestInstantiate838
{
    public class Program
    {
        static void Main(string[] args)
        {
            List<PageItem> pageItems = PageItems.GetAll();
            List<ViewModelPageItemBase> pageItemViewModels = new List<ViewModelPageItemBase>();

            foreach (PageItem pageItem in pageItems)
            {
                switch (pageItem.IdCode)
                {
                    case "manageCustomers":
                        pageItemViewModels.Add(new PageItemManageCustomersViewModel(pageItem));
                        break;
                    case "manageEmployees":
                        pageItemViewModels.Add(new PageItemManageEmployeesViewModel(pageItem));
                        break;
                    default:
                        break;
                }
            }

            foreach (ViewModelPageItemBase pageItemViewModel in pageItemViewModels)
            {
                System.Console.WriteLine("{0}:{1}", pageItemViewModel.IdCode, pageItemViewModel.Title);
            }
            Console.ReadLine();
        }
    }

    public class PageItemManageCustomersViewModel : ViewModelPageItemBase
    {
        public PageItemManageCustomersViewModel(PageItem pageItem)
        {
            IdCode = pageItem.IdCode;
            Title = pageItem.Title;
        }
    }

    public class PageItemManageEmployeesViewModel : ViewModelPageItemBase
    {
        public PageItemManageEmployeesViewModel(PageItem pageItem)
        {
            IdCode = pageItem.IdCode;
            Title = pageItem.Title;
        }
    }

    public abstract class ViewModelPageItemBase : ViewModelBase
    {
        //these are the properties which every PageItemViewModel needs
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    public abstract class ViewModelBase
    {
        protected void OnPropertyChanged(string propertyName)
        {
            //this is the INotifyPropertyChanged method which all ViewModels need
        }
    }

    public class PageItem
    {
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    public class PageItems
    {
        public static List<PageItem> GetAll()
        {
            List<PageItem> pageItems = new List<PageItem>();
            pageItems.Add(new PageItem { IdCode = "manageCustomers", Title = "ManageCustomers"});
            pageItems.Add(new PageItem { IdCode = "manageEmployees", Title = "ManageEmployees"});
            return pageItems;
        }
    }

}

Switch ステートメントを削除するための回答:

ありがとうジャブ:

string assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
string viewModelName = assemblyName + ".ViewModels.PageItem" + StringHelpers.ForcePascalNotation(pageItem.IdCode) + "ViewModel";
var type = Type.GetType(viewModelName);
var viewModel = Activator.CreateInstance(type, pageItem) as ViewModelBase;
AllPageViewModels.Add(viewModel);
4

5 に答える 5

2

これら 2 つのプロパティを実装する BaseViewModel から継承するクラスを作成できますか?これを必要とする PageItemViewModel クラスはそれから継承できます。

于 2009-06-22T14:16:04.410 に答える
1

Paddy が提案したように、追加の抽象クラス PageViewModelBase を作成し、これらの auto-props を定義しました。

using System.Collections.Generic;

namespace TestInstantiate838
{
    public class Program
    {
        static void Main(string[] args)
        {
            List<PageItem> pageItems = PageItems.GetAll();
            List<ViewModelBase> pageItemViewModels = new List<ViewModelBase>();

            foreach (PageItem pageItem in pageItems)
            {
                switch (pageItem.IdCode)
                {
                    case "manageCustomers":
                        pageItemViewModels.Add(new PageItemManageCustomersViewModel(pageItem));
                        break;
                    case "manageEmployees":
                        pageItemViewModels.Add(new PageItemManageEmployeesViewModel(pageItem));
                        break;
                    default:
                        break;
                }
            }
        }
    }

    public class PageItemManageCustomersViewModel : PageViewModelBase
    {
        public PageItemManageCustomersViewModel(PageItem pageItem)
        {

        }
    }

    public class PageItemManageEmployeesViewModel : PageViewModelBase
    {
        public PageItemManageEmployeesViewModel(PageItem pageItem)
        {


        }
    }

    public abstract class ViewModelBase
    {
        protected void OnPropertyChanged(string propertyName)
        {
            //this is the INotifyPropertyChanged method which all ViewModels need
        }
    }

    public abstract class PageViewModelBase : ViewModelBase
    {
        //these are the properties which every PageItemViewModel needs
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    public class PageItem
    {
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    public class PageItems
    {
        public static List<PageItem> GetAll()
        {
            List<PageItem> pageItems = new List<PageItem>();
            pageItems.Add(new PageItem { IdCode = "manageCustomers", Title = "ManageCustomers"});
            pageItems.Add(new PageItem { IdCode = "manageEmployees", Title = "ManageEmployees"});
            return pageItems;
        }
    }

}
于 2009-06-22T14:22:07.860 に答える
1

あまりきれいではありませんが機能する解決策の 1 つは、規則を使用して switch ステートメントを取り除くことです。これは、IdCodes を変更するか、少なくともケースを ViewModel に一致するように変更できることを前提としています。

    var type = Type.GetType("PageItem" + pageItem.IdCode + "ViewModel");
    var viewModel = Activator.CreateInstance(type) as ViewModelBase;
    pageItemViewModels.Add(viewModel);

ここにエラーチェックを追加する必要があることに注意してください。ここにはいくつかの失敗点があります。ただし、増え続ける switch ステートメントを維持するよりはましです。

于 2009-06-22T15:14:15.213 に答える
1

PageItem考えられる解決策の 1 つは、コード内のとの関係を逆にすることPageItemViewModelです。現在、 aPageItemViewModelに基づいて a を生成していますPageItemが、最初に を作成し、PageItemViewModel次に各PageItemViewModelのコンストラクターで適切な を作成した場合はどうなるPageItemでしょうか? switchこれにより、モデルがビューモデルを担当するのではなく、ビューモデルがモデルを担当するようになったため、 の必要性がなくなり、物事がすっきりします。

現在のコードに基づく例:

using System;
using System.Collections.Generic;

namespace TestInstantiate838
{
    public class Program
    {
        static void Main(string[] args)
        {
            List<ViewModelPageItemBase> pageItemViewModels = PageItemViewModels.GetAll();

            // No switch needed anymore. Each PageItem's view-model contains its PageItem
            // which is exposed as property of the view-model.
            foreach (ViewModelPageItemBase pageItemViewModel in pageItemViewModels)
            {
                System.Console.WriteLine("{0}:{1}", pageItemViewModel.PageItem.IdCode, pageItemViewModel.PageItem.Title);
            }
            Console.ReadLine();
        }
    }

    public class PageItemManageCustomersViewModel : ViewModelPageItemBase
    {
        public PageItemManageCustomersViewModel()
        {
            PageItem = new PageItem { IdCode = "manageCustomers", Title = "ManageCustomers" };
        }
    }

    public class PageItemManageEmployeesViewModel : ViewModelPageItemBase
    {
        public PageItemManageEmployeesViewModel()
        {
            PageItem = new PageItem { IdCode = "manageEmployees", Title = "ManageEmployees" };
        }
    }

    public abstract class ViewModelPageItemBase : ViewModelBase
    {
        //The PageItem associated with this view-model
        public PageItem PageItem { get; protected set; }
    }

    public abstract class ViewModelBase
    {
        protected void OnPropertyChanged(string propertyName)
        {
            //this is the INotifyPropertyChanged method which all ViewModels need
        }
    }

    public class PageItem
    {
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    // Replaces PageItems class
    public class PageItemViewModels
    {
        // Return a list of PageItemViewModel's instead of PageItem's.
        // Each PageItemViewModel knows how to build it's corresponding PageItem object.
        public static List<PageItemViewModelBase> GetAll()
        {
            List<PageItemViewModelBase> pageItemViewModels = new List<PageItemViewModelBase>();
            pageItemViewModels.Add(new PageItemManageCustomersViewModel());
            pageItemViewModels.Add(new PageItemManageEmployeesViewModel());
            return pageItemViewModels;
        }
    }
} 
于 2009-06-22T15:40:34.707 に答える
1

GetViewModel()適切なビュー モデルを返す基本 PageItem クラスに仮想メソッドを配置できないのはなぜですか?

   foreach (PageItem pageItem in pageItems)
   {
       pageItemViewModels.Add(pageItem.GetViewModel());
   }

コードの臭いのようにすぐに見えるのは、「id」プロパティの使用です。これは通常、ポリモーフィズムに置き換えることができます。したがって、switchステートメントを上記のコードに置き換えます。

編集:

PageItem クラスがビュー モデルについて何も知らない場合、この方法で実装することはできません。基本的に、すでに(ある意味で)持っているファクトリーが必要です。

私は通常、リレーションシップ (PageItem から ViewModel へ) のリストを持っていますDictionary<String, Type>。その後、初期化中にこのリストに入力し、後で適切なビュー モデルをインスタンス化できます。

リフレクションを使用してこのリストを作成するには、ビュー モデルでサポートされているページ アイテムをプログラムで知る必要があります。その目的のために、カスタム属性を使用してクラスを装飾できます。

public class SupportsPageItemAttribute : Attribute
{
    private readonly string _id;
    public string ID
    {
        get { return _id;}
    }

    public SupportsPageItemAttribute(string id)
    {
        _id = id;
    }
}

次に、その属性を使用して、モデルが受け入れることができる PageItem を定義します。

[SupportsPageItemAttribute("manageCustomers")
public class PageItemManageCustomersViewModel
{
   // ...
}

次に、リフレクションを使用して IPageItemViewModel を実装するすべてのクラスを取得し、それらの属性をチェックして PageItem id 文字列を取得します。

例 (多くのエラーチェックなし):

Dictionary<String, Type> modelsById = new Dictionary<String, Type>();
String viewModelInterface = typeof(IPageItemViewModel).FullName;

// get the assembly
Assembly assembly = Assembly.GetAssembly(typeof(IPageItemViewModel));

// iterate through all types
foreach (Type viewModel in assembly.GetTypes())
{
    // get classes which implement IPageItemViewModel
    if (viewModel.GetInterface(viewModelInterface) != null)
    {
        // get the attribute we're interested in
        foreach (Attribute att in Attribute.GetCustomAttributes(viewModel))
        {
            if (att is SupportsPageItemAttribute)
            {
                // get the page item id
                String id = (att as SupportsPageItemAttribute).ID;

                // add to dictionary
                modelsById.Add(id, viewModel);
            }
        }
    }
}

一方、厄介なリフレクション作業を自分で行う代わりに検討できるさまざまな制御の反転フレームワークがあります。

于 2009-06-22T14:29:45.317 に答える