Windows Communication Foundation入门(Part Three)

《Windows Communication Foundation之一起》系列的三

当葡萄城ActiveReports报表被,可以装报表中不同控件的体裁,然后把这些体保存至一个表面的XML文件中,供其他报表使用。如果用户要同客报表以不用之外观分发,只待简单地改样式表单,无需逐个改变每个报表中之单个控件的字、颜色、尺寸等。

示范代码下载:DuplexSample.rar

本文中示范的是吗年销量统计表设置不同的皮样式,我们供准备了三独皮肤样式,以下是事无巨细操作步骤:

季、Service Contract编程模型
以Part Two中,我坐“Hello
World”为条例讲解了哪定义一个Service。其主导就是啊接口或看似施加ServiceContractAttribute,为计施加OperationContractAttribute。在Service的点子吃,可以接受多只参数,也得以来返路,只要这些数据类型能够被序列化。这样平等种植方式一般为称呼本地对象,远程过程调用(local-object,
Remoting-Procedure-Call)方式,它好有益开发人员快速地展开Service的付出。

1、创建报表文件

以应用程序中开创一个叫作也
rptTheme1.rdlx
底葡城ActiveReports报表文件,使用的类模板也葡萄城ActiveReports报表的页面报表,创建好后于
VS
的表菜单项中选择变为连续页面布局(CPL)报表,将固定页面报表转换为总是页面报表。

在Service Contract编程模型中,还有平等栽方式是依据Message
Contract的。服务的艺术极其多只能有一个参数,以及一个赶回值,且其的数据类型是通过Message
Contract自定义之信息类型。在自定义消息遭到,可以吧信定义详细的Header和Body,使得对信息之置换更加灵敏,也重新有利对信息的支配。

2、打开报表资源管理器,并循以下信息创建报表数据源

名称: NWind_CHS
类型: Micorsoft OleDb Provider
OLE DB 提供程序: Microsoft.Jet.OLEDB.4.0
服务器或文件名称: Data\NWind_CHS.mdb

 

一个幽默的话题是当我们定义一个Service时,如果一个private方法被施加了OperationContractAttribute,那么对客户端而言,这个法子是足以被调用的。这不啻同private对于目标封装的义有抵触。但是这么的确定是有那个现实意义的,因为对于一个劳动而言,服务端和客户端的需求数会无平等。在服务端,该服务目标就允许吃远程调用,但本地调用却可能会见盖情要各异。如下面的劳动概念:
[ServiceContract]
public class BookTicket
{
 [OperationContract]
 public bool Check(Ticket ticket)
 {
  bool flag;
  //logic to check whether the ticket is none;
  return flag;
 }
 [OperationContract]
 private bool Book(Ticket ticket)
 {
  //logic to book the ticket
 }
}
以劳动类BookTicket中,方法Check和Book都是服务章程,但后者为定义成private方法。为什么也?因为对此客户而言,首先会见检讨是不是还有电影票,然而更预定该电影票。也就是说这点儿件功能还是面向客户之服务,会被远程调用。对于Check方法,除了远程客户会调用该法外,还发或给询问电影票、预定电影票、出售电影票等工作逻辑所调用。而Book方法,则单纯对中长途客户,只恐为远程调用。为了保该措施的安康,将那个设置为private,使得地方对象不至于调用它。

3、 添加数据集

于新建的
NWind_CHS
数据源上鼠标右键并选取丰富多少集菜单项,添加以下简单只数据集:

常规-名称:Sales

查询-查询:

SELECT

类别.类别名称,订单.货主地区,

SUM

(订单明细.数量)

AS

销售量

FROM

(( 订单

INNERJOIN

订单明细

ON

订单.订单ID =
订单明细.订单ID)

INNERJOIN

产品

ON

订单明细.产品ID
= 产品.产品ID)

INNERJOIN

类别

ON

产品.类别ID =
类别.类别ID

WHERE

