Sunday, December 27, 2009

WPF

csharpwithdj.blogspot.com

Introduction

WPF offers a new way to build Windows applications that offer the user a richer experience. WPF applications offer the user advanced graphical features, animation, multimedia components, a better look and integration with documents, among other features.

Developers of WPF applications can now declaratively define the appearance of an application with Extensible Application Markup Language (XAML), and can implement the mechanics of an application in a code-behind file, separating presentation from logic in a way similar to what is done in ASP.NET. This approach also makes applications easier to divide up: designers can work with the visual half of the application using tools such as Microsoft Expression, and developers can work with the programming half using Visual Studio. The two halves can then be easily combined to form a visually pleasing product.

Moreover, while WPF provides a way to create traditional Windows applications, many users may prefer a browser-based experience. Because of this, WPF also offers a way to create browser-hosted applications, where the application will open in the user's browser rather than a traditional desktop window. Furthermore, Silverlight is compatible with WPF, and so knowledge of WPF in building Windows applications will carry over to building Silverlight applications.

Creating a WPF Project

• Open up Visual Studio 2008 and open the New Project dialog.

• There are two main WPF project templates:

o WPF Application

o WPF Browser Application.

The WPF Application template is used for standard desktop applications, and it's what we'll be using here. C# project named WpfToDo.

The WPF Browser Application is used for browser-hosted applications. Creating a browser application isn't much different from creating a desktop application.

Creating WPF Application

When the project is created and loaded, you should immediately see a split-view in the workspace. On the top will be a visual representation of the application's default window, and on the bottom will be the corresponding XAML. The visual part will probably be bigger, but you may want to click the Swap Panes button in order to enlarge the XAML view.

For now, though, leave the workspace area alone and take a look at the Solution Explorer. You should see two files, App.xaml and Window1.xaml. As the extensions give away, these contain the application's XAML. If you expand the two files, you should see something like this:



Under the XAML files are the code-behind files, whose names are created simply by appending “.cs” to the XAML file names. The -.xaml files of WPF are analogous to the -.aspx files of ASP.NET, and the code-behind files simply tack on the appropriate language extension.

The App files aren't very important to us right now. They deal with the application as a whole. For example, if we had a resource that we needed to share across the entire application, then we'd probably deal with that in the App files. But we don't. The only thing worth mentioning right now is one attribute in App.xaml:

<="" p="" x:class="WpfToDo.App">

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

StartupUri="Window1.xaml">

The attribute in bold specifies what the starting window is. In this case, it's Window1.xaml. With that, turn your attention to the Window1 files. Window1 is going to be the main window for our application.

Creating the basic UI with XAML

WPF applications are, just like Windows Form applications and ASP.NET applications, event-driven. So, before we start working with code, we need to create the basic user interface of our application using XAML. Then, we can work on responding to events raised through user interaction with the interface. The initial contents of Window1.xaml should look like this:

<="" p="" x:class="WpfToDo.Window1">

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="Window1" Height="300" Width="300">

Right now, there's just a Window tag which contains an empty Grid tag. Let's take a look at the Window tag first. In the first line, we define the Class attribute. This attribute points to the class associated with the window as defined in XAML. The next two lines aren't important—leave them be. Then, there are the Title, Height and Width attributes. These attributes specify the title, initial height and initial width of the window. Go ahead and change the title to something more appropriate, such as “To-Do List.”

Speaking of height and width, there is one important thing to be mentioned here. WPF doesn't use standard pixels. Instead, it uses “device independent pixels.” The goal with the device independent pixel is to provide consistent sizing across different devices and video settings. So 96 device independent pixels make up 1 inch. This makes since, since 96 dots per inch is common.

Inside the Window is a Grid. WPF offers numerous ways to control the layout of a window. The default method is through a Grid, which, as its name implies, arranges controls in a grid, with rows and columns whose sizes are specified by the developer. Another layout control is the StackPanel, which simply arranges controls from left to right or from top to bottom. There are, of course, a number of other layout controls, but we won't need them for our application. We'll be using a Grid for the main window, since it fits the desired layout.

The layout of the application doesn't have to be very complicated. A long, rectangular window will do. Inside the window will be a list of tasks, and below the list of tasks will be two buttons. One button will be for adding a new task to the list, and the other button will be for deleting a task from the list. Let's start by modifying the window's dimensions. Set the height of the window to 480 (5 inches) and the width of the window to 384 (4 inches).

Creating the Grid

