2011-05-14 57 views
1

我可以使用yield關鍵字更新循環的進度嗎?在循環中使用yield關鍵字來更新C中的進度條

foreach(something obj in somelist) 
{ 
    yield return obj; 
} 

我知道我可以這樣做,但是如何使用這些值來更新進度條?

謝謝。

+5

不知道,但我想你混淆了事件產量... – digEmAll 2011-05-14 13:37:05

+0

不,我需要通過WCF服務使用的收益率從服務器更新客戶端關於進展。 – Josh 2011-05-14 13:39:37

+0

您可能正在尋找計時器 - 請參閱http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx – 2011-05-14 13:41:39

回答

1

基於您的評論,你是

試圖更新WCF服務 傳輸數據的進度,一個 Windows窗體

這裏是我用過的解決方案在最近的一個項目上。示例代碼適用於下載文件(即pdf,jpg等),但可以應用於任何流傳輸數據。

第一個重要的注意事項是,您的WCF綁定選項將決定這是否可能。如果您使用的是basicHttpBinding或netTcpBinding,那麼該方法應該可行,但如果您使用的是WsHttpBinding,那麼您運氣不佳。

樣本綁定可能看起來是這樣的:

<basicHttpBinding> 
    <binding name="ResourceServiceBasicHttpBinding" maxReceivedMessageSize="20971520" sendTimeout="00:05:00" receiveTimeout="00:05:00" transferMode="Streamed" messageEncoding="Mtom"> 
     <security mode="None" /> 
    </binding> 
    </basicHttpBinding> 

需要注意的重要一點是傳輸模式「流」。

要報告WCF服務和WinForm之間的數據傳輸進度,您需要確保您的WCF服務使用DataContracts上的MessageContracts。原因是MessageContract允許您將單個流作爲響應主體。例如,你可能有一個請求/響應消息的合同如下:

的請求......

[MessageContract] 
public class DownloadFileRequest 
{ 
    [MessageHeader(MustUnderstand = true)] 
    public Guid FileId { get; set; } 
} 

響應...

[MessageContract] 
public class DownloadFileResponse : IDisposable 
{ 
    [MessageHeader(MustUnderstand = true)] 
    public Int32 FileSize { get; set; } 

    [MessageHeader(MustUnderstand = true)] 
    public String FileName { get; set; } 

    [MessageBodyMember(Order = 1)] 
    public Stream FileByteStream { get; set; } 

    public void Dispose() 
    { 
    if (FileByteStream != null) 
     FileByteStream.Dispose(); 
    } 
} 

現在,這一切都很好,但是您需要有一種方法來將該下載(或上載)進度報告給客戶端。不幸的是,.NET並沒有提供一個很好的方式來做到這一點...正如其他人所提到的,您正在尋找事件......特別是「ObservableStream」的實現。再次,這裏是我喜歡使用的實現....

事件args向用戶報告讀取或寫入操作...

public class ObservableStreamEventArgs : EventArgs 
{ 
    private readonly Int64 _length; 
    private readonly Int64 _position; 
    private readonly Int32 _numberOfBytes; 

    public Int64 StreamLength { get { return _length; } } 
    public Int64 StreamPosition { get { return _position; } } 
    public Int32 NumberOfBytes { get { return _numberOfBytes; } } 

    public ObservableStreamEventArgs(Int32 numberOfBytes, Int64 position, Int64 length) 
    { 
    _numberOfBytes = numberOfBytes; 
    _position = position; 
    _length = length; 
    } 
} 

當然流實現自身和...

public class ObservableStream : Stream 
{ 
    private readonly Stream _baseStream; 
    private Int64 _streamLength; 

    public event EventHandler<ObservableStreamEventArgs> BytesRead; 
    public event EventHandler<ObservableStreamEventArgs> BytesWritten; 

    public ObservableStream(Stream stream) 
    { 
    Verify.NotNull(stream); 

    _baseStream = stream; 
    _streamLength = _baseStream.Length; 
    } 

    public ObservableStream(Stream stream, Int64 length) 
    { 
    Verify.NotNull(stream); 

    _baseStream = stream; 
    _streamLength = length; 
    } 

    public override bool CanRead  
    {   
    get { return _baseStream.CanRead; } 
    } 

    public override bool CanSeek 
    { 
    get { return _baseStream.CanSeek; } 
    } 