DATEDIFF(“yyyy”,订单.订购日期,’2011-01-01′) = 0

GROUPBY

好像别.类别名称,订单.货主地区

ORDERBY

恍如别.类别名称,订单.货主地区

 

常规-名称:SaleDetails

查询-查询:

SELECT

DATEPART(“m”,订单.订购日期)

AS

立购月,类别.类别名称,

SUM

(订单明细.数量)

AS

销售量

FROM

(( 订单

INNERJOIN

订单明细

ON

订单.订单ID =
订单明细.订单ID)

INNERJOIN

产品

ON

订单明细.产品ID
= 产品.产品ID)

INNERJOIN

类别

ON

产品.类别ID =
类别.类别ID

WHERE

DATEDIFF(“yyyy”,订单.订购日期,’2011-01-01′) = 0

GROUPBY

DATEPART(“m”,订单.订购日期),类别.类别ID,
类别.类别名称

ORDERBY

DATEPART(“m”,订单.订购日期),类别.类别ID,
类别.类别名称

 

于是当WCF中,一个方是否当为装也劳动方法,以及当安装为public还是private,都亟待基于实际的工作逻辑来判断。如果涉嫌到村办的劳务方法比较多,一栽好之方法是采用设计模式的Façade模式,将这些措施结合起来。而这些艺术的实逻辑,可能会见分散到个别的本土对象被,对于这些地方对象,也足以叫与自然之顾限制,如下面的代码所示:
internal class BusinessObjA
{
 internal void FooA(){}
}
internal class BusinessObjB
{
 internal void FooB(){}
}
internal class BusinessObjC
{
 internal void FooC(){}
}
[ServiceContract]
internal class Façade
{
 private BusinessObjA objA = new BusinessObjA();
 private BusinessObjB objB = new BusinessObjB();
 private BusinessObjC objC = new BusinessObjC();
 [OperationContract]
 private void SvcA()
 {
  objA.FooA();
 }
 [OperationContract]
 private void SvcB()
 {
  objB.FooB();
 }
 [OperationContract]
 private void SvcC()
 {
  objC.FooC();
 }
}
艺术FooA,FooB,FooC作为internal方法,拒绝让先后集外的地方对象调用,但SvcA,SvcB和SvcC方法,却足以让远程对象所调用。我们还是可以BusinessObjA,BusinessObjB等相近定义也Façade类的镶嵌套类。采用这样的办法,有利于这些特别之劳动方式,被远程客户又方便的调用。

4、设计报表界面

4.1、在 GrapeCity
ActiveReports 主题编辑器中创造三个主题样式

Style1.rdlx-theme

图片 1

 

Style2.rdlx-theme

图片 2

 

Style3.rdlx-theme

图片 3

开创好上述主题样式之后,为表指定一个主题,我们以筹划时指定的主题样式也
Style1

图片 4

4.2、创建年度各地段销量统计表

于 Visual Studio
工具箱中将 Chart 控件添加到报表设计界面,按照以下列表设置 Chart
控件的性能

图形数据性对话框:

常规-数据集名称: Sales
系列值-值: =Sum([销售量])
类别分组: 名称:Chart1_CategoryGroup1
分组-表达式:=[类别名称]
标签:=[类别名称]
系列分组: 名称:Chart1_SeriesGroup1
分组-表达式:=[货主地区]
标签:=[货主地区]

 

图片外观属性对话框:

类型: 图表类型:折线图
子类型:平滑
标题:2011年度各地区销售量统计表
调色板:
=Theme.Colors.Accent1
=Theme.Colors.Accent2
=Theme.Colors.Accent3
=Theme.Colors.Accent4
=Theme.Colors.Accent5

 

4.3、创建各地段销量图

由 Visual Studio
工具箱中将 Chart 控件添加到报表设计界面,按照以下列表设置 Chart
控件的性能

图表数据性对话框:

图表 属性名称 属性值
东北地区销量图 常规: 数据集名称:Sales
  系列值: 值:=Sum([销售量])
  类别分组: 名称:Chart2_CategoryGroup1
