none
WebClient的DownloadStringCompleted,封装后如何返回e.result RRS feed

  • 问题

  • public static class SLConfig
        {
            public static string GetAppSetting(string xmlFileUrl,string key)
            {
                string result = "";
    
                WebClient wc = new WebClient();
                wc.DownloadStringAsync(new Uri(HtmlPage.Document.DocumentUri, xmlFileUrl));
                wc.DownloadStringCompleted += (sender, e) =>
                {
                    XmlReader reader = XmlReader.Create(new StringReader(e.Result));
                    XDocument document = XDocument.Load(reader);
    
                    var s = from f in document.Descendants("appSettings").Descendants("add")
                            where f.Attribute("key").Value == key
                            select f;
    
                    if (s == null || s.Count() <= 0)
                    {
                        result = ""; 
                    }
                    else
                    {
                        result = s.FirstOrDefault().Attribute("value").Value;//这里我已经跟踪到了,已经取到值了
                    }
                };
                return result;
            }   
        }



    我把这个东西封装到一个类里了。我在xaml文件里调用。

    this.myTextBlock.Text = SLConfig.GetAppSetting("abc.xml", "myName");
    

    我跟踪了下,发现先执行这个赋值语句,

    然后才执行DownloadStringCompleted里语句呢。

    执行顺序不是我想象的,第一次接触异步调用。

    请问如何把那个方法封装起来呢?谢谢呀

     


    视别人的帮助为恩赐~
    2009年10月27日 9:46

答案

  • 在return result 前加一个WaitHandle, 然后在Completed handler里call Set.  Something like this:

    public static class SLConfig
        {
            public static string GetAppSetting(string xmlFileUrl,string key)
            {
                AutoResetEvent resetEvent = new AutoResetEvent(false);

                string result = "";

                WebClient wc = new WebClient();
                wc.DownloadStringAsync(new Uri(HtmlPage.Document.DocumentUri, xmlFileUrl));
                wc.DownloadStringCompleted += (sender, e) =>
                {
                    XmlReader reader = XmlReader.Create(new StringReader(e.Result));
                    XDocument document = XDocument.Load(reader);

                    var s = from f in document.Descendants("appSettings").Descendants("add")
                            where f.Attribute("key").Value == key
                            select f;

                    if (s == null || s.Count() <= 0)
                    {
                        result = "";
                    }
                    else
                    {
                        result = s.FirstOrDefault().Attribute("value").Value;//这里我已经跟踪到了,已经取到值了
                    }
                   resetEvent.Set()
                };
                resetEvent.WaitOne(2000);
                return result;
            }  
        }
    但这种用法要保证从Background thread中调用,否则UIThread会停住.
    更正规的解决方法是用Binding来解决,这样返回数据更新的时候会自动更新UI.

    2009年10月27日 19:49
  • resetEvent.WaitOne(2000);
    貌似不好吧。。要是真的网络延迟就完了.

    他的意思类似于这样的
     this
    .myTextBlock .setBinding(TextProperty,Bind);

    个人觉得。。应该在

    if (s == null || s.Count() <= 0)
    {
        result = ""
    }
     else
    {
         result = s.FirstOrDefault().Attribute("value").Value;//这里我已经跟踪到了,已经取到值了
    }
    后面加一个事件。表示该操作执行完毕.
    比如 ValeChanged

    然后在this.myTextBlock所在的类中实现ValeChanged事件。ValeChanged触发后再给this.myTextBlock赋值
    不过这样就修改比较大。不能是静态类了。


        纯属个人见解


    2009年10月28日 2:29

全部回复

  • 返回的是为 ""

    既然是异步的你就不能

    this.myTextBlock.Text = SLConfig.GetAppSetting("abc.xml", "myName");

    现在我没环境 明天帮你看看.
    2009年10月27日 14:33
  • 在return result 前加一个WaitHandle, 然后在Completed handler里call Set.  Something like this:

    public static class SLConfig
        {
            public static string GetAppSetting(string xmlFileUrl,string key)
            {
                AutoResetEvent resetEvent = new AutoResetEvent(false);

                string result = "";

                WebClient wc = new WebClient();
                wc.DownloadStringAsync(new Uri(HtmlPage.Document.DocumentUri, xmlFileUrl));
                wc.DownloadStringCompleted += (sender, e) =>
                {
                    XmlReader reader = XmlReader.Create(new StringReader(e.Result));
                    XDocument document = XDocument.Load(reader);

                    var s = from f in document.Descendants("appSettings").Descendants("add")
                            where f.Attribute("key").Value == key
                            select f;

                    if (s == null || s.Count() <= 0)
                    {
                        result = "";
                    }
                    else
                    {
                        result = s.FirstOrDefault().Attribute("value").Value;//这里我已经跟踪到了,已经取到值了
                    }
                   resetEvent.Set()
                };
                resetEvent.WaitOne(2000);
                return result;
            }  
        }
    但这种用法要保证从Background thread中调用,否则UIThread会停住.
    更正规的解决方法是用Binding来解决,这样返回数据更新的时候会自动更新UI.

    2009年10月27日 19:49
  • 谢谢呀,请有了环境后帮忙看下 :)
    视别人的帮助为恩赐~
    2009年10月28日 1:19
  • 谢谢xun sun,请问“更正规的解决方法是用Binding来解决,这样返回数据更新的时候会自动更新UI.”

    这种办法如何做呢?
    视别人的帮助为恩赐~
    2009年10月28日 1:20
  • resetEvent.WaitOne(2000);
    貌似不好吧。。要是真的网络延迟就完了.

    他的意思类似于这样的
     this
    .myTextBlock .setBinding(TextProperty,Bind);

    个人觉得。。应该在

    if (s == null || s.Count() <= 0)
    {
        result = ""
    }
     else
    {
         result = s.FirstOrDefault().Attribute("value").Value;//这里我已经跟踪到了,已经取到值了
    }
    后面加一个事件。表示该操作执行完毕.
    比如 ValeChanged

    然后在this.myTextBlock所在的类中实现ValeChanged事件。ValeChanged触发后再给this.myTextBlock赋值
    不过这样就修改比较大。不能是静态类了。


        纯属个人见解


    2009年10月28日 2:29