More servicesWindows Live
HomeHotmailSpacesOneCare
 
MSN
Sign in
 
 
Spaces home  BadCorporateLogoPhotosProfileFriendsMore Tools Explore the Spaces community

BadCorporateLogo

Good Workmen Never Quarrel with their Tools
June 24

More on Designers... Still to Come

I've started the next three posts for my series on customizing the designer. Unfortunately, I haven't had time to finish any of them. I've received a couple messages from people saying thanks for the info... But when is the next part coming? :-)

I really appreciate that people are interested, and I do intend to continue writing. Sorry that it's taking me so long. Life has caught up with me.

When I built my menu designer in the first place, it was because I was looking for a diversion after writing a big design document. Well, implementation of that design is underway, and I'm having to put in longer hours to try to keep the schedule I set for myself (I ran into a couple unexpected setbacks -- which, ironically, is generally expected). That leaves less time at the end of the day for other interests.

Add to that the fact that it finally stopped raining (it rains all winter here), and suddenly I don't have an excuse anymore to sit inside instead of going out to work on my house and property. The remodel I started in January isn't finished yet, and I've got family coming to visit later this summer. So my evening hours are spent working with power tools instead of software tools.

My wife doesn't care who's waiting for my next blog post - she wants the house put back together and the furniture moved back in!

Thanks for your patience... I'll be posting the next article as soon as I can!

June 10

Making a Designer of Your Own - House Salad

Last time, I started to write about the managed designer infrastructure in Visual Studio. This infrastructure is all part of the .NET Framework's System.ComponentModel namespace, and so it naturally deals with components (things that implement System.ComponentModel.IComponent) as the base unit. The component model is meant to provide a generic framework for any kind of designer, but in Visual Studio it was implemented specifically to support Windows Forms and source code generation. It was later made more generic, with the intention of supporting any components, but there is still a lot of implementation that works only with Controls.

So what does the Control designer have to do with making a designer for XNA Framework game components? Nothing, except that in order to re-use the existing infrastructure, you'll need to know a bit about Control designers.

Combining all the ingredients available in the Component Model to create a visual programming experience for XNA game components takes a fair bit of background knowledge. As I said in my last post, you can't just look at my recipe for a Game State designer and know how to build your own Input designer. The bulk of the implementation isn't in my code - it's in the designer infrastructure. If you want to make your own dishes, you need to know what's in the pantry, and how to combine certain things to get the desired flavor.

Last time, I showed how any class deriving from Component will support a design view in Visual Studio. This design view is based on the concept of composition. You add objects to the class by dragging them from the Toolbox onto the design surface. Doing so results in a field being declared for the new object instance, and code to instantiate and initialize it. The designer lets you select and name objects on the design surface so that you can further edit them in the Properties Window. After making these changes, the entire design surface is serialized to code so that the composite class can be compiled and then re-created at runtime.

When you design a Control, the objects on the design surface are usually represented at design time by the same objects you'll use at runtime. So when you add a Button to the Form designer, a real Button instance is created on the design surface to represent the Button instance you'll get at runtime. When you change the Text property on the Button in the Properties Window, the text on the button changes on the design surface because you've actually changed the Text property of that Button instance.

You can change the BackColor and ForeColor properties for more visual evidence that the Properties Window is letting you edit the actual control instance on the design surface. At first glance, you might imagine that the Properties Window is using reflection to enumerate the properties of the selected object, get their values, and allow you to set them.

That makes sense and might even seem pretty obvious at first... But if you change the Visible property to false, suddenly things are not so obvious. The button is still visible on the design surface! (If you aren't sure what Visible is intended to do, you can run the project at this point to see that the button isn't visible at runtime.)

image

Figure 1 - Something invisible is revealed when you set the Visible property to false.

The reason the Button didn't disappear is because the Visible property in the Properties Window doesn't belong to the Button the way the Text, BackColor, and ForeColor properties do. Instead, it belongs to the Button's designer. :-)

At this point, things are undoubtedly going to get confusing. The term "designer" is overloaded, and I need to use it to mean something other than the visually-oriented composition editor we've been discussing until now.

Every component created on the design surface has a designer associated with it. The designer is another class that modifies the design-time appearance and behavior of the component. One role of the component designer is to filter or enhance an object's metadata. To put it another way, it can change the attributes on a component or its properties, and can remove, replace, or add properties. A designer can do this to change both the appearance and behavior of a control at design-time.