    public override bool CanWrite 
    { 
    get { return _baseStream.CanWrite; } 
    } 

    public override void Flush() 
    { 
    _baseStream.Flush(); 
    } 

    public override Int64 Length 
    { 
     get { return _streamLength; } 
    } 

    public override Int64 Position 
    { 
     get { return _baseStream.Position; } 
     set { _baseStream.Position = value; } 
    } 

    public override Int32 Read(Byte[] buffer, Int32 offset, Int32 count) 
    { 
     Int32 bytesRead = _baseStream.Read(buffer, offset, count); 

     var listeners = BytesRead; 
     if (listeners != null) 
     listeners.Invoke(this, new ObservableStreamEventArgs(bytesRead, Position, Length)); 

     return bytesRead; 
    } 

    public override Int64 Seek(Int64 offset, SeekOrigin origin) 
    { 
     return _baseStream.Seek(offset, origin); 
    } 

    public override void SetLength(Int64 value) 
    { 
     _streamLength = value; 
    } 

    public override void Write(Byte[] buffer, Int32 offset, Int32 count) 
    { 
     _baseStream.Write(buffer, offset, count); 

     var listeners = BytesWritten; 
     if (listeners != null) 
     listeners.Invoke(this, new ObservableStreamEventArgs(count, Position, Length)); 
    } 

    public override void Close() 
    { 
     _baseStream.Close(); 

     base.Close(); 
    } 

    protected override void Dispose(bool disposing) 
    { 
     if (disposing) 
     _baseStream.Dispose(); 

     base.Dispose(disposing); 
    } 
    } 

最後,你必須消耗在您的Windows客戶端的數據...

簡單的打電話給你的服務方法爲您通常會(ServiceChannelFactory是一個ChannelFactory實現的包裝器......不用擔心)。

DownloadFileResponse response; 
using (var channel = _serviceChannelFactory.CreateReportServiceChannel()) 
    response channel.Service.GenerateReport(request) 

,然後轉播到你觀察到的流更新UI(顯然必須工作在後臺線程,否則,你只會阻塞UI)

using (var downloadStream = response.FileByteStream) 
using (var observableStream = new ObservableStream(downloadStream, response.FileSize)) 
using (var fileStream = new FileStream(tempFile, FileMode.OpenOrCreate, FileAccess.Write)) 
{ 
    observableStream.BytesRead += (sender, e) => _view.UpdateProgress(Convert.ToInt32(e.StreamPosition * 100/e.StreamLength)); 

    do 
    { 
    bytesRead = observableStream.Read(buffer, 0, 4096); 
    fileStream.Write(buffer, 0, bytesRead); 
    } while (bytesRead > 0); 
} 

編輯

哦,服務方法本身很簡單...忘了包括它;所有你需要做的就是回到你的流:

return new DownloadFileResponse 
      { 
      FileName = report.FileName, 
      FileSize = report.Data.Length, 
      FileByteStream = new MemoryStream(report.Data, false) 
      }; 
+0

非常感謝!我非常感謝你爲我製作這樣複雜的代碼所做的努力。但是,它看起來相當複雜,因此您能夠爲我提供可在任何地方下載的解決方案嗎?謝謝 – Josh 2011-05-14 20:09:11

+0

@Josh - 這些都是你需要的構建塊;如果您還有其他問題或需要進一步幫助將這些部分放在一起,請提問。 – 2011-05-15 13:15:28

0

正如用戶digEmAll的評論,它更可能是你想使用這個事件。

但是,如果您想要一個更新yield return進度的解決方案,您可能需要在迭代整個集合之前獲取元素的數量。然後,對於返回的每個元素,可以將進度條更新爲1。

如果你詳細闡述了你的問題 - 你試圖完成什麼,或許它會有用。

+0

詳細說明,我試圖更新wcf服務中的數據傳輸到Windows窗體的進度 – Josh 2011-05-14 13:49:13

+0

即使您使用yield,WCF服務也會在發送之前序列化整個消息。最簡單的方法可能是重新設計服務,以便它可以返回更小的數據塊 - 然後更新您獲取的每個塊的WinForm UI。 – driis 2011-05-14 13:51:57

0

爲什麼不更新循環內部的進度條?

+0

其在wcf服務上的服務器上,並且無法訪問該表單。 – Josh 2011-05-14 13:46:16

+0

您應該在最初的問題中包含這些信息。 – 2011-05-14 16:09:35