我可以使用yield關鍵字更新循環的進度嗎?在循環中使用yield關鍵字來更新C中的進度條
foreach(something obj in somelist)
{
yield return obj;
}
我知道我可以這樣做,但是如何使用這些值來更新進度條?
謝謝。
我可以使用yield關鍵字更新循環的進度嗎?在循環中使用yield關鍵字來更新C中的進度條
foreach(something obj in somelist)
{
yield return obj;
}
我知道我可以這樣做,但是如何使用這些值來更新進度條?
謝謝。
基於您的評論,你是
試圖更新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)
};
非常感謝!我非常感謝你爲我製作這樣複雜的代碼所做的努力。但是,它看起來相當複雜,因此您能夠爲我提供可在任何地方下載的解決方案嗎?謝謝 – Josh 2011-05-14 20:09:11
@Josh - 這些都是你需要的構建塊;如果您還有其他問題或需要進一步幫助將這些部分放在一起,請提問。 – 2011-05-15 13:15:28
正如用戶digEmAll的評論,它更可能是你想使用這個事件。
但是,如果您想要一個更新yield return
進度的解決方案,您可能需要在迭代整個集合之前獲取元素的數量。然後,對於返回的每個元素,可以將進度條更新爲1。
如果你詳細闡述了你的問題 - 你試圖完成什麼,或許它會有用。
爲什麼不更新循環內部的進度條?
其在wcf服務上的服務器上,並且無法訪問該表單。 – Josh 2011-05-14 13:46:16
您應該在最初的問題中包含這些信息。 – 2011-05-14 16:09:35
不知道,但我想你混淆了事件產量... – digEmAll 2011-05-14 13:37:05
不,我需要通過WCF服務使用的收益率從服務器更新客戶端關於進展。 – Josh 2011-05-14 13:39:37
您可能正在尋找計時器 - 請參閱http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx – 2011-05-14 13:41:39