As an example, the Button's designer replaces the Visible property with a different property that comes from the designer instance. In designer-speak, this is called shadowing. In the Properties Window, the Visible property appears to belong to the Button, but in fact it belongs to the Button's designer. When you change the Visible property value, it's the designer's property that is changed (you don't see the effect because the designer was never visible in the first place). This prevents the Button instance from becoming invisible, which would make it hard to select or reposition it on the design surface.

If you look more closely at the properties of the Button instance, you'll notice there are a few listed that aren't actually properties of the Button class. To pick out just a few, in the Design category, you'll find "GenerateMember", "Locked", and "Modifiers". These control whether the instance will be declared as a field of the class or a local variable, whether the control can be moved or resized on the design surface, and the visibility modifier of the field declaration (if there is one), respectively. Changing these properties affects the code generated for the Button instance, but they don't represent actual properties of any object on the design surface.

A basic designer implementation is provided by System.ComponentModel.Design.ComponentDesigner. This class makes it easy to override just the parts you want to modify. As the documentation says, you associate a designer with a component through the DesignerAttribute.

Now, a word of caution when using the DesignerAttribute. Its constructor has overloads to use System.Type or System.String arguments to specify the designer's type. If you implement the designer in the same assembly as the component, you can use the System.Type constructor. However, if you implement the designer in a separate assembly, it is almost always a good idea to use the System.String constructor. The reason is that the designer is always given a reference to the component instance, and if you want the designer to be able to reference that component by its proper type, it will be dependent on the component's assembly. That precludes having a reference to the designer assembly from the component assembly, because then you have a circular dependency. So the way to avoid it is to allow you component to be ignorant of its designer, except for its name.

A very good reason to have the designer in a different assembly is because the designer will often depend on a bunch of assemblies that you don't ever need at runtime, or that maybe aren't redistributable. In fact, the designer class itself is not needed at runtime! So if you keep it in a separate assembly, then you can keep your runtime libraries lightweight and your redist small. Your customers will appreciate that!

I think I'll wrap it up here. Next time, I'll write more about how designers affect the behavior of controls on the design surface. At this point, it's also appropriate to start using code samples to illustrate, and I don't have anything ready right now.

June 02

Making a Designer of Your Own - A Starter Course

A couple people have e-mailed me recently about my "teaser" screen shots, because they want to know how I did it. As much as I'd like to explain the menu designer I'm working on, it's complicated. Writing long blog entries just isn't as interesting as writing code (for me, at least), so in all honesty, I'd rather work on the designer than try to explain what I'm doing. The easiest way to explain it is, I'm using the Windows Forms designer framework. It's reasonably well-documented, but not all in one place. You can't read it like a book.

Someone demanded asked me for my source code today because he wants to build his own designer. Source code isn't the place to start in this case. That would be like looking at a big turkey dinner I just prepared, and then asking me for the recipe because you want to grill a salmon. What you really want to know is how to cook.

For that reason, I'm not going to share my recipe -- not yet, at least. Instead, I'll try to focus on explaining how to cook. Let's start with an appetizer, and work our way up to the entree.

In Visual Studio, there is already an extensible designer infrastructure for components (types that implement System.ComponentModel.IComponent). This infrastructure has further specialization to support Components (types that derive from System.ComponentModel.Component) and Controls (types that derive from System.Windows.Forms.Control). Note that the general infrastructure is for the IComponent interface, while there is extra stuff for the Component class, which is a default implementation of the IComponent interface. There is even more extra stuff for Controls and various specialized subclasses, like Form.

The extra stuff is the interesting part. Windows Forms has an editor registered in Visual Studio that will handle any code file in a code project that contains a class declaration for a class that derives from Component. The IDE determines this by asking the language service (the thing that gives you Intellisense) for the first class declaration in a code file. Then it uses another service (probably ITypeResolutionService, but I forget, and it doesn't matter enough to check), to determine whether the base class of the type declaration derives from Component. If so, then the file is treated as a "component" file.

Let's take a look at an example.

image

Figure 1 shows a regular Class Library project in VC# Express. Look what happens when I edit the declaration for Class1 to derive from Component.

image

Figure 2 shows the subtle change. In the Solution Explorer, the icon next to the Class1.cs file is the "component file" icon. Even though the file is already open in the code editor, now when you double-click on the file in Solution Explorer, it will open the Component Designer.

image

Figure 3 shows the Class1 class in the the Component Designer.

The Component Designer provides a basic, visual code editing experience. You can drag and drop components onto the Class1 design view from the Toolbox, and edit those objects in the Properties Window. The changes you make will be serialized to code in a method called InitializeComponent in the Class1 class declaration.

After some experimentation, an observant person will note several things. First, the generated code appears in InitializeComponent, but that function is never called. This is because the designer generally ignores everything else in the class except that function. The standard practice is to define a default constructor that calls InitializeComponent. Since Class1 was not created from a Component template, I'll need to add that code myself.

Second, the name of Class1 in the Properties Window refers to the name of the type Class1. Every other component dropped onto the design surface has a name that refers to the name of the member variable that the component is assigned to.

Third, the properties in the Properties Window aren't the properties of Class1. If you add a new property to the class definition in Class1.cs, it won't appear in the Properties Window at all.

For example, let's add a string property to Class1.

    public class Class1 : Component
    {
        private string myProperty;

        public string MyProperty
        {
            get { return myProperty; }
            set { myProperty = value; }
        }
    }

Going back to the design view, MyProperty is nowhere to be seen. If you look closely at the Properties Window, you'll see the reason why. It shows Class1 to be an instance of System.ComponentModel.Component. This is important -- the designer creates an instance of the component's base class at design time. The same goes for editing Forms or Controls.

This only applies to the root component, however -- the type being designed -- and not for components that are dragged onto the design surface. Part of the reason for this is because you are changing the source code for the type as you edit it in the designer -- making it difficult to keep a compiled instance of the type-being-edited up to date with the latest changes.

Now that you know the designer creates an instance of the base type instead of the actual type, you might already have guessed what I will do next. I'm going to add another class to the project in its own file, Class2.cs, and declare a new class Class2 that derives from Class1.

Now, as long as I've compiled the project first, I can double-click on Class2.cs and have it open up in the designer. This happens because Class2 derives from Component. Except this time, the instance on the design surface is an instance of Class1! See how the Properties Window shows MyProperty?

image

Figure 4 shows that you can design your own types in the Component Designer.

Getting your own class into the Component Designer is the first step. Now it's time to customize!

There are a lot of ways to customize the design-time experience for particular components. Most of it is driven through metadata, or custom attributes.

For example, using attributes, I can change the way MyProperty appears in the Properties Window.

    public class Class1 : Component
    {
        private string myProperty = "elephant";

        [DefaultValue("elephant")]
        [Category("Animals")]
        [DisplayName("My Property")]
        [Description("Set this property to the first thing that comes to mind.")]
        public string MyProperty
        {
            get { return myProperty; }
            set { myProperty = value; }
        }
    }

After rebuilding the project, the Properties Window for Class2 reflects the changes.

image

Figure 5 provides an example of how you can customize the design-time behavior of your components. The MyProperty property is now displayed as My Property (note the space), it is in the Animals category instead of Misc, and it has a description at the bottom of the Properties Window. The value of the property is "elephant", which is the default value. This is important to the designer because when a property has its default value, it is not serialized to code. Imagine having a component with two hundred properties, and all but one is set to its default value (the value it has when constructed from a default constructor). You would want the code generated for that component to only set the one property that was different from its default, not all 200 values. Otherwise, such a class would initialize 199 of its properties twice!

It's important to note here that the DefaultValueAttribute custom attribute is used as a hint to the designer, and doesn't actually set the property to anything. It's purpose is to tell the designer what the default value is, so the designer can compare the property's actual value against it when it's time to generate code. The Properties Window also uses it to visually indicate which properties need to be serialized -- that is, if a property's value is anything but the default, then that value will appear in bold in the Properties Window. If you specify the wrong value in the attribute, the designer will behave incorrectly -- so be careful!

There are a bunch of attributes in System.ComponentModel that are interesting. A few noteworthy attributes in terms of editing properties are:

  • BrowsableAttribute -- use Browsable(false) to prevent a property from appearing in the Properties Window.
  • DesignerSerializationVisibilityAttribute -- choose not to generate code for a property (often used in combination with Browsable(false)), or serialize only the object returned by the property (like a read-only property that returns an editable collection).
  • ReadOnlyAttribute -- make a read/write property appear read-only in the Properties Window.
  • TypeConverterAttribute -- allows you to specify a custom type converter (hint: this is where you can do neat stuff!). Usually, you want to convert between a custom type and its string representation. This way, your custom type can both be displayed in the Properties Windows, and be edited by a user typing in a new value.
  • EditorAttribute -- allows you to specify a custom editor for the property (hint: this is another place to do neat stuff!). By creating a custom UITypeEditor, you can make rich design-time experiences for your properties, like the color picker for System.Drawing.Color properties on Control classes.

In my menu designer, I've been getting a lot of mileage out of creating custom type converters. I created one to enumerate all types that derive from GameScreen to assign to a System.Type property, I created one to enumerate the names of all SpriteFonts in the nested Content project to assign to a string property, and I created another to enumerate the names of all Texture2D content to assign to another string property.

Okay, that's as far as I'm going to get in one sitting. Our starter course is looking pretty modest -- more like cheese and crackers than shrimp cocktail -- but at least there's something on the plate!

May 27

Compelling Design-Time Experiences in an XNA Game Studio-Based Designer

The other week, I started playing around with building a designer for Game State Management. It wasn't long before I had something working -- mainly because I leveraged a lot of existing code in the .NET Framework. The screen shots I showed previously are the result -- a design surface that can interact with the Toolbox and Properties Window. This is sufficient to quickly add menu items, edit their appearance, and even generate event handlers for input events.

I was pretty excited about it and that's why I posted to my blog. Unfortunately, the MenuScreen and MenuEntry classes in the Game State Management sample weren't intended to be designable. They didn't have many interesting properties or events, so the designer could only save you from writing a couple lines of code before you had to drop back to source view and type the rest of your program with a keyboard. Gross! ;-P

Last week, I thought about how I often navigate menu screens in games -- selecting a menu item to activate another menu screen, then canceling back to the previous screen, and so forth. To build a navigation system without writing any code, I needed a way to specify what the next screen from within the designer.

What I came up with was a property of type System.Type, which could only be set to a type that derives from GameScreen (the base class for all screens). I implemented a navigation item that, when selected, would activate a new instance of the specified GameScreen type and begin a transition to it. That was pretty easy, except for the idea of someone trying to edit the System.Type property in the Properties Window.

You can customize the design-time behavior of properties by using a TypeConverter, that you can associate with the property by using a TypeConverterAttribute. My custom TypeConverter class uses ITypeDiscoveryService to enumerate all the available GameScreen types in the project and list them in a combobox for easy selection. This new ability to select a screen type in the designer makes it possible to build fancy menus with pop-ups and structured navigation without writing any code. After that, it was a snap to add another navigation item that could both specify the next screen, as well as a second screen to animate during lengthy, asynchronous content loads.

More recently, I thought about how I'd like to customize the appearance of a menu. I'd like to do things like customize the menu's font, or create background screens by selecting textures. I actually would like to be able to create a whole 2D scene out of sprites on non-menu screens, or to display sprites when menu items are activated... But first things first.

A compelling design-time experience for any of this would require the designer to know about the content built by a project. If the menu had a Font property, you'd want the Properties Window to let you choose from a list of SpriteFont items in your project. Then, for a really good design-time experience, you'd want the designer to actually use that SpriteFont when rendering the design surface.

The way I picture that working would be to have a string property for the Font, which specifies the asset name of the SpriteFont. The TypeConverter for this property would enumerate the SpriteFont items from the project, and then the menu would load the asset by name during initialization.

The hard part, unfortunately, is figuring out what SpriteFonts are available in the project at design time.

Tonight, I believe I've solve this problem.

image

This figure shows a SpriteFont property added to the MenuScreen which lists available SpriteFonts from the content project at design-time. It doesn't yet use that font for rendering the design surface, but that's the next logical step.

Writing a TypeConverter to be aware of the content items was tricky and required very specific domain knowledge of Visual Studio and the managed designer infrastructure, but didn't take a lot of code. I'm still implementing everything in Visual C# Express 2005, and am able to run it and debug it without installing any packages or add-ins into the IDE. I also do not have the VS SDK installed on my laptop. To be honest, I'm a bit surprised at how much you can do without the VS SDK!

Being able to enumerate content from within the designer opens the door to all kinds of interesting scenarios! I'm looking forward to thinking about it some more! (I'm always looking forward to having free time!) :-)

