Monday, May 28, 2007

WPF binding to WCF and more

Windows foundations are great. But how to make them work together. Today, I decided to explain how to build simple WPF application, with data, achieved from WCF OperationContracts (Methods) with simple and complicated parameters. So, let's start.

First of all, we'll create simple WPF application, then we'll add WCF Service Library to our solution. It'll create nice template with explanations what to put where. So, we'll use it AS-IS and use our WPF application as host for WCF service (this can run in different application as well). So, do everything written in the template. Create App.config and write where

<system.serviceModel>
<
services>
<
service name="FlickrHost.service1">
<
endpoint contract="FlickrHost.IService1" binding="wsHttpBinding" />
</
service>
</
services>
</
system.serviceModel>

 


Additional file with host start and stop and methods into Window1.xaml.cs


public Window1()
{
InitializeComponent();
MyServiceHost.StartService();

}


~Window1()
{
MyServiceHost.StopService();
}

 


So far, so good. Compile, run - ERROR, AddressAccessDeniedException: "HTTP could not register URL http://+". What the hell? We can not create entrypoint as user because of UAC in Vista. So, let's see what we can do.


Open command prompt and type "netsh http show urlacl". You'll get something like this



Reserved URL            : http://+:80/wsman/
    User: NT AUTHORITY\NETWORK SERVICE
        Listen: Yes
        Delegate: Yes
        SDDL: D:(A;;GA;;;NS)

Reserved URL            : http://+:80/Temporary_Listen_Addresses/
    User: \Everyone
        Listen: Yes
        Delegate: No
        SDDL: D:(A;;GX;;;WD)


What is it? Why we can not use port 8080, provided by template. Let's add it into our account.
netsh http add urlacl url=http://+:8080/ user=DOMAIN\username. You'll get error:



Url reservation add failed, Error: 5
The requested operation requires elevation.


Run the command shell "As Administrator" and you'll success. So, UAC good or not for jews? You choose :)


Next step is creation of  ObjectDataProvider, that uses the service methods to provide results. We'll add additional method to default WCF service template, that receives no parameters. Something like this will definitely fine.


[ServiceContract()]
public interface IService1
{
[
OperationContract]
string MyOperation2(string myValue);
[
OperationContract]
string MyOperation3(DataContract1 dataContractValue);
[
OperationContract]
string MyOperation1();
}

public class service1 : IService1
{
public string MyOperation2(string myValue)
{
return "Hello: " + myValue;
}
public string MyOperation3(DataContract1 dataContractValue)
{
return "Hello: " + dataContractValue.FirstName;
}
public string MyOperation1()
{
return "Hello World!";
}
}

 


Now, back to WPF. I believe you know, that you can bind to methods?


<ObjectDataProvider ObjectType="c:service1" x:Key="operation1" MethodName="MyOperation1"/>

 


Adding TextBlock, binded to this object and wow, we have Hello World application.


<TextBlock Text="{Binding Source={StaticResource operation1}}"/>

 


So far so good. Now, we have two additional methods, that need parameters. One is regular string, but other is DataContract from WFC service. How to do it? How to understand how to pass parameters into methods. For WPF - this is really simple. For us - not so straight forward - there is no intellisense support in XAML schema for such features in VS2005 with .NET 3.0 toolkit. What we have to do, is to use ObjectDataProvider.MethodParameters variable to set parameters for the method, bided into ObjectDataProvider. Let's rock'n'roll.


<ObjectDataProvider ObjectType="c:service1" x:Key="operation2" MethodName="MyOperation2">
<
ObjectDataProvider.MethodParameters>
<
s:String>aaa</s:String>
</
ObjectDataProvider.MethodParameters>
</
ObjectDataProvider>

 


Well. We have System mscorlib clr namespace, but what to do with object (Data Contact), defined in WCF service? For real? The same stuff. The only difference is other namespace and internal object initialization and assignment. Here comes the king.


<ObjectDataProvider ObjectType="c:service1" x:Key="operation3" MethodName="MyOperation3">
<
ObjectDataProvider.MethodParameters>
<
c:DataContract1>
<
c:DataContract1.FirstName>
<
s:String>bbb</s:String>
</
c:DataContract1.FirstName>
</
c:DataContract1>
</
ObjectDataProvider.MethodParameters>
</
ObjectDataProvider>

 


So far, we have three methods, binded to objects, achieved from WCF service provider, run by WCF data host. But wait. The data is not read only. We can change it. What's the hell, we can not notify to the object data provider about changes, there are no INotifyUpdate interface implemented as well as nothing can call us back about the change. What to do?


The simpler answer - DIY. The complicated make your client application notify about. So, I'll add TextBox and Button. Once the button clicked, I'll read information from the textbox, update my WCF service and notify about the changes.


void onClick(object sender, RoutedEventArgs e)
{
ObjectDataProvider odp2 = Resources["operation2"] as ObjectDataProvider;
odp2.MethodParameters[0] = name.Text;
odp2.Refresh();

ObjectDataProvider odp3 = Resources["operation3"] as ObjectDataProvider;
((FlickrHost.
DataContract1)odp3.MethodParameters[0]).FirstName = name.Text;
odp3.Refresh();
}

 


Well. We did it. Now let's write real world application. Read the attached source name and you'll may understand, that I'm going to write service, that reads, parses and uses beautiful images, found here. But, not today :)


Source code for this article

No comments: