使用 WPF 的图表应用程序的 MVVM 模式 - 将枚举转换为 xxxViewModel

发布于 2024-09-13 11:53:41 字数 285 浏览 1 评论 0原文

我正在尝试将 MVVM 设计模式应用于图表应用程序。在此应用程序中有不同的项目(例如矩形、圆形……)。我想将项目类型保存为模型中的枚举。

在我的模型视图中,我为每个项目类型创建了一个类(矩形视图模式、圆形视图模式……)。

在我看来,我将数据模板应用于该类型,因此它呈现为圆形或矩形。

问题是...如何将模型中的枚举转换为所需的 xxxViewMode?我有很多类型,我想要自动转换。

我是 MVVM 的新手,也许有更好的方法......所以欢迎更好的解决方案! :)

非常感谢

I'm trying to apply the MVVM design pattern to a diagramming application. In this application there are different items (for example a rectangle, a circle,...). I would like to save the item type as an enum in my model.

In my modelview I made a class for every item type (rectangleViewMode, circleViewMode,...).

On my view I apply a data template to the type, so it renders like a circle, or like a rectangle.

The problem is...how can I convert the enum in my model to the requiered xxxViewMode? I have a lot of types and I would like an automatic conversion.

I'm new to MVVM and maybe there is a better approach...so better solutions are welcome! :)

Thank you very much

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

风透绣罗衣 2024-09-20 11:53:41

我对你的问题的理解与其他答案略有不同,我不相信你只是在寻找一种将枚举绑定到组合的方法,我认为你正在寻找一种将枚举值与对象类型相关联的方法。如果我弄错了,请立即停止阅读:)

首先: 我不确定将形状类型保存为枚举(或者甚至将形状与枚举相关)是否具有很好的可扩展性。请继续阅读,我将在最后解释。

要将项目类型与枚举相关联,只需让该项目通过属性返回适当的枚举值:

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 并使用转换器从形状类型生成一个易于理解的名称(使用字符串资源文件,完全消除枚举)。

I read your question a little differently to the other answerers, i don't believe you are just looking for a way to bind an enum to a combo, i think you are looking for a way to relate an enum value to an object type. If i got this wrong then stop reading now :)

First up: I'm not sure that saving the shape types as an enumeration (or even relating the shape to an enumeration) is very scalable. Read on, and i'll explain towards the end.

To relate an item type to an enum, just have the item return the appropriate enum value via a property:

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

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

This means that you don't have to employ a converter or another translator mechanism. If you want to then populate a bunch of these into a combo then it is quite simple - check this following sample and insert breakpoints at the appropriate spot to see how it works.

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

With this approach you will have a combobox with nice human readable shape names in it, and you can instantly get the actual shape type of the selected item. It would be a trivial task to turn the class Example into an abstract base ViewModel, any ViewModel you then derive from it will have the AvailableShapes property.

But back to my original point of scalability - as you increase the shape types you also need to update the enumeration. This could be problematic if you ship libraries of new shapes or allow users to create their own. You may be better off saving it as myShape.GetType().ToString(), which returns a string value that can then be used to recreate the an instance of the object using reflection. In the above example of showing the items in a combo, you could just get a List<Type> of the available shapes and use a converter to produce a nice human readable name from the shape type (using a string resource file, eliminating the enumeration altogether).

转瞬即逝 2024-09-20 11:53:41

根据您的需要,您可以使用转换器类:(

How将 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
}

Depending on your needs, you could use a converter class:

(stolen from How to bind RadioButtons to an enum?)

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
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文