May 20

Going Somewhere? Menu Navigation Woes.

There have been about a billion views of my blog since I posted screen shots of the menu designer I started working on last week. For those of you who are keenly interested in that particular topic, I am still working on it. I mean you, Eli! ;-)

Getting a design surface going took some technical trickery, but I've done it before so it was no big deal. The real difficult part is making a set of classes that are intuitively designable. What does that mean? Well, I guess I'm trying to figure out the right set of classes to support, the right properties to put on all the classes, and the right set of toolbox items to have available. I want something that is both simple enough to get people started right away, and easy enough to customize that not every game looks like it came from the same designer.

I've been building this designer and its components in Visual C# 2005 Express Edition. A few of you might be surprised at that, mainly because you can't use the VS SDK to build customizations for VC# Express (except for some very special cases like XNA Game Studio, but my personal projects are no exception). The fact is, a visual designer doesn't require customizations to the IDE. There's already a powerful designer infrastructure built-in to VC# Express -- the very same one used by Windows Forms! To create custom design-time experiences, you don't need to modify anything in the IDE. All you need is the right metadata on your classes, and you can take control of that infrastructure for yourself.

Anyway, tonight I am trying to make sense of the mess I made with respect to navigation items. I've created an item that you can drop onto the menu and then specify what type of screen it navigates to. Selecting this menu item when the menu is running will transition to the next screen. I also gave it the option of specifying a custom background for the next screen, and for loading it on a separate thread with a load screen. The trouble is that if you use the load screen, you can't "cancel" back to the previous screen. If you don't use it, then you can. I'm having trouble figuring out what to call the properties so the resulting behavior will be obvious.

