Шаблон MVVM для приложения для построения диаграмм с WPF — преобразование enum в xxxViewModel

Я пытаюсь применить шаблон проектирования MVVM к приложению для построения диаграмм. В этом приложении есть разные элементы (например, прямоугольник, круг,...). Я хотел бы сохранить тип элемента как перечисление в моей модели.

В моем представлении модели я создал класс для каждого типа элемента (rectangleViewMode, circleViewMode,...).

На мой взгляд, я применяю шаблон данных к типу, поэтому он отображается как круг или как прямоугольник.

Проблема в том... как я могу преобразовать перечисление в моей модели в требуемый xxxViewMode? У меня много типов, и я хотел бы автоматическое преобразование.

Я новичок в MVVM, и, возможно, есть лучший подход... так что лучшие решения приветствуются! :)

Большое спасибо


person tundris    schedule 07.08.2010    source источник
comment
Это аналогичный вопрос здесь: stackoverflow.com/questions/397556/. Класс преобразователя может помочь вам.   -  person Merlyn Morgan-Graham    schedule 08.08.2010


Ответы (2)


Я прочитал ваш вопрос немного иначе, чем другие ответчики, я не верю, что вы просто ищете способ привязать перечисление к комбо, я думаю, вы ищете способ связать значение перечисления с типом объекта. Если я ошибся, то прекратите читать сейчас :)

Во-первых: я не уверен, что сохранение типов фигур в виде перечисления (или даже связывание фигуры с перечислением) является масштабируемым. Читайте дальше, и я объясню ближе к концу.

Чтобы связать тип элемента с перечислением, просто сделайте так, чтобы элемент возвращал соответствующее значение перечисления через свойство:

public CircleViewMode
{
    public ShapeType Shape { get { return ShapeType.Circle; }}
}

public enum ShapeType 
{
    Circle,
    Square,
    Rectangle,
    Triangle,
    FancyShape1,
    FancyShape2
}

Это означает, что вам не нужно использовать конвертер или другой механизм транслятора. Если вы хотите затем объединить их в комбинацию, то это довольно просто — проверьте следующий пример и вставьте точки останова в соответствующее место, чтобы увидеть, как это работает.

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            new Example().Run();

            Console.ReadKey();
        }
    }

    public class Example : INotifyPropertyChanged
    {
        public void Run()
        {
            var availableShapes = AllMyShapes.Where(x => x.NumberOfSides == 4);
            AvailableShapes = new List<KeyValuePair<string, Type>>
                (from Shape s in availableShapes
                 select new KeyValuePair<string, Type>(
                                                        Enum.GetName(typeof(ShapeType), s.ShapeType)
                                                       ,s.GetType()
                                                       ));

            //at this point any combobox you have bound to the AvailableShapes property will now carry a new list of shapes
        }

        public List<Shape> AllMyShapes
        {
            get
            {
                return new List<Shape>() {   new Circle(){NumberOfSides=1, ShapeType=ShapeType.Circle}
                                            ,new Square(){NumberOfSides=4, ShapeType=ShapeType.Square}
                                            ,new Rectangle(){NumberOfSides=4, ShapeType=ShapeType.Rectangle}
                                            ,new Triangle(){NumberOfSides=3, ShapeType=ShapeType.Triangle}
                                            ,new FancyShape1(){NumberOfSides=10, ShapeType=ShapeType.FancyShape1}
                                            ,new FancyShape2(){NumberOfSides=30, ShapeType=ShapeType.FancyShape2}
                                        };
            }
        }

        public List<KeyValuePair<string, Type>> AvailableShapes
        {
            get { return _availableShapes; }
            protected set 
            {
                _availableShapes = value;
            }
        }

        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        private List<KeyValuePair<string, Type>> _availableShapes;

        public event PropertyChangedEventHandler PropertyChanged;
    }


    public abstract class Shape
    {
        public int NumberOfSides { get; set; }
        public ShapeType ShapeType { get; set; }
    }

    public class Square : Shape { }
    public class Rectangle : Shape { }
    public class Triangle : Shape { }
    public class Circle : Shape { }
    public class FancyShape1 : Shape { }
    public class FancyShape2 : Shape { }

    public enum ShapeType
    {
        Circle,
        Square,
        Rectangle,
        Triangle,
        FancyShape1,
        FancyShape2
    }
}

При таком подходе у вас будет выпадающий список с красивыми именами форм, понятными человеку, и вы сможете мгновенно получить фактический тип формы выбранного элемента. Было бы тривиальной задачей превратить класс Example в абстрактную базовую ViewModel, любая ViewModel, которую вы затем получите от нее, будет иметь свойство AvailableShapes.

Но вернемся к моей исходной точке масштабирования: по мере увеличения количества типов фигур также необходимо обновлять перечисление. Это может быть проблематично, если вы отправляете библиотеки новых фигур или разрешаете пользователям создавать свои собственные. Возможно, вам лучше сохранить его как myShape.GetType().ToString(), который возвращает строковое значение, которое затем можно использовать для воссоздания экземпляра объекта с использованием отражения. В приведенном выше примере отображения элементов в комбинации вы можете просто получить List<Type> доступных фигур и использовать преобразователь для создания удобного для восприятия человеком имени из типа фигуры (используя файл строковых ресурсов, полностью исключая перечисление).

person slugster    schedule 08.08.2010
comment
@tundris - не беспокойтесь, это был дождливый воскресный день, и мне больше нечего было делать. Надеюсь, поможет :) - person slugster; 09.08.2010

В зависимости от ваших потребностей вы можете использовать класс преобразователя:

(украдено из Как привязать RadioButtons к перечислению?)

public class EnumBooleanConverter : IValueConverter
{
  #region IValueConverter Members
  public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    string parameterString = parameter as string;
    if (parameterString == null)
      return DependencyProperty.UnsetValue;

    if (Enum.IsDefined(value.GetType(), value) == false)
      return DependencyProperty.UnsetValue;

    object parameterValue = Enum.Parse(value.GetType(), parameterString);

    return parameterValue.Equals(value);
  }

  public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    string parameterString = parameter as string;
    if (parameterString == null)
        return DependencyProperty.UnsetValue;

    return Enum.Parse(targetType, parameterString);
  }
  #endregion
}
person Merlyn Morgan-Graham    schedule 07.08.2010