views:

362

answers:

2

I've been exploring the MVP pattern with ASP.NET but I have had trouble keeping all of the presentation logic in the presenter when using a databound control on the page.

This following scenario and classes are just for example and the real case I'm working with is more complex. Any ideas or direction would be greatly appreciated.

Say that I have a page which displays info about a Customer including Name and Address. It also renders a list of Orders using a Repeater control.

public class CustomerDto {
    public string Name { get; set; }
    public string Address { get; set; }
    public List<OrderDto> OrderList { get; set; }
}

public class OrderDto {
    public int Id { get; set; }
    public decimal Amount { get; set; }
    public bool IsRush { get; set; }
}

At first I had the presenter set the Name, Address and OrdrerList on the view. At this point there was still some presentation logic that was occurring in the ItemDataBound event of the Repeater depending on the value of IsRush on the order. In my opinion this logic doesn't belong in the code-behind but in a testable presenter class.

public interface IOrderView {
    void SetName(string name);
    void SetAddress(string address);
    void SetOrderList(List<OrderDto> orderList);
}

public partial class OrderPage : Page, IOrderView
{
    public void SetName(string name) {
        labelName.Text = name;
    }

    public void SetAddress(string address) {
        labelAddress.Text = address;
    }

    public void SetOrderList(List<OrderDto> orderList) {
        repeaterOrders.DataSource = orderList;
        repeaterOrders.DataBind();
    }

    protected void repeaterOrders_ItemDataBound(object sender, RepeaterItemEventArgs e)
    {
        if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) {
            OrderDto orderDto = e.Item.DataItem as OrderDto;
            if (orderDto.IsRush) {
                Label labelOrderId = (Label)e.Item.FindControl("labelOrderId");
                labelOrderId.ForeColor = System.Drawing.Color.Red;
            }
        }
    }
}

It almost seems like each item in the repeater needs its own Presenter and View but I haven't found any similar example anywhere. I have come up with a couple of ways to keep all the presentation logic in the presenter but they all feel like a hack and I was wondering hope people generally handle this situation.

Thanks!

+2  A: 

The answer is definitely on the blurry boundary. Changing the colour of the label for rush orders is definitely in the realm of the view, but the logic for determining rush orders versus normal orders is not IMO.

In your case I'm imagining something like a visitor. Foreach order in the repeater you pass that order onto the presenter or controller, and that calls the appropriate method in the view based upon domain knowledge that the view doesn't need.

OrderDto orderDto = e.Item.DataItem as OrderDto;
controller.Visit(this, orderDto);

// somewhere in your controller/presenter you've got

void Visit(ISomeView view, OrderDto dto) {
   if (dto.IsRush) {
        view.RenderRushOrder(dto);
   } else {
        view.RenderNornamlOrder(dto);
   }
}

The view can then render as you've already detailed. IMO it separates the responsibilities nicely and something I often use in practice in non MVC ASP.Net

Hope this helps.

Phil Bennett
I don't think Visit needs to accept the view, as presenter in webforms usually has a reference to the view anyway.
epitka
A: 

The decision to apply the ForeColor or not is logic and has to be in the presenter to be testable.

You could just add a CssClass or ForeColor attribute to your object DTO. You definitely want to be able to test if the attribute was set or not according to your logic currently in OnItemDataBound.

Maxime