Clearly, this one navigation item is too complex. The best thing to do is probably to have more than one navigation item -- say, one that pushes new screens onto the screen manager's stack (allowing back-button navigation), and another that clears the stack to start from scratch (like a gameplay or level screen). That would definitely be easier to understand, but what do I call them? Argh!

The thing I like about the navigation items is that it gives you an easy way to build a complex menu system without writing any code.

image

This screen clipping above shows how the NextScreen property has a dropdown combobox to select the type of the next screen. The combobox will list all the game screens in your project (or referenced by it). It's neat and easy, but the resulting behavior is not yet intuitive.

Coming up with good names is hard. It reminds me of a quote from Knuth:

The most important thing in the programming language is the name. A language will not succeed without a good name. I have recently invented a very good name and now I am looking for a suitable language.
Donald Knuth

Once again, it's time for bed.

May 14

Game State Management for N00bs Like Me

Last week, I finished writing a design document that I'd been working on for about a month. The design review meeting seemed to go well -- at least, I think it did. There were a lot of nodding heads around the room as I presented my design, and I like to think they were nodding in approval. The alternative is that they were nodding off to sleep, but I'm going to assume that wasn't it. :-)

Anyway, after so long working on the same thing, I decided to take a look at something new and different this past weekend. I installed XNA Game Studio 2.0 and VC# 2005 Express Edition on my laptop at home and started to poke around at some samples. I downloaded Nazeeh's Game State Management project template -- something that I consider to be a terrific example of what a starter kit should really be -- and created a project from it.