In order to lay out the controls as described above, we need two columns (since there are two buttons below the task list, each of which will need its own column) and two rows (since, vertically, there's one list and one group of buttons). The columns need to be of equal width, since the buttons will need to be of equal width, but the rows will need to be of very different heights, since the buttons will naturally occupy less vertical space than the list. The list will span across two columns.

Setting the height of each row and the width of each column (and, thus, the size of each individual cell) isn't very difficult. It involves simply adding some tags within the Grid tag:

The above XAML is pretty straightforward. First, we define the columns of the grid, and then we define the rows, exactly as described earlier, with two columns of equal width and two rows of unequal width. Grid columns are defined using the ColumnDefinition tag, and column definitions are all wrapped inside of a Grid.ColumnDefinitions tag. Rows are defined the exact same way, except with RowDefinition and Grid.RowDefinitions.

Notice that the dimensions of the grid are considerably less than the dimensions of the window. Some difference is to be expected, since the border and title bar of the window will take up some space, but I've subtracted even more space from the grid in order to create some empty space between the controls and the border of the window. If we add controls to the grid as-is, though, the empty space will only be to the right and to the bottom of the grid. That is, everything will be crammed to the top left. To fix this, we need to center the the columns and rows within the grid. This isn't very hard. Only the Grid tag needs to be modified:

Above, we add a HorizontalAlignment attribute along with a VerticalAlignment tag. Now, all of the controls will be centered in the window, with a margin between the controls and the edge of the window. Note, though, that there is also a Margin attribute for controls, which we'll take a look at shortly.

Using XAML, we've declaratively created a layout for our application's controls, and, so far, the XAML has been rather uncomplicated and uncluttered, just as it should be. In the next article, we'll start by placing the controls inside the cells of the Grid.

















WCF Tutorial - Basic Interprocess Communication

csharpwithdj.blogspot.com

WCF Tutorial - Basic Interprocess Communication

Related Posts

• WCF Tutorial - Events and Callbacks

• WCF Tip: Using Properties in Service Contracts

• WCF Callbacks Hanging WPF Applications

• .NET 3.5 Adds Named Pipes Support

What the heck is WCF?

At its core, WCF is a mechanism that facilitates communication between processes - either on the same machine or separate machines. Part of the confusion I had when trying to understand WCF was the almost limitless ways WCF can accomplish this. We're not going to worry about all of that though. We're going to strip out everything except the bare essentials required to get a client/server system up and running - and it's actually very little code.

What we're building today will look a lot like a simple remote procedure call. We'll be building a server that exposes a function that can be called by a client. Just to show how easy it is to change the transport layer, the client will connect to the server through two different mechanisms - http and named pipe.

The first thing we need to do is define what the client will have access to. We do this by defining an interface in C# and giving it a few attributes for WCF. The server will create a class that implements the interface to actually do the work. The client will just be provided the interface so it knows which functions are available.

using System;

using System.ServiceModel;

[ServiceContract]

public interface IStringReverser

{

[OperationContract]

string ReverseString(string value);

}

Here's my simple interface. It provides a function that takes a string and returns a new string with all of the characters reversed. The ServiceContract attribute tells WCF that this interface can be exposed for client use. The OperationContract attributes tells WCF that ReverseString is part of this service contract and can be used by clients. There are lots of optional settings that can be applied to both of these attributes, but this is all that's required to get things up and running. The System.ServiceModel namespace is available by adding a reference to the System.ServiceModel assembly to your project, which is available in the default installation of .NET 3.5.

Starting with the server, the first thing we need to do is create a class that implements this interface and provides the functionality behind ReverseString.

using System;

using System.ServiceModel;

[ServiceContract]

public interface IStringReverser

{

[OperationContract]

string ReverseString(string value);

}


public class StringReverser : IStringReverser

{

public string ReverseString(string value)

{

char[] retVal = value.ToCharArray();

int idx = 0;

for (int i = value.Length - 1; i >= 0; i--)

retVal[idx++] = value[i];

return new string(retVal);

}

}

Pretty simple, right? There might be more elegant ways to reverse a string, but we're not here to criticize the implementation of this function. It's actually not required to use the interface method for defining service contracts (i.e. you could stick the attributes directly on the class), but it's the recommended way, and it makes client applications much easier to implement if they can simply share the same interfaces.

Surprisingly, there's actually very little code required to make the server fully functional. We'll begin by creating a ServiceHost, which is responsible for most of the work behind exposing the service to clients.

class Program

{

static void Main(string[] args)

{

using (ServiceHost host = new ServiceHost(

typeof(StringReverser),

new Uri[]{

new Uri("http://localhost:8000"),

new Uri("net.pipe://localhost")

}))

{


}

}

}

The most difficult thing here is the array of Uri objects. These are our base addresses that clients can use to connect to this WCF server. Like I said before, we're exposing two ways to connect to this service: http and named pipe. How the address is formatted depends on the type of Binding it represents.

Now that we've got our ServiceHost created, we need to configure some endpoints. These will actually enable the http and named pipe bindings and give them the address required by the client.

class Program

{

static void Main(string[] args)

{

using (ServiceHost host = new ServiceHost(

typeof(StringReverser),

new Uri[]{

new Uri("http://localhost:8000"),

new Uri("net.pipe://localhost")

}))

{

host.AddServiceEndpoint(typeof(IStringReverser),

new BasicHttpBinding(),

"Reverse");

host.AddServiceEndpoint(typeof(IStringReverser),

new NetNamedPipeBinding(),

"PipeReverse");

host.Open();

Console.WriteLine("Service is available. " +

"Press to exit.");

Console.ReadLine();

host.Close();

}

}

}

Here we're adding two endpoints - one for http and one for named pipe. The address that's passed in is what appears after the base address specified in the ServiceHost constructor (e.g. for http it would be: "http://localhost:8000/Reverse"). We have to specify a base address for each endpoint we're configuring. So if the net.pipe base address was not present in the ServiceHost constructor, the server would throw an exception when it attempted to create the named pipe endpoint. After the endpoints are configured, we simply call Open on the ServiceHost to enable it.

Believe or not, that's it for a fully functional WCF server. Below is all of the code put together.

using System;

using System.ServiceModel;

namespace WCFServer

{

[ServiceContract]

public interface IStringReverser

{

[OperationContract]

string ReverseString(string value);

}

public class StringReverser : IStringReverser

{

public string ReverseString(string value)

{

char[] retVal = value.ToCharArray();

int idx = 0;

for (int i = value.Length - 1; i >= 0; i--)

retVal[idx++] = value[i];

return new string(retVal);

}

}

class Program

{

static void Main(string[] args)

{

using (ServiceHost host = new ServiceHost(

typeof(StringReverser),

new Uri[]{

new Uri("http://localhost:8000"),

new Uri("net.pipe://localhost")

}))

{

host.AddServiceEndpoint(typeof(IStringReverser),

new BasicHttpBinding(),

"Reverse");

host.AddServiceEndpoint(typeof(IStringReverser),

new NetNamedPipeBinding(),

"PipeReverse");

host.Open();

Console.WriteLine("Service is available. " +

"Press to exit.");

Console.ReadLine();

host.Close();

}

}

}

}

Now we can move on to the client. The first thing we'll need in the client code is the same interface, with the same attributes, that we defined in the server. If this were being used in a production environment, these interfaces would probably be created in a dedicated library that could be easily distributed. For now, I just copied and pasted the code into another project.

We have to first establish a channel between the client and a server. A channel is basically a connection that allows the client and server to send messages to each other. Fortunately, WCF provides something called a ChannelFactory that makes creating these very simple.

using System;

using System.ServiceModel;

using System.ServiceModel.Channels;


namespace WCFClient

{

[ServiceContract]

public interface IStringReverser

{

[OperationContract]

string ReverseString(string value);

}


class Program

{

static void Main(string[] args)

{

ChannelFactory httpFactory =

new ChannelFactory(

new BasicHttpBinding(),

new EndpointAddress(

http://localhost:8000/Reverse));


ChannelFactory pipeFactory =

new ChannelFactory(

new NetNamedPipeBinding(),

new EndpointAddress(

"net.pipe://localhost/PipeReverse"));



IStringReverser httpProxy =

httpFactory.CreateChannel();



IStringReverser pipeProxy =

pipeFactory.CreateChannel();

}

}

}

We're building two proxies here - one for http and one for named pipe. The addresses passed into the ChannelFactory constructor are the same as those configured on the server. We also have to pass in the specific bindings we want: BasicHttpBinding and NetNamedPipeBinding. Lastly we call CreateChannel on each channel factory, which returns an IStringReverser interface. Now we can call functions on those interfaces and WCF makes a remote call through the channel to our server to get the result.

string str = Console.ReadLine();

Console.WriteLine("http: " + httpProxy.ReverseString(str));

Console.WriteLine("pipe: " + pipeProxy.ReverseString(str));

Incredibly, we're now done with the client. Here's the client program in its entirety:

using System;

using System.ServiceModel;

using System.ServiceModel.Channels;



namespace WCFClient

{

[ServiceContract]

public interface IStringReverser

{

[OperationContract]

string ReverseString(string value);

}



class Program

{

static void Main(string[] args)

{

ChannelFactory httpFactory =

new ChannelFactory(

new BasicHttpBinding(),

new EndpointAddress(

"http://localhost:8000/Reverse"));



ChannelFactory pipeFactory =

new ChannelFactory(

new NetNamedPipeBinding(),

new EndpointAddress(

"net.pipe://localhost/PipeReverse"));



IStringReverser httpProxy =

httpFactory.CreateChannel();



IStringReverser pipeProxy =

pipeFactory.CreateChannel();



while (true)

{

string str = Console.ReadLine();

Console.WriteLine("http: " +

httpProxy.ReverseString(str));

Console.WriteLine("pipe: " +

pipeProxy.ReverseString(str));

}

}

}

}

It's amazing when you think about the amount of stuff happening behind the scenes that developers no longer have to worry about. Basically, all developers have to do is define interfaces and objects and WCF takes care of the rest. The server side and client side code can be drastically reduced by using configuration files to replace the setup we did in code, but for the purpose of understanding, explicitly setting it is a good way to know what's behind configuration settings.

I think that about wraps it up for this introduction to WCF. There's an immense amount of configuration and customization supported by WCF, and volumes could be dedicated to it. Hopefully we'll slowly unravel WCF's complexity and introduce new concepts in following tutorials. You