下面我们来接着做客户端:
1.在Solution中添加SiliverLight Project,命名为ChatClient
这里,我将对应的网站 放在WCF Service中

2.先在ChatClient工程中 添加DLL引用:
System.Runtime.Serialization.dll
System.ServiceModel.dll
System.ServiceModel.PollingDuplex.dll

3.先绘制一个简单界面,修改Page.xaml 如下:
<UserControl x:Class="ChatClient.Page"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width
="655" Height="420">
    
<Grid x:Name="LayoutRoot"
          Background
="White"
          ShowGridLines
="True">
        
<Grid.RowDefinitions>
            
<RowDefinition />
            
<RowDefinition Height="40" />
        
</Grid.RowDefinitions>

        
<Grid ShowGridLines="True"
              Grid.Row
="0">
            
<Grid.ColumnDefinitions>
                
<ColumnDefinition />
                
<ColumnDefinition Width="150" />
            
</Grid.ColumnDefinitions>

            
<!--聊天内容-->
            
<!--TODO:加入滚动条-->
            
<TextBlock x:Name="txt_CharText"
                       Grid.Column
="0"
                       Grid.Row
="0"
                       VerticalAlignment
="Center"
                       HorizontalAlignment
="Center" />
            
            
<!--用户列表-->
            
<!--TODO:加入滚动条 ListView-->
            
<TextBlock x:Name="txt_lstChatters"
                       Grid.Column
="1"
                       VerticalAlignment
="Top"
                       HorizontalAlignment
="Center" />
        
</Grid>

        
<Grid Grid.Row="1"
              ShowGridLines
="True">
            
<Grid.ColumnDefinitions>
                
<ColumnDefinition />
                
<ColumnDefinition Width="75" />
                
<ColumnDefinition Width="150" />
                
<ColumnDefinition Width="75" />
            
</Grid.ColumnDefinitions>

            
<!--输入对话框-->
            
<TextBox x:Name="tb_Message"
                     Grid.Column
="0"
                     Margin
="5,5,5,5"
                     KeyDown
="tb_Message_KeyDown"/>

            
<!--发送按钮-->
            
<Button x:Name="btn_Say"
                    Grid.Column
="1"
                    Width
="70"
                    Height
="30"
                    HorizontalAlignment
="Center"
                    VerticalAlignment
="Center"
                    Click
="btn_Say_Click">
                
<TextBlock HorizontalAlignment="Center"
                           VerticalAlignment
="Center">Send</TextBlock></Button>
            
            
<!--名字输入对话框-->
            
<TextBox x:Name="tb_Name"
                     Grid.Column
="2"
                     Margin
="5,5,5,5"
                     Text
="Guest"
                     KeyDown
="tb_Name_KeyDown"/>
            

            
<!--Connect按钮-->
            
<Button x:Name="btn_Connect"
                    Grid.Column
="3"
                    Width
="70"
                    Height
="30"
                    HorizontalAlignment
="Center"
                    VerticalAlignment
="Center"
                    Click
="btn_Connect_Click">
                
<TextBlock HorizontalAlignment="Center"
                           VerticalAlignment
="Center">Connect</TextBlock>
            
</Button>

        
</Grid>
    
</Grid>
</UserControl>
为了方便用户输入,在两个TextBox中 加入了KeyDown事件 用来截获"回车"符

4.在相应的 Page.xaml.cs中 添加如下引用:
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading;

5.添加如下代码,创建Channel
        SynchronizationContext uiThread;
        IDuplexSessionChannel m_Channel;

        
public void InitChannel()
        
{
            
// Grab a reference to the UI thread.
            uiThread = SynchronizationContext.Current;

            uiThread.Post(WriteText, 
"Connecting" + Environment.NewLine);

            
// Instantiate the binding and set the time-outs.
            PollingDuplexHttpBinding binding = new PollingDuplexHttpBinding()
            
{
                PollTimeout 
= TimeSpan.FromSeconds(10),
                InactivityTimeout 
= TimeSpan.FromMinutes(1)
            }
;

            
// Instantiate and open channel factory from binding.
            IChannelFactory<IDuplexSessionChannel> factory =
                binding.BuildChannelFactory
<IDuplexSessionChannel>(new BindingParameterCollection());

            IAsyncResult factoryOpenResult 
=
                factory.BeginOpen(
new AsyncCallback(OnOpenCompleteFactory), factory);
            
if (factoryOpenResult.CompletedSynchronously)
            
{
                CompleteOpenFactory(factoryOpenResult);
            }

        }
这里利用
SynchronizationContext.Current.Post()
异步向页面输送信息

6.加如相应的完成事件
void OnOpenCompleteFactory(IAsyncResult result)
        
{
            
if (result.CompletedSynchronously)
                
return;
            
else
                CompleteOpenFactory(result);

        }


        
void CompleteOpenFactory(IAsyncResult result)
        
{
            IChannelFactory
<IDuplexSessionChannel> factory =
                (IChannelFactory
<IDuplexSessionChannel>)result.AsyncState;

            factory.EndOpen(result);

            
// The factory is now open. Create and open a channel from the channel factory.
            IDuplexSessionChannel channel =
                factory.CreateChannel(
new EndpointAddress("http://192.168.0.13:18600/ChatService.svc"));

            IAsyncResult channelOpenResult 
=
                channel.BeginOpen(
new AsyncCallback(OnOpenCompleteChannel), channel);
            
if (channelOpenResult.CompletedSynchronously)
            
{
                CompleteOpenChannel(channelOpenResult);
            }

        }


        