For those of you who are not familiar with this template, I'll briefly describe it. It creates a project for a game that has a couple menu screens, and an animated loading screen to entertain people while larger screens (aka, game levels) load asynchronously in the background. It is a slick starting point for just about any game.

I ran the project and put breakpoints in a few places to see how things were being invoked to get a better understanding of the code. Then I modified a few things and found it was reasonably easy to make changes and customize the screens.

For those of you who don't know me, my background is in tools. I make the programs that make it easier to develop games with the XNA Framework, but I don't work on the XNA Framework libraries, and I hardly know anything about game programming. So when I looked at the sample code and saw how easy it was to customize the game state, the first thing I thought of was, "That's pretty cool -- but I can make this easier!"

The funny thing about things being easy is that they are usually easy to automate. Building menus, changing properties, handling input events... Hmm, where have I seen that before?

Instead of playing around with XNA Game Studio to finally learn about game programming, I decided to build a designer for Game State Management! :-)

Tonight, I have a working menu designer. I used the WinFormsGraphicsDevice sample to create a design surface that actually draws the menu screen as menu items are added to it, and updates in real-time as properties are edited. To design a menu, I just need to declare a class that derives from my MenuScreen type, then double-click on the code file, and up pops the designer!

I only have two types of menu items to play with at the moment -- a counter and a toggle. There is a lot of work to do before it would be suitable for general use, but I'm excited about the possibilities. The samples provided a big head-start!

