2

我需要根据属性的特定值显示不同的图像。
这是我完成这项任务的方式。

我有模型:

public class DetailInfoViewModel
{
    public string Status {get; set;}
    public string StatusImageName {get; set;}
    ... other fields
}

在行动中:

var model = ...// create view model and fill properties    
model.StatusImageName = GetImageFileNameForStatus(model.Status.ToLower());

并且,主要方法:

private string GetImageFileNameForStatus(string status)
{
    if (status == "payed" || status == "approved")
        return "payed.png";

    if (status == "printed")
        return "not-pay.png";

    if (status == "closed")
        return "closed.png";

    if (status == "transfer")
        return "transfer.png";

    return string.Empty;
}

在视图中,我有简单的 html:

<div class="pull-right">
    <img src="@Url.Content("~/Images/" + Model.StatusImageName)"/>
</div>

但是这种方式是丑陋的。如果我重命名图像或打印错误,则此代码将无法正常工作。
有更好的方法来做到这一点吗?

4

2 回答 2

1

为了使其不易出错,请考虑将状态设为枚举

enum Status { Payed, Printed, Closed, Approved, Transfer}

并使用T4MVC生成资源的强链接:

private string GetImageFileNameForStatus(Status status)
{
    switch(status){
      case Payed:
      case Approved:
        // This is generated by T4MVC for you
        return Links.Content.Images.payed_png;
      case Printed:
        return Links.Content.Images.not_pay_png;          
      // ... etc.
      default:
        return Links.Content.Images.default_png;
    }
}

您可以将信息保存在 a 中,而不是 switch 语句,Dictionary<Status, string>但这是一个实现细节。

然后,您可以类似地使用:

<img src="@Model.StatusImageName" alt="@Model.Status"/>

注意:记得添加 alt 属性!

如果您重命名图像或移动它们,编译将中断。

对于大多数较小的系统来说,这个解决方案应该是可以接受的。如果您对更复杂的“纯 OOP”解决方案(根据用例可能会受到过度设计的影响)感兴趣,请继续阅读。

从纯粹的 OOP 角度来看,这个开关——虽然相对简单——并不是最漂亮的解决方案。您可能会考虑状态模式.NET 实现)。

interface IStatus{
    string InfoImage{get;}
}

class PayedStatus : IStatus {    
    public string InfoImage{get{return Links.Content.Images.payed_png;}}
}

class PrintedStatus : IStatus {    
    public string InfoImage{get{return Links.Content.Images.not_pay_png;}}
}

// etc.

你的模型:

public class DetailInfoViewModel
{
  ... 
  public IStatus Status{get;set;}
}

在您看来:

<img src="@Model.Status.InfoImage" alt="Status"/>

如果有特定于给定状态的东西,这种方法会更好。您可以将与特定状态相关的所有内容整齐地封装在相应的 IStatus 实现中。

同样,从面向对象设计的角度来看,UI 细节(要显示的图像)成为模型的一部分感觉很奇怪。另一方面,它是一个视图模型,为什么不呢。如果您想采用顽固的 OOP 并且系统复杂性和要求保证它,您可以考虑访问者模式.NET 实现):

interface IStatusVisitor<T>{
   T VisitPayedStatus<T>(PayedStatus status);
   T PrintedStatus<T>(PrintedStatus status);
   // ...
}

interface IStatus{
    T Accept(IStatusVisitor<T> visitor);
}

class PayedStatus : IStatus {    
    public T Accept(IStatusVisitor<T> visitor){
        return visitor.VisitPayedStatus(this);
    }
}

class PrintedStatus : IStatus {    
    public T Accept(IStatusVisitor<T> visitor){
        return visitor.VisitPrintedStatus(this);
    }
}


class InfoImageVisitor : IStatusVisitor<string>{

  public string VisitPayedStatus(PayedStatus status){
    return Links.Content.Images.payed_png;
  }

  public string VisitPrintedStatus(PrintedStatus status){
    return Links.Content.Images.not_pay_png;
  }

}


<img src="@Model.Accept(new InfoImageVisitor())" alt="Status"/>

通过这种方式,您可以将有关信息图像的所有内容从状态类移到单独的类中。

但正如我所说,这已经可以被认为是过度设计的案例。这取决于您的系统的要求。

作为一般说明:阅读设计模式绝对值得。

于 2013-02-12T10:03:05.280 回答
0

我不知道这是最好的方法,但是您可以将 imageName 定义为状态名称,我的意思是

status | statusImage |
----------------------
 payed |   payed.png |
closed |  closed.png |
   ... |         ... |

然后

<div class="pull-right">
    <img src="@Url.Content("~/Images/" + Model.Status) + ".png""/>
</div>
于 2013-02-12T09:36:53.090 回答