void OnOpenCompleteChannel(IAsyncResult result)
        
{
            
if (result.CompletedSynchronously)
                
return;
            
else
                CompleteOpenChannel(result);
        }


        
void CompleteOpenChannel(IAsyncResult result)
        
{
            IDuplexSessionChannel channel 
= (IDuplexSessionChannel)result.AsyncState;

            channel.EndOpen(result);

            
this.m_Channel = channel;

            
// The channel is now open. Send a message.
            Message message = Message.CreateMessage(channel.GetProperty<MessageVersion>(), "Silverlight/IChat/JoinChat"this.tb_Name.Text.ToString());
            IAsyncResult resultChannel 
= channel.BeginSend(message, new AsyncCallback(OnSend), channel);
            
if (resultChannel.CompletedSynchronously)
            
{
                CompleteOnSend(resultChannel);
            }


            
// Also start the receive loop to listen for callbacks from the service.
            ReceiveLoop(channel);
        }


注意:
(1)这里的
IDuplexSessionChannel channel =
                factory.CreateChannel(
new EndpointAddress("http://192.168.0.13:18600/ChatService.svc"));
这个地址应该添入,WCF Service的地址,不要忘记端口号

(2)利用
IDuplexSessionChannel.BeginSend()方法 与服务器通信

7.完成与服务器通信的相应调用
 void OnSend(IAsyncResult result)
        
{
            
if (result.CompletedSynchronously)
                
return;
            
else
                CompleteOnSend(result);
        }



        
void CompleteOnSend(IAsyncResult result)
        
{
            IDuplexSessionChannel channel 
= (IDuplexSessionChannel)result.AsyncState;

            channel.EndSend(result); 
        }


        
void ReceiveLoop(IDuplexSessionChannel channel)
        
{
            
// Start listening for callbacks.
            IAsyncResult result = channel.BeginReceive(new AsyncCallback(OnReceiveComplete), channel);
            
if (result.CompletedSynchronously)
                CompleteReceive(result);
        }


        
void OnReceiveComplete(IAsyncResult result)
        
{
            
if (result.CompletedSynchronously)
                
return;
            
else
                CompleteReceive(result);
        }


        
void CompleteReceive(IAsyncResult result)
        
{
            
// A callback was received.
            IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;

            
try
            
{
                Message receivedMessage 
= channel.EndReceive(result);

                
if (receivedMessage == null)
                
{
                    
// Server closed its output session, can close client channel, 
                    
// or continue sending messages to start a new session.
                }

                
else
                
{
                    
// Show the service response in the UI.
                    string text = receivedMessage.GetBody<string>();
                    uiThread.Post(WriteText, text 
+ Environment.NewLine);
                    ReceiveLoop(channel);
                }

            }

            
catch (CommunicationObjectFaultedException)
            
{
                
// The channel inactivity time-out was reached.
            }


        }


        
private void SendMessage(string text)
        
{
            
// The channel is now open. Send a message
            Message message = Message.CreateMessage(this.m_Channel.GetProperty<MessageVersion>(), "Silverlight/IChat/SayChat", text);
            IAsyncResult resultChannel 
= this.m_Channel.BeginSend(message, new AsyncCallback(OnSend), this.m_Channel);
            
if (resultChannel.CompletedSynchronously)
            
{
                CompleteOnSend(resultChannel);
            }


            
// Also start the receive loop to listen for callbacks from the service.
            ReceiveLoop(this.m_Channel);
        }

8.加入前台逻辑处理
        private void btn_Say_Click(object sender, RoutedEventArgs e)
        
{
            SendMessage(
this.tb_Message.Text.ToString());
            
this.tb_Message.Text = "";
        }


        
private void btn_Connect_Click(object sender, RoutedEventArgs e)
        
{
            InitChannel();
        }


        
private void tb_Message_KeyDown(object sender, KeyEventArgs e)
        
{
            
if (e.Key == Key.Enter)
            
{
                SendMessage(
this.tb_Message.Text.ToString());
                
this.tb_Message.Text = "";
            }

        }


        
private void tb_Name_KeyDown(object sender, KeyEventArgs e)
        
{
            
if (e.Key == Key.Enter)
            
{
                InitChannel();
            }

        }


        
void WriteText(object text)
        
{
            
this.txt_CharText.Text += (string)text;
        }

***********************************华丽的分割线**************************************

至此 一个支持回调的SiliverLight 2.0 聊天室 就完成了,从中间可以看出,SiliverLight对WCF的支持还是不够好,
它并不能连接TCP的WCF Service Host 这样,它所支持的所谓CallBack实际上 是SiliverLight内部自己的轮询,
在本质上,似乎还是没有什么重大的突破,只是做的更有效率一点而已,但是SiliverLight所支持的多线程操作,
使其有更好的可发展空间,这一点,也是让大家十分高兴的~毕竟不会象原来那样,因为种种原因,整个浏览器崩掉~

*****************************

应要求 现在补上我的Sulotion  :   Download
 posted on 2008-07-14 15:05  monkey-猴子  阅读(2017)  评论(10编辑  收藏  举报