Here's a series of screen shots to show you what I'm talking about.

image

Figure 1 - Shows the code required to get started (an obvious template candidate). The context menu provides a "View Designer" command to open the designer. Double-clicking the code file in Solution Explorer works just as well.

image

Figure 2 - The initial design view. This is an XNA GraphicsDevice cleared to CornflowerBlue and rendering a menu with no items.

image

Figure 3 - Shows the menu after editing the Title property, then adding a ToggleMenuItem and editing its label and name.

image

Figure 4 - Back to code view to see what the designer generated.

It might not look like much today, but it kept me busy for the past four evenings and was kind of fun! My goal really is to make it easier to get going right away. When I've worked through all the kinks enough to share the designer, I'll blog about how the designer works.

Now it's time for bed!

May 09

Game Studio and Visual Studio 2008

Last December, we released XNA Game Studio 2.0 with support for Visual Studio 2005. This was a big step up from having Visual C# Express support only -- but since it was hot on the heels of the Visual Studio 2008 release, some people were still disappointed. People were excited to get VS 2008, and they were excited to get XGS 2.0 -- but they wouldn't work together!

In and around Microsoft, we have a pretty big community of XGS enthusiasts. Some of them have backgrounds in tools development, including Visual Studio extensibility. Those people knew that the extensibility model for VS 2008 was completely backwards-compatible with VS 2005 (or at least it is advertised to be). So why couldn't XGS 2.0 install into VS 2008?

There were inevitable questions about whether XGS 2.0 could be made to work in VS 2008. Not the naive questions about whether you could compile code against the XNA Framework -- but whether you could get the project system to install into it, use the Content Pipeline, and debug Xbox 360 games. In January, we gave it a shot and it didn't work. It turned out there was a backwards-compatibility bug in VS 2008 that prevented content projects from loading into the IDE. It was a tiny bug with a trivial fix, but one that had to be made by in Visual Studio itself.

As a result of finding this bug, word got out that XGS 2.0 didn't support VS 2008 because it was blocked by a compatibility issue that will be fixed in VS 2008 SP1. When we released our CTP this week, someone asked me how we did it -- because weren't we blocked by the same bug that blocked XGS 2.0?

I'd like to set the record straight on that, because we were never blocked by a compatibility issue.

The incompatibility makes for a convenient excuse, but it’s not the reason we didn’t support VS 2008 in XGS 2.0. Our test team was overburdened even as it was for the XGS 2.0 release. There was absolutely no way we could have delivered support for two versions of VS, even if we’d wanted to. We knew that from the beginning, and originally JoeN and I pushed to make the one version be VS 2008. In the end, our leadership vetoed our proposal and chose VS 2005. That decision was made around April -- eight months before the release of XGS 2.0!

We put our heads down and committed to the task at hand -- shipping on VS 2005.

It wasn't until after VS 2008 had shipped and after XGS 2.0 had shipped that we finally looked at VS 2008. We found and identified the compatibility bug within an hour of getting the XGS 2.0 bits installed into VS 2008. So to be completely honest, if we’d had any intention of supporting VS 2008, we would have easily caught this bug before the Orcas release. Its impact on our projects was obvious, and the fix is trivial. If we’d even bothered to try our stuff in VS 2008 before it shipped, that bug would no longer exist.

Letting people believe that a bug is the reason we don't support VS 2008 is like saying, “Hey, it wasn't not our fault – blame the VS team!” But in fact it is entirely our own responsibility. Whether everyone understands it or not, we decided not to support VS 2008.

I feel pretty strongly now that we made the right choice -- even though I was opposed to it at the time of the decision. If you don't agree, I happen to have good news for you... We're supporting VS 2008 in XGS 3.0!

The XGS 3.0 CTP was released this week to preview our addition of support for Zune, and the CTP targets VS 2008! With this release, you can develop games for Zune from either VS 2008 Standard (or higher) or VC# 2008 Express Edition!

View more entries
 

Xbox Live GamerCard

Righteous Tool
Xbox Live GamerCard
Rep:
Reputation:Reputation:Reputation:Reputation:Reputation:
Score:
8193
Zone:
Recreation
CrackdownLEGO Star Wars: TCSJewel QuestFroggerPenny Arcade Episode 1
Updated 2/25/2008
Updated 6/11/2008