Silverlight Dependency Property Snippet

by Cameron Albert 9. March 2009 10:50

I created this snippet for Dependency Properties in Silverlight. If you are creating custom Silverlight controls you might find this snippet useful. I stored my code snippets here "My Documents\Visual Studio 2008\Code Snippets\Visual C#\My Code Snippets" so all you need to do is copy the file to this location. Visual Studio should load the snippet the next time you start it up. If it does not then you can go into "Tools", "Code Snippets Manager" and load it up manually.

Now when you type "propsl" and hit tab you will get a stubbed out property like below, where tabbing through the items allows you to change the type and name of the property, set the owning class and provide a default value.

public int MyProperty
{
	get { return (int)GetValue(MyPropertyProperty); }
	set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty = 
     DependencyProperty.Register("MyProperty", typeof(int), typeof(MyClass), 
     new PropertyMetadata(null, new PropertyChangedCallback(MyClass.OnMyPropertyPropertyChanged)));
private static void OnMyPropertyPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{

Here is the code for the snippet and a link to the file:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>propsl</Title>
            <Shortcut>propsl</Shortcut>
            <Description>Code snippet for an automatically implemented dependency property in Silverlight.</Description>
            <Author>Cameron Albert</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>type</ID>
                    <ToolTip>Property type</ToolTip>
                    <Default>int</Default>
                </Literal>
                <Literal>
                    <ID>property</ID>
                    <ToolTip>Property name</ToolTip>
                    <Default>MyProperty</Default>
                </Literal>
                <Literal>
                    <ID>ownerClass</ID>
                    <ToolTip>Owner Class</ToolTip>
                    <Default>MyClass</Default>
                </Literal>
                <Literal>
                    <ID>defaultValue</ID>
                    <ToolTip>Default Value</ToolTip>
                    <Default>null</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp">
                <![CDATA[public $type$ $property$
            {
                get { return ($type$)GetValue($property$Property); }
                set { SetValue($property$Property, value); }
            }
            public static readonly DependencyProperty $property$Property = DependencyProperty.Register("$property$", typeof($type$), typeof($ownerClass$), new PropertyMetadata($defaultValue$, new PropertyChangedCallback($ownerClass$.On$property$PropertyChanged)));
            private static void On$property$PropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
            {
            }
            $end$]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

Silverlight Dependency Property Snippet

 

Perenthia Armorial Version 1.0

by Cameron Albert 18. January 2009 23:34

I uploaded the Perenthia Armorial Version 1.0 to the Perenthia Alpha site tonight. The Perenthia Armorial is a way to provide external access to Perenthia information. The first version includes the Player Bar feature, where you pass a player name to the service and renders back HTML with player details.

The call to http://alpha.perenthia.com/Armorial/Armorial.ashx?n=Aldarian renders the following:

Client/Server Communication Data Formats

by Cameron Albert 5. January 2009 23:41

While developing Perenthia I tried several different communication formats from sending JSON serialized objects back and forth to sending byte arrays containing mostly integers indicating what should be loaded and displayed, what commands to execute etc. What I eventually settled on was a bit of a hybrid. I've created a simple set of "tags" that can be sent to and from the server. The tags are nothing more than pipe delimited strings contained within curly braces and there are a limited number of tags that provide simplicity yet flexibility with the data that is transmitted. The tags are represented in C# by tag objects allow me to create them, query them, etc.

A simple command tag for the SAY command might look like this: {CMD|SAY|"Hello World!"}

I wrote a custom tag writer class that parses the tag objects into strings to be sent to the client and likewise a tag reader that reads the strings sent from the client and parses them into tag objects.

The client can only send commands to the server but the server sends commands, messages and objects to the client. The commands are all the same, the CMD text then the one word name of the command and then a list of arguments, messages are system, chat and tell messages but objects have a bit more information. For instance, an object tag encompasses several different types starting with a base ACTOR, a PLACE, a PLAYER and a PROP or property. The ACTOR, PLACE and PLAYER tags all define the ID, Name and Descriptions of the objects, with some additional data per type but the PROP tag defines the object id of its owner and a name/value pair. An example of a PLAYER tag with properties for x, y and z coordinates might be:

{OBJ|PLAYER|3756|"Aldarian"|"My character description..."}

{OBJ|PROP|3756|"X"|45}

{OBJ|PROP|3756|"Y"|26}

{OBJ|PROP|3756|"Z"|0}

The client can find and parse the player tag and then find the properties associated with that player instance. The way the server works now it will send the full player tag once logged in and a character is selected and then from then on out it just sends property updates.

Using this type of tag structure allows quite a bit of flexibility for the client. I could choose to write a very simple client that only displays MSG tags, much like a MUD, I could write a simple 2D interface client and display and move sprites about the map using the properties from the server, etc. Once Perenthia is up and running I will probably post some information on the tags and how to talk with the server should someone feel the need to write their own client. :)

Game Scripting in IronPython

by Cameron Albert 16. September 2008 18:20

Since my last post about game scripting I have had the chance to play around more with IronPython. I wrote a quick MUD type demo game that utilizes C# and IronPython on the server and uses Silverlight 2 as the user interface.

Play Cameron's Dungeon!

I wanted to explore this scripting for Perenthia because I want to be able to add dynamic functions or scripts to objects. I wrote this sample to test out using C# as base classes for IronPython classes and passing operations back and forth between the two. As such the source is kind of loose but the concepts are demonstrated.

What I have is a basic framework of C# classes that define an Actor (any object in the game), a Client (a connected player), a Game (the logic of the game) and a Server (for handling HTTP communication, etc.). The Actor, Client and Game classes also server as the base classes for IronPython classes or Creatures, NPCs, Players and Rooms.

Server is the main C# class and gets initialized in the global.asax file like so:

protected void Application_Start(object sender, EventArgs e)
        {
            SLGameEngine.Server.Start(Server.MapPath("python/startup.py"),
                new string[] 
                { 
                    Server.MapPath("python"), 
                    Server.MapPath("bin"),
                    System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory()
                });
        }

Inside the Server class a static class called PyEngine handles all the execution of the IronPython code. The Start method inside the server class makes a few calls into the PyEngine that starts the engine and executes the code in the /python/startup.py script:

public static void Start(string startupFilePath, string[] searchPaths)
        {
            try
            {
                PyEngine.Startup(searchPaths);
                PyEngine.AddAssembly(typeof(Game).Assembly);

                PyEngine.ExecuteFile(startupFilePath);

                _timer = new Timer(300000);
                _timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
                _timer.Start();
            }
            catch (Exception ex)
            {
                Log.Write(ex.ToString());
            }
        }

The first two lines initialize the scripting engine while the third actually executes the startup.py script. Inside the startup script is called to create a global instance of the Game class and to start the game running. The timer stuff is used to remove inactive players. After the game is started any input sent from the user to the server is handled by an IHttpHandler class called CommandHandler. This class parses the request and send the input to the Server class that will actually pass the handling of the input to the IronPython Game class.

public void ProcessRequest(HttpContext context)
        {
            string response = "Invalid Command";
            try
            {
                string cmd = context.Request.Form["cmd"];
                string ip = context.Request.UserHostAddress;
                if (!String.IsNullOrEmpty(cmd))
                {
                    if (cmd.StartsWith("NEW"))
                    {
                        response = Intro.GetIntro(context);
                    }
                    else if (cmd.StartsWith("NAME "))
                    {
                        string name = cmd.Substring(5);
                        if (Server.NameExists(name))
                        {
                            response = "That name has already been used, please choose another.";
                        }
                        else
                        {
                            Server.AddClient(ip, name);
                            response = String.Format("Welcome {0}!{1}{2}", name, Environment.NewLine, Server.GetClientOutput(ip));
                        }
                    }
                    else
                    {
                        Server.ProcessInput(ip, cmd);
                        response = Server.GetClientOutput(ip);
                    }
                }

                if (String.IsNullOrEmpty(response))
                {
                    response = "Internal Error";
                }
            }
            catch (Exception ex)
            {
                Log.Write(ex.ToString());
                response = "Internal Error";
            }
            finally
            {
                context.Response.ContentType = "text/plain";
                context.Response.Write(response);
            }
        }

The Server class calls methods on the PyEngine class to actually execute IronPython method calls:

public static void ProcessInput(string ipAddress, string input)
        {
            PyEngine.CallMethod("game", "ProcessInput", ipAddress, input);
        }

From PyEngine:

public static void CallMethod(string variableName, string methodName, params object[] args)
        {
            try
            {
                ObjectOperations ops = _engine.Operations;
                if (_scope.ContainsVariable(variableName))
                {
                    object instance = _scope.GetVariable(variableName);
                    if (instance != null)
                    {
                        if (ops.ContainsMember(instance, methodName))
                        {
                            object method = ops.GetMember(instance, methodName);
                            ops.Call(method, args);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Write(ex.ToString());
            }
        }

The IronPython Game class ProcessInput method:

def ProcessInput(self, ip, input):
        words = input.split(' ')
        client = self.Clients[ip]
        
        # Update the stored date on the client so it will stay active.
        client.LastUpdateDate = DateTime.Now;
        
        # Get the remainder of the words as a string for chat messages.
        index = 0
        sb = StringBuilder()
        for w in words:
            if index > 0:
                sb.Append(w).Append(" ")
            index += 1
        message = sb.AppendLine().ToString()

        # Setup potential movement realted variables.
        prevRoom = client.player.room
        exit = -1
        direction = ""
        if words[0] == "n":
            exit = 0
            direction = "north"
        elif words[0] == "s":
            exit = 1
            direction = "south"
        elif words[0] == "e":
            exit = 2
            direction = "east"
        elif words[0] == "w":
            exit = 3
            direction = "west"
        elif words[0] == "look":
            client.Write(ROOMS[client.player.room].ToString(client.player))
        elif words[0] == "say":
            # Send the say message to all players in the game.
            for item in self.Clients:
                c = item.Value
                if c.player.name != client.player.name:
                    c.Write(String.Format("{0} says: {1}", client.player.name, message))
                elif c.player.name == client.player.name:
                    client.Write(String.Format("You say: {0}", message))
        elif words[0] == "who":
            # Print a list of players online.
            client.Write("Who's Online:\n")
            for item in self.Clients:
                client.Write("%s\n"%(item.Value.player.name))
        else:
            client.Write("\nInvalid Command\n")
        
        if exit > -1:
            if ROOMS[client.player.room].exits[exit] > -1:
                # Remove the player from the previous room
                if prevRoom > -1:
                    ROOMS[prevRoom].avatars.remove(client.player)
                
                # Add the player to the current room
                client.player.room = ROOMS[client.player.room].exits[exit]
                ROOMS[client.player.room].avatars.append(client.player)
                
                # Output the resuls to the client
                client.Write("You move %s\n"%(direction))
                client.Write(ROOMS[client.player.room].ToString(client.player))
            else:
                client.Write("You can not move in that direction.\n")

The client.Write statements actually pass control back to C# to write the text into a StringBuilder so that all the text can be retrieved at once before sending the response down to the client. On the client side or Silverlight 2 side, input from the user is sent to the server using my HttpHelper class.

private void ExecuteCommand(string cmd)
        {
            HttpHelper helper = new HttpHelper(new Uri((App.Current as App).ServerUrl), "POST",
                new KeyValuePair<string, string>("cmd", cmd));
            helper.ResponseComplete += new HttpResponseCompleteEventHandler(helper_ResponseComplete);
            helper.Execute();
        }

All responses from the server are just output to the chat window:

void helper_ResponseComplete(HttpResponseCompleteEventArgs e)
        {
            switch (_state)
            {
                case GameState.NewConnection:
                    _state = GameState.Name;
                    break;
                case GameState.Name:
                    _state = GameState.Play;
                    break;    
            }

            if (e.Response.Equals("That name has already been used, please choose another."))
            {
                _state = GameState.Name;
            }
            
            this.UpdateConsole(e.Response);
        }

        private delegate void UpdateConsoleDelegate(string text);
        private void UpdateConsole(string text)
        {
            if (this.CheckAccess())
            {
                _content.Append(text);
                txtConsole.Content = _content.ToString() + Environment.NewLine;

                double scrollOffset = txtConsole.VerticalOffset;
                scrollOffset += txtConsole.ScrollableHeight + txtConsole.ViewportHeight;
                txtConsole.ScrollToVerticalOffset(scrollOffset);
            }
            else
            {
                this.Dispatcher.BeginInvoke(new UpdateConsoleDelegate(this.UpdateConsole), text);
            }
        }

The source is not commented very well and lacks consistency because I was trying different things but does lend as a primer and provide something to build from. Of course, this could handle other client interfaces such as AJAX, etc. but I used Silverlight since Perenthia is going to use Silverlight as its UI.

And, because I am good like that, you can download Cameron's Dungeon Source Code (6MB)!

Or just play Cameron's Dungeon!

Tags:

ASP.NET Development | Game Development | General | Perenthia PBBG | Silverlight 2 Development | Silverlight Games

Silverlight 2 and Cross Domain Web Calls

by Cameron Albert 18. March 2008 15:49

Frank LaVigne has written a nice Silverlight 2 Cross Domain Web Proxy Utility for making any type of web calls from Silverlight 2. This a great utility if you want to serve images or outside content to your Silverlight 2 apps but do not have the ability to setup a domain policy file on the content hosts server.

Tags:

ASP.NET Development | General | Silverlight 2 Development

asp:Silverlight Override

by Cameron Albert 11. March 2008 23:01

If you are using the <asp:Silverlight/> control to host your Silverlight 2.0 apps from an ASPX page and want to be able to provide a custom splash screen as outlined in Pete Brown's post Xap! App! Pow! Packaging and Application Startup in Silverlight 2 Beta 1 - Part 2 I created the following override that allows you to set the SplashScreenSource and OnSourceDownloadProgressChanged values from within the control itself.

Here is the source for the control:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Lionsguard.Web.UI.SilverlightControls
{
    [ToolboxData(@"<{0}:Silverlight runat=""server""></{0}:Silverlight>")]
    public class Silverlight : System.Web.UI.SilverlightControls.Silverlight
    {
        protected override IDictionary<string, string> GetSilverlightParameters()
        {
            IDictionary<string, string> dictionary = base.GetSilverlightParameters();
            if (!String.IsNullOrEmpty(this.SplashScreenSource))
            {
                dictionary.Add("SplashScreenSource", this.SplashScreenSource);
            }
            if (!String.IsNullOrEmpty(this.OnSourceDownloadProgressChanged))
            {
                dictionary.Add("OnSourceDownloadProgressChanged", this.OnSourceDownloadProgressChanged);
            }
            return dictionary;
        }

        [Category("Behavior"), Browsable(true), DefaultValue(""), Description("")]
        public virtual string SplashScreenSource
        {
            get
            {
                return (((string)this.ViewState["SplashScreenSource"]) ?? string.Empty);
            }
            set
            {
                this.ViewState["SplashScreenSource"] = value;
            }
        }

        [Category("Behavior"), Browsable(true), DefaultValue(""), Description("")]
        public virtual string OnSourceDownloadProgressChanged
        {
            get
            {
                return (((string)this.ViewState["OnSourceDownloadProgressChanged"]) ?? string.Empty);
            }
            set
            {
                this.ViewState["OnSourceDownloadProgressChanged"] = value;
            }
        }
    }
}

And here is the way that I am using it in my code:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Perenthia.Web._Default" %>
<%@ Register Assembly="Lionsguard.Web.Silverlight" Namespace="Lionsguard.Web.UI.SilverlightControls" TagPrefix="lg" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Perenthia</title>
    <script language="javascript" type="text/javascript" src="/Common/Scripts/Silverlight.js"></script>
    <script language="javascript" type="text/javascript" src="/Common/Scripts/Splash.js"></script>
</head>
<body>
    <form id="frmMain" runat="server">
        <asp:ScriptManager ID="scriptManager" runat="server"></asp:ScriptManager>
        <div>
            <lg:Silverlight ID="silverlight1" runat="server" Source="~/ClientBin/Perenthia.xap" 
                Version="2.0" Width="800" Height="500" SplashScreenSource="~/Common/Xaml/Splash.xaml" 
                OnSourceDownloadProgressChanged="onSourceDownloadProgressChanged" />
        </div>
    </form>
</body>
</html>

The Splash.js file contains the following JavaScript:

function onSourceDownloadProgressChanged(sender, eventArgs)
{
    sender.findName("uxStatus").Text =  "Loading: " + Math.round((eventArgs.progress * 1000)) / 10 + "%";
    sender.findName("uxProgressBar").ScaleY = eventArgs.progress * 356;
}

Both the Splash.xaml and associated JS are from the Silverlight Beta 1 Quick Start for Displaying a Splash Screen While Loading a Silverlight-Based Application. I will eventually replace the splash with my own Xaml but for now this one works OK for testing. :)

 

**EDIT: The XAP file and the XAML file used to render the splash screen must reside in the same directory, be that the ClientBin or the root.

 ** BETA 2 EDIT: Microsoft has added the SplashScreenSource property and OnPluginSourceDownloadProgressChanged event to their Silverlight control. Kind of makes this one obsolete. I will post more about it soon.

Tags:

ASP.NET Development | Game Development | General | Silverlight Games

Silverlight 2.0

by Cameron Albert 5. March 2008 14:33
Silverlight 2.0 is available for download on silverlight.net!

Tags:

ASP.NET Development | Game Development | General

ToXml Extension Method in .NET 3.5

by Cameron Albert 27. February 2008 22:07

I built the following extension method in .NET 35. to XML serialize objects; thought it might be useful for someone else. I use to serialize collections to pass XML data to my stored procedures that save player skills and items in my PBBG engine.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace WebTools
{
public static class XmlHelper
    {
public static string ToXml(this object obj)
{
XmlSerializer serializer = new XmlSerializer(obj.GetType());
using (StringWriter sw = new StringWriter())
{
serializer.Serialize(sw, obj);
return sw.ToString();
}
}
}
}

I wrote another extension method specific to retrieving the modified values of a collection. You have to control when the IsDirty flag is set yourself but my properties take care of that.

public interface IModifiable
    {
bool IsDirty { get; }
void MarkClean();
void MarkDirty();
}

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WebTools
{
public static class ModifierHelpercs
    {
public static List<TSource> Modified<TSource>(this IEnumerable<TSource> source) where TSource : IModifiable
        {
return (from s in source where s.IsDirty select s).ToList();
}
}
}

Tags: , , , ,

ASP.NET Development | Game Development | General

SQL 2005 XML Data Type, Stored Procedures and Lists

by Cameron Albert 14. November 2007 17:41

I've seen a lot of stuff out there regarding the SQL 2005 XML data type but most of it is just regurgitates the MSDN documentation. That's fine and all but what about practical uses of it? Well, I have a practical use sample. In building my persistent browser based game Perenthia I have a concept of a Place. A place is a virtual space in which objects are stored. For Perenthia the places represent the various rooms or tiles players move around on. The place or room has exits defined that allow the player to move from one place to the next. The exits are the typical directions; north, south, up, down, etc. In the database I have a Places table and a PlaceExits table. The Places table stores all the information regarding a place and the PlaceExits table stores the placeId along with a directionId and destinationId so I know what exits are available in any room and what rooms they lead to.

The simplified schema for the places would be:

 Places Tables

 In the stored procedure that retrieves the place information I use the following query snippet in the select clause:

    SELECT

        p.*,

    (
            SELECT
                e.DirectionId        AS "@directionId",
                e.DestinationId        AS "@destinationId"
            FROM
                dbo.PlaceExits e
            WHERE
                e.PlaceId = p.PlaceId
            FOR XML PATH('exit'), ROOT('exits')
        ) AS ExitsXml

    FROM dbo.Places p 

 This creates an XML fragment I can then parse in the application to fill a collection of Exits on the Place object.

When saving place information I pass XML generated from the Exits collection in a stored procedure like so:

CREATE PROCEDURE dbo.Places_SavePlace  (@PlaceId int, @ExitsXml xml)

From within the save procedure I perform an update or insert of the place data and then execute the following sql to insert and update the exits for the current place:

    -- Exits

    -- Process the existing exits first

    UPDATE
        dbo.PlaceExits
    SET
        DestinationId    = e.ex.value('(@destinationId)[1]', 'int')
    FROM
        @ExitsXml.nodes('/exits/exit') as e(ex)
    WHERE
        PlaceId = @PlaceId
        AND DirectionId = e.ex.value('(@directionId)[1]', 'tinyint')

    -- Process any new exits

    INSERT INTO dbo.PlaceExits
    (
        ObjectId, DirectionId, DestinationId
    )
    SELECT
        @PlaceId,
        e.ex.value('(@directionId)[1]', 'tinyint'),
        e.ex.value('(@destinationId)[1]', 'int')
    FROM
        @ExitsXml.nodes('/exits/exit') as e(ex)
    WHERE
        e.ex.value('(@directionId)[1]', 'tinyint') NOT IN
        (
            SELECT DirectionId FROM dbo.PlaceExits WHERE PlaceId = @PlaceId
        )

This is working pretty well and keeps me from having to loop through the exits in the application and make multiple database calls. 

PBBG Engine Core Object Structure

by Cameron Albert 6. November 2007 13:20

The core of my PBBG Engine consists of a Place and an Object. A Place is a spacial definition and can represent a room, world, universe, etc. An Object is anything that can reside within a place, meaning all people, monsters and items are Objects. The Places and Objects are defined using meta data so that properties of an Object are the meta data of the object. This meta data is stored in a seperate table from the object and retrieved as XML when an object is queried. Places work in much the same way with a few pre-defined fields such as the name and x, y, z location of the place. Going this route will allow me to have different objects defined in different games without having null database fields that are not used in all games.

Another example would be a Sword. In Perenthia a Sword dervies from Weapon which dervies from Thing which implements the Object interface required by the PBBG Engine in order to persist the properties of the Sword in the database. I can then create instances of the Sword class when Swords are crafted or purchased. Each level of inheritence can implement properties that will persist for each Sword instance created.

Sword : Weapon : Thing : IObject 

So far it seems to be working pretty well and with the XML features of SQL 2005 the querying of the data is very fast. I am hoping to get Perenthia ported over to this new framework soon as it will provide a more flexible system and allow me to manage game content a little easier. 

Powered by BlogEngine.NET 1.5.0.7
Modified Theme by Mads Kristensen

About the Author

CameronAlbert.com I am Senior Software Development Consultant specializing in Silverlight, WPF and the Microsoft .NET Framework. 

I have released an iPhone game called the Adventures of Puppyman that was built using ExEn and am currently working on a WP7 and iPhone version of Perenthia soon to be released.

View Cameron Albert's profile on LinkedIn
See how we're connected

Follow cameronalbert on Twitter

 

Recommended Books

Silverlight 4 Business Application Development - Beginner's Guide:

http://www.packtpub.com/microsoft-silverlight-4-business-application-development-beginners-guide/book

Microsoft Silverlight 4 Business Application Development: Beginner’s Guide