views:

162

answers:

4

As my first WPF project, I am attempting to build an application to play a card game similar to Magic the Gathering. It is not clear to me how to lay out the main play area. You can see some examples that are similar to what I am attempting by looking at example 1 or example 2. The chat/info areas on the right would be separate user controls.

The cards must maintain their aspect ratios, and each play area would start with 10 columns and two rows of cards. As more cards are played, the number of columns and/or rows may change. Each player area may have a different number of columns and/or rows. Cards may overlap, and may be placed sideways (tapped). Cards in all areas should be the same size (although they may be cropped in some areas). Cards do not need to lie exactly on the grid (they do not necessarily snap-to-grid).

When the user hovers the mouse over a card, it should expand to a significantly larger size using an animation. A card in one player area may overflow into the other player's area when expanded (but only as long as the mouse hovers).

Given these requirements, I am tempted to use one large user control derived from Canvas with image objects for each card (along with other shapes to delineate the areas). This implies that I will be doing a lot of work during the OnRenderSizeChanged event to position the child items within the canvas (manual layout).

Using a grid does not seem feasible to me, due to the free-form placement and overlap.

Decomposing the play area into smaller user controls would leverage the WPF layout capabilities, but it seems like decomposition would prevent the cards from expanding into adjacent user controls during the mouse-over, so that doesn't seem feasible either.

Is there a better alternative to one large canvas-based control? It seems wrong to be doing manual layout in WPF, but I cannot see an alternative.

A: 

I recommend you take a look at this guys project . In java I know but if I was to go the route of building a card game. That would be what I would go off of.

Terrance
Thanks for the link, but forge is written in java and is more focused on getting an AI to play the game (a much harder problem). My goal is to allow two people to play over an internet connection.There is a similar WPF-based project (http://moxdev.wordpress.com/) which is where the card expansion idea came from. He is also using a canvas (I believe), but it still seems a bit wrong to abuse a canvas that way.
Doug
A: 

A lot of canvases inside of a grid could help you here, the canvas will allow the content to render outside of its bounds, as long as you turn ClipToBounds to false, and you will be given much more control over exact placement of the cards than with other schemes. You will also get the powerful functionality of a grid control, allowing you to add and remove columns and rows as needed (though you will also have to dynamically add and remove canvasses, though this isn't too difficult.

If you're worried about the contents of your "Card" moving around when the box is rescaled, surround it in a viewbox. It will manage all your scaling for you, and ensures your card uses as much real estate as it can get. Alternatively you could use a RenderTransform, but a lot of these might slow your program down (Experts: does the viewbox operate using RenderTransforms? If so this point is moot)

To ensure the cards maintain their aspect ratios make sure each Image's Stretch attribute is set to "Uniform", making them all keep the same size could be done by designating a master card, and binding heights and widths of all subsequent cards to this original card, though that is a little messy and doesn't allow the cards to expand. Another solution is to set a single size for each card manually, animating this when you want to expand or shrink.

Nick Udell
If he uses a RenderTransform the content can be rendered outside its bounds, anyway, with no need to set other properties. Also, he should probably utilize ItemsControls rather than dynamic canvases, since he could then use the binding engine and bind to collections rather than constantly updating and creating new canvases in code.
Charlie
Good idea, but he'd still need to use canvases for exact item placement, translating them from fixed points might be too complex. Though the canvases could be created as part of the ItemsTemplate for a parent control.
Nick Udell
+2  A: 

This sounds like a great scenario for Composite Application ala Prism. It provides solid framework for implementing regions, modules, sending message between modules etc... From looking at your screen captures, developing a shell with different regions and dropping modules into them would probably greatly benefit your layout. As for the cards themselves, perhaps they could be modules as well?

Check out: http://msdn.microsoft.com/en-us/library/ff649780.aspx

Particualy good examples come with the download package including a stock market like application and event aggregator example.

Matthew
I'm not sure having the cards as modules would be very efficient, but other than that, Prism would help out a lot.
Jeff Wain
I've looked a bit at prism, and confess I've gotten a little lost. It is not clear to me how it would help, as coordinating the card sizes between the regions seems tricky when the window is resized. Having a card overflow its region and overlay another region during mouse-over expansion also seems tricky.
Doug
This may be useful, but will probably just add unnecessary complexity to a problem WPF is capable of handling all on its own.
Charlie
@Doug I've only started using Prism myself but from what I've read you should be able to put an ItemsControl into a Region. From there when the window is resize it would translate down into your Regions. If you made a 'Card' control it could draw itself to reflect the new Region size. See Layout and Regions @ http://msdn.microsoft.com/en-us/library/ff648265.aspx
Matthew
+1  A: 

You said:

Decomposing the play area into smaller user controls would leverage the WPF layout capabilities, but it seems like decomposition would prevent the cards from expanding into adjacent user controls during the mouse-over, so that doesn't seem feasible either.

But this is not correct. Decomposition is absolutely the right approach to take, and this would not prevent the cards from expanding into adjacent user controls. The reason being that you can use a RenderTransform rather than a LayoutTransform. See this example, by Charles Petzold, or this article, to visualize the difference. Because a RenderTransform is applied after the layout has already occurred, your cards would be able to expand outside their bounds.

Given that decomposition is the right approach, I would arrange your various card collections into a Grid, with each collection being an ItemsControl. The ItemsControl should bind its ItemsSource property to some collection, and then you can provide a custom ItemTemplate that would display the image and any other information. I would be hesitant to use a Canvas, as this would restrict you to hard-coding the positions for the cards (which is a very WinForms-like solution for a problem that can be far more elegantly solved). Take advantage of WPF's fantastic layout engine and use nested grids and items controls to create a dynamic layout. This will ensure that your game board looks good at any resolution and when stretched to various sizes.

Charlie
Thank you for the helpful info. RenderTransform should solve the card overflow problem. One other problem with decomposition, however, is keeping the cards the same size across all the children. As an example, consider a parent container control and two child controls (one for each player's cards). When the outer window is resized, I need to calculate new card sizes based on whichever child area has the most cards. Is there an event I can hook in the container after the children have been positioned (and sized) so that I can calculate the card size and send it to the children?
Doug
Rather than hooking into an event, you should bind the card size (for all cards) to a single consistent property on your view-model. Then when a child area gains or loses a card, you recalculate this size property, and all of the bindings are updated automatically.
Charlie