表达式:=[类别名称]
标签:=[类别名称]
  过滤: 表达式:=[货主地区]
运算符:Equal
值:东北

 

图外观属性对话框:

类型: 图表类型:分高图表
调色板: =Theme.Colors.Accent1
=Theme.Colors.Accent2
=Theme.Colors.Accent3
=Theme.Colors.Accent4
=Theme.Colors.Accent5
=Theme.Colors.Accent6
=Theme.Colors.Hyperlink
=Theme.Colors.HyperlinkFollowed

 

4.4、重复4.3操作,创建华北、华东、华南、西南四个地段的销量图,图表区域在过滤条件的值不同。

说到底的计划布局使下图:

图片 5

概念一个Service,最广泛的抑显式地以接口定义为Service。这样的方让劳动之概念更加灵活,这一点,我都以Part
Two中生出了描述。当然,采用这种方法,就不在前所陈述之私房方法成为服务措施的花样了,因为以一个接口定义中,所有术还是public的。

5、在后台代码中动态加载用户指定的主题样式

GrapeCity.ActiveReports.PageReport report = new
GrapeCity.ActiveReports.PageReport(new
System.IO.FileInfo(Server.MapPath(“../Reports/” + reportname + “.rdlx”)));
report.Report.DataSources[0].DataSourceReference = “”;
report.Report.DataSources[0].ConnectionProperties.DataProvider =
“OLEDB”;
report.Report.DataSources[0].ConnectionProperties.ConnectString =
string.Format(“Provider=Microsoft.Jet.OLEDB.4.0;Data
Source={0};”, Server.MapPath(“../Data/NWind_CHS.mdb”));
report.Report.Themes.Clear();
report.Report.Themes.Add(Server.MapPath(string.Format(“../Theme/Style{0}.rdlx-theme”,
themeid))); WebViewer1.Report =
report;

 

此外一个话题是关于“服务接口的存续”。一个于标记了[ServiceContract]的接口,在该随后承链上,允许有多只同标记了[ServiceContract]的接口。对接口内定义之OperationContract方法,则是依据“聚合”的法,如下的代码所示:
[ServiceContract]
public interface IOne
{
    [OperationContract(IsOneWay=true)]
    void A();
}
[ServiceContract]
public interface ITwo
{
    [OperationContract]
    void B();
}
[ServiceContract]
public interface IOneTwo : IOne, ITwo
{
    [OperationContract]
    void C();
}

6、运行程序

图片 6

在这个事例中,接口IOneTwo继承了接口IOne和ITwo。此时劳动IOneTwo暴露的劳务措施应该吗方法A、B和C。

在线演示和源码下载地址:

http://www.gcpowertools.com.cn/products/activereports\_demo.htm
图片 7

 

相关阅读:

【报表福利大放送】100余套报表模板免费生载 

 矩表 –
现代多少解析中必不可少的表工具

 

唯独当我们应用Duplex消息交换模式(文章后会详细介绍Duplex)时,对于服务接口的回调接口在接口继承上起早晚之限。WCF要求服务接口IB在持续另一个劳动接口IA时,IB的回调接口IBCallBack必须以继续IACallBack,否则会丢掉来InvalidContractException异常。正确的概念如下所示:
[ServiceContract(CallbackContract = IACallback)]
interface IA {}
interface IACallback {}

[ServiceContract(CallbackContract = IBCallback)]
interface IB : IA {}
interface IBCallback : IACallback {}

五、消息交换模式(Message Exchange Patterns,MEPS)
每当WCF中,服务端与客户端里信息之置换共有三栽模式:Request/Reply,One-Way,Duplex。

1、Request/Reply
即时是默认的平等栽信息交换模式,客户端调用劳动方式发出请求(Request),服务端收到请求后,进行相应的操作,然后回来一个结果值(Reply)。

若是没其他特别之设置,一个办法而标记了OperationContract,则该措施的信息交换模式就是是应用的Request/Reply方式,即使它的返回值是void。当然,我们吧可拿IsOneWay设置为false,这吗是默认的安。如下的代码所示:
[ServiceContract]
public interface ICalculator
{
 [OperationContract]
 int Add(int a, int b);

 [OperationContract]
 int Subtract(int a, int b);
}

2、One-Way
使消息交换模式为One-Way,则表明客户端与服务端之间只有告,没有响应。即使响应信息被有,该应信息也会受忽视。这种措施接近于信息的打招呼或者播放。当一个劳动措施给设置为One-Way时,如果该方法有返值,会丢掉来InvalidOperationException异常。

假设拿劳动措施设置也One-Way非常简单,只待将OperationContractAttribute的性质IsOneWay设置为true就足以了,如下的代码所示:
public class Radio
{
 [OperationContract(IsOneWay=true)]
 private void BroadCast();
}

3、Duplex
Duplex消息交换模式抱有客户端和服务端双向通信的功能,同时其的兑现还可以假设信息交换具有异步回调的图。

设促成信息交换的Duplex,相对比较复杂。它用定义两个接口,其中服务接口用于客户端向服务端发送信息,而回调接口则是于服务端返回消息于客户端,它是通过回调的方法来成功的。接口定义如下:
劳动接口:
[ServiceContract(Namespace =
“http://microsoft.servicemodel.samples/”,
Session = true, CallbackContract=typeof(ICalculatorDuplexCallback))]
public interface ICalculatorDuplex
{
    [OperationContract(IsOneWay=true)]
    void Clear();

    [OperationContract(IsOneWay = true)]
    void AddTo(double n);

    [OperationContract(IsOneWay = true)]
    void SubtractFrom(double n);

    [OperationContract(IsOneWay = true)]
    void MultiplyBy(double n);

    [OperationContract(IsOneWay = true)]
    void DivideBy(double n);
}
回调接口:
public interface ICalculatorDuplexCallback
{
    [OperationContract(IsOneWay = true)]
    void Equals(double result);

    [OperationContract(IsOneWay = true)]
    void Equation(string equation);
}
留神在接口定义中,每个服务方法的消息转换模式均设置也One-Way。此外,回调接口是受地方调用,因此无待定义[ServiceContract]。在劳动接口中,需要设置ServiceContractAttribute的CallbackContract属性,使其对回调接口的类type。

对此贯彻劳务之近乎,实例化模式(InstanceContextMode)究竟是应用PerSession方式,还是PerCall方式,应依据拖欠服务对象是不是要保留状态来控制。如果是PerSession,则服务对象的生命周期是长存于一个对话期间。而PerCall方式下,服务对象是当道给调用时创造,结束晚即使吃灭绝。然而当Duplex模式下,不可知采取Single方式,否则会招大抛出。本例的落实如下:
[ServiceBehavior(InstanceContextMode =
InstanceContextMode.PerSession)]
public class CalculatorService : ICalculatorDuplex
{
    double result;
    string equation;
    ICalculatorDuplexCallback callback = null;
    public CalculatorService()
    {
        result = 0.0D;
        equation = result.ToString();
        callback = OperationContext.Current.
        GetCallbackChannel();
    }
    public void AddTo(double n)
    {
        result += n;
        equation += ” + ” + n.ToString();
        callback.Equals(result);
    }
   // Other code not shown.
}

在类CalculatorService中,回调接口对象callback通过OperationContext.Current.GetCallbackChannel<>()获取。然后以劳务方式例如AddTo()中,通过调用该回调对象的主意,完成服务端向客户端返回消息的机能。

当用Duplex时,Contract使用的Binding应该是系统提供的WSDualHttpBinding,如果运用BasicHttpBinding,会油然而生谬误。因此Host程序应该如下所示:
    public static void Main(string[] args)
    {
        Uri uri = new
Uri(“http://localhost:8080/servicemodelsamples”);
        using (ServiceHost host = new
ServiceHost(typeof(CalculatorService), uri))
        {
            host.AddServiceEndpoint(typeof(ICalculatorDuplex),new
WSDualHttpBinding(),”service.svc”);
            host.Open();
            Console.WriteLine(“Press any key to quit service.”);
            Console.ReadKey();
        }
    }
万一是应用安排文件,也应作相应的改,如本例:
  <system.serviceModel>
    <client>
      <endpoint name=””
               
address=”http://localhost:8080/servicemodelsamples/service.svc”
                binding=”wsDualHttpBinding”
                bindingConfiguration=”DuplexBinding”
                contract=”ICalculatorDuplex” />
    </client>
    <bindings>
      <!– configure a binding that support duplex communication
–>
      <wsDualHttpBinding>
        <binding name=”DuplexBinding”
                
clientBaseAddress=”http://localhost:8000/myClient/”>
        </binding>
      </wsDualHttpBinding>
    </bindings>
  </system.serviceModel>

当服务端将消息回送至客户端后,对信息之拍卖是由回调对象来处理的,所以回调对象的贯彻该是当客户端好,如下所出示之代码应该是在客户端挨:
    public class CallbackHandler : ICalculatorDuplexCallback
    {
        public void Equals(double result)
        {
            Console.WriteLine(“Equals({0})”, result);
        }
        public void Equation(string equation)
        {
            Console.WriteLine(“Equation({0})”, equation);
        }
    }

客户端调用劳动对象相应的也:
    class Client
    {
        static void Main()
        {
            // Construct InstanceContext to handle messages on
            // callback interface.
            InstanceContext site = new InstanceContext(new
CallbackHandler());

            // Create a proxy with given client endpoint
configuration.
            using (CalculatorDuplexProxy proxy =
            new CalculatorDuplexProxy(site, “default”))
            {
                double value = 100.00D;
                proxy.AddTo(value);
                value = 50.00D;
                proxy.SubtractFrom(value);
                // Other code not shown.

                // Wait for callback messages to complete before
                // closing.
                System.Threading.Thread.Sleep(500);
                // Close the proxy.
                proxy.Close();
            }
        }
    }

留神在Duplex中,会话创建的机遇并无是客户端创建Proxy实例的时,而是当服务对象的办法为第一不好调动用时,会话方才建立,此时服务目标会以方调用之前被实例化,直至会话结束,服务目标都是有的。

如上之代码例子在WinFX的SDK
Sample中得以找到。不过该例子并无克直接体现出Duplex功能。通过前的牵线,我们知晓Duplex具有客户端和服务端双向通信的职能,同时她的落实还足以使信息交换具有异步回调的作用。因此,我分别实现了点儿独实例来呈现Duplex在个别者的意图。

(1)客户端与服务端双向通信功能——ChatDuplexWin
实例证实:一个类于聊天室的稍程序。利用Duplex支持客户端与服务端通信的特性,实现了客户端与服务端聊天的效用。

劳动接口和回调接口的概念如下:
    [ServiceContract(Namespace =
“http://www.brucezhang.com/WCF/Samples/ChatDuplex”, Session = true,
CallbackContract=typeof(IChatDuplexCallback))]
    public interface IChatDuplex
    {
        [OperationContract(IsOneWay=true)]
        void Request(string cltMsg);
        [OperationContract(IsOneWay = true)]
        void Start();
    }
    public interface IChatDuplexCallback
    {
        [OperationContract(IsOneWay=true)]
        void Reply(string srvMsg);
    }
可怜扎眼,Request方法的功力也客户端向服务端发送信息,Reply方法则要服务端回送消息被客户端。服务接口IChatDuplex中之Start()方法,用于展示的树立一个会话,因为当是法子被,我用一直得到callback对象,使得服务端不必等客户端先发送信息,而是可以使callback对象积极先往客户端发送信息,从而实现聊天功能。

实现类似的代码如下:
    [ServiceBehavior(InstanceContextMode =
InstanceContextMode.PerSession)]
    public class ChatDuplex:IChatDuplex
    {
        public ChatDuplex()
        {
            m_callback =
OperationContext.Current.GetCallbackChannel();           
        }
        private IChatDuplexCallback m_callback = null;           
        public void Request(string cltMsg)
        {
           
ChatRoomUtil.MainForm.FillListBox(string.Format(“Client:{0}”,
cltMsg));           
        }
        public void Start()
        {
            ChatRoomUtil.MainForm.SetIIMDuplexCallback(m_callback);
        }
    }

为自求在劳动端界面中,能够用客户端发送来的信显示在主窗体界面中。所以采取了全局变量MainForm,用来保存主窗体对象:
    public static class ChatRoomUtil
    {
        public static ServerForm MainForm = new ServerForm();
    }
假如在服务端程序运行时,Application运行的主窗口呢也该全局变量:
Application.Run(ChatRoomUtil.MainForm);

倘促成聊天功能,最酷之阻力是当服务端收到客户端音不时,不克及时Reply消息,而应等待服务端用户输入回送的音信内容,方得以Reply。也即凡说,当客户端调用劳动对象的Request方法时,不能够一直调用callback对象。因此我以Start()方法,将服务目标中赢得的callback对象传递到主窗体对象被。这样,callback对象就足以留待服务端发送信息不时调用了:
    public partial class ServerForm : Form
    {       
        private IChatDuplexCallback m_callback;
        private void btnSend_Click(object sender, EventArgs e)
        {
            if (txtMessage.Text != string.Empty)
            {
                lbMessage.Items.Add(string.Format(“Server:{0}”,
txtMessage.Text));
                if (m_callback != null)
                {
                    m_callback.Reply(txtMessage.Text);
                }
                txtMessage.Text = string.Empty;
            }
        }       
        public void FillListBox(string message)
        {
            lbMessage.Items.Add(message);
        }
        public void SetIIMDuplexCallback(IChatDuplexCallback callback)
        {
            m_callback = callback;
        }
     //Other code not shown;
    }

对于客户端的实现,相对简便易行,需要留意的是回调接口的贯彻:
    public class ChatDuplexCallbackHandler:IChatDuplexCallback
    {
        public ChatDuplexCallbackHandler(ListBox listBox)
        {
            m_listBox = listBox;
        }
        private ListBox m_listBox;       

        public void Reply(string srvMsg)
        {
            m_listBox.Items.Add(string.Format(“Server:{0}”, srvMsg));
        }
    }
鉴于自家由定义了该对象的构造函数,所以在实利化proxy时见面产生微区别:
InstanceContext site = new InstanceContext(new
ChatDuplexCallbackHandler(this.lbMessage));
proxy = new ChatDuplexProxy(site);
proxy.Start();

经过proxy对象的Start()方法,使得我们于确立proxy对象开始,就创办了对话,从而令劳动目标被实例化,从而得以运行下面的立即行代码:
m_callback = OperationContext.Current.GetCallbackChannel(); 

也就是说,在proxy对象建立之后,服务端就已经收获callback对象了,这样尽管可以保证服务端能够先向客户端发送信息使非见面盖callback为null,导致错误的发生。

(2)消息交换的异步回调功能——AsyncDuplexWin
实例证实:本实例比较简单,只是为证实当回调对象吃调用时,客户端是不是可以被异步运行。调用服务目标时,服务端会开展一个抬高运算。在运算未形成前,客户端会执行显示累加数字的天职,当服务端运算结束后,只要客户端程序的线程处于Sleep状态,该回调对象就见面受调用,然后根据用户挑选是否又持续运行剩下的职责。本例中服务端为控制台应用程序,客户端则也Windows应用程序。

事例中之接口定义非常简单,不再赘述,而落实类似的代码如下:
    public class SumDuplex:ISumDuplex
    {
        public SumDuplex()
        {
            callback = OperationContext.Current.GetCallbackChannel();
        }
        private ISumDuplexCallback callback = null;

        #region ISumDuplex Members
        public void Sum(int seed)
        {
            int result = 0;
            for (int i = 1; i < = seed; i++)
            {
                Thread.Sleep(10);
                Console.WriteLine(“now at {0}”,i);
                result += i;               
            }
            callback.Equals(result);
        }
        #endregion
    }
深显眼,当客户端调用该服务目标时,会于劳务端的控制台上打印出迭代值。

鉴于客户端需要以callback调用时,停止对当前任务的运作,所以用因此到几近线程机制:
    public delegate void DoWorkDelegate();
    public partial class ClientForm : Form
    {
        public ClientForm()
        {
            InitializeComponent();
            InstanceContext site = new InstanceContext(new
SumDuplexCallbackHandler(this.lbMessage));
            proxy = new SumDuplexProxy(site);           
        }
        private SumDuplexProxy proxy;
        private Thread thread = null;
        private DoWorkDelegate del = null;       
        private int counter = 0;

        private void btnStart_Click(object sender, EventArgs e)
        {
            proxy.Sum(100);
            thread = new Thread(new ThreadStart(delegate()
            {
                while (true)
                {
                    if (ClientUtil.IsCompleted)
                    {
                        if (MessageBox.Show(“Game over,Exit?”, “Notify”,
MessageBoxButtons.YesNo,
                            MessageBoxIcon.Question) ==
DialogResult.Yes)
                        {
                            break;
                        }
                    }
      if (counter > 10000)
                    {
                        break;
                    }
                    if (del != null)
                    {
                        del();
                    }
                    Thread.Sleep(50);
                }
            }
            ));
            del += new DoWorkDelegate(DoWork);
            thread.Start();
        }                  

        private void DoWork()
        {
            if (lbMessage.InvokeRequired)
            {
                this.Invoke(new DoWorkDelegate(DoWork));
            }
            else
            {
                lbMessage.Items.Add(counter);               
                lbMessage.Refresh();
                counter++;
            }
        }
        private void ClientForm_FormClosing(object sender,
FormClosingEventArgs e)
        {
            if (thread != null)
            {
                thread.Abort();
            }
        }
    }

因为急需在多线程中对ListBox控件的items进行改动,由于该控件不是线程安全之,所以应采用该控件的InvokeRequired属性。此外,在线程启动时之匿名方式中,利用while(true)控制时线程的运作,并运用全局变量ClientUtil.IsCompleted判断回调对象是否为调用,如果让调用了,则会弹来对话框,选择是否退出当前任务。这里所谓的当前任务实际就是调用DoWork方法,向ListBox控件的items中不止添加累加的counter值。注意客户端的回调对象实现如下:
    class SumDuplexCallbackHandler:ISumDuplexCallback
    {
        public SumDuplexCallbackHandler(ListBox listBox)
        {
            m_listBox = listBox;
        }
        private ListBox m_listBox;      
        #region ISumDuplexCallback Members
        public void Equals(int result)
        {
     ClientUtil.IsCompleted = true;
            m_listBox.Items.Add(string.Format(“The result is:{0}”,
result));
            m_listBox.Refresh();           
        }     
        #endregion
    }
当客户端点击Start按钮,调用服务对象的Sum方法后,在劳动端会显示迭代值,而客户端也开实行好之任务,向ListBox控件被添加累加数。一旦服务端运算完毕,就用运算结果经回调对象传递到客户端,全局变量ClientUtil.IsCompleted将于装也true。如果上加累加值的线程处于sleep状态,系统就是会见用结果值加加至ListBox控件被,同时会弹来对话框,决定是否延续剩下的任务。

流淌:本文示例的代码和实例均于Feb 2006 CTP版本下运作。

< 未完待续>

参考:
1、David Chappell,Introducing Windows Communication Foundation
2、Microsoft Corporation,WinFX SDK