2018/01/18

使用 using IDisposable 物件包裝 Stopwatch 並可自訂 log 處理動作

受到黑暗執行序一篇文章《野人獻曝 - 極簡風格 .NET Stopwatch 計時法》的啟發,我延伸了一點小小的功能,讓寫 log 的動作更有彈性一點。原文使用 IDisposable 物件包裝 Stopwatch,在程式碼中以 using 方式使用,在大括號內自動計時,並且在結束的時候寫出 log 內容。在實作 DIspose() 時示範加入 Console.WriteLine 寫出 log 資訊。但如果使用的情境,不一定是要在 Console 或 log 檔案輸出資訊的話呢? 可能是在視窗畫面上顯示資訊。為了達成這樣的目的,我作了小小的修改,在建構子傳入 Action<Stopwatch> 委派,日後就可以依需求自訂 log 處理動作。

/// <summary>
/// 以 using 方式包裝 Stopwatch 提供監看程式耗時的功能
/// </summary>

public class StopwatchScope : IDisposable {         
    private readonly Stopwatch stopwatch = new Stopwatch();
    private Action<Stopwatch> proc;         

    /// <summary>           
    /// 是否停用           
    /// </summary>
         
    public static bool Disabled { get; set; } = false;

    /// <summary>
    /// 建構式
    /// </summary>

    /// <param name="stopwatchProcess">StopwatchScope dispose 時候要執行的動作</param>
    public StopwatchScope(Action<Stopwatch> stopwatchProcess) {
        if (Disabled) return;
        proc = stopwatchProcess;
        stopwatch.Start();
    }
 
    /// <inheritdoc />
    public void Dispose() {
        if (Disabled) return;
        stopwatch.Stop();
        if (proc != null) proc.Invoke(stopwatch);
    }
}

使用起來大概像這樣,假設我們有個 Logger 物件專門寫 log 檔案,在 using 建立 StopwatchScope 時,可以傳入寫 log 的方法。

using (StopwatchScope sw = new StopwatchScope(w => {Logger.Write("do something 花費秒數: " + w.Elapsed.TotalSeconds.ToString());}) {
    // do something
}

呼叫 Logger 寫檔案,也可以替換成更新 Windows UI 某個 label 的內容或是 status bar 的資訊,如此就可以自訂 log 處理動作。

2015/04/11

Error message “The definition of the report '' is invalid” when opening a report in Windows Forms ReportViewer.

 

In the brief

I developed a Windows Forms application. There is a ReportViewer control to show some reports. The reports showed up well on my develop machine, but got error message on deployment machine. I finally found that the deployment machine was lack of some assemblies which ReportViewer needs.

Error Message Log

[Error] Message = An error occurred during local report processing. 
   at Microsoft.Reporting.WinForms.LocalReport.EnsureExecutionSession() 
   at Microsoft.Reporting.WinForms.LocalReport.SetParameters(IEnumerable`1 parameters)


[Error] Message = The definition of the report '' is invalid
   at Microsoft.Reporting.ReportCompiler.CompileReport(ICatalogItemContext context, Byte[] reportDefinition, Boolean generateExpressionHostWithRefusedPermissions, ControlSnapshot& snapshot) 
   at Microsoft.Reporting.LocalService.GetCompiledReport(PreviewItemContext itemContext, Boolean rebuild, ControlSnapshot& snapshot) 
   at Microsoft.Reporting.LocalService.CompileReport() 
   at Microsoft.Reporting.LocalService.Microsoft.Reporting.ILocalProcessingHost.CompileReport() 
   at Microsoft.Reporting.WinForms.LocalReport.EnsureExecutionSession(), Source = Microsoft.ReportViewer.Common, Method = CompileReport

 

ReporViewer assemblies

In my case, my project is reference Microsoft.ReportViewer.WinForms.dll version 11.0.0.0. All assemblies you need to copy to deployment machine are:

  • Microsoft.ReportViewer.Common.dll
  • Microsoft.ReportViewer.ProcessingObjectModel.dll
  • Microsoft.ReportViewer.WinForms.dll
  • Microsoft.SqlServer.Types.dll

You can find them in c:\windows\assembly\GAC_MSIL\ . Every assembly has its own folder and subfolder of different version.

How I find out the solution

I create an empty report. No parameters, no data source. And open it on deployment machine. Then, I saw the message on ReportViewer as showed below. The message says it needs other assembly to process report.

reportviewer

After I copied Microsoft.SqlServer.Types.dll to my application folder on the deployment machine. It showed next message that it want Microsoft.ReportViewer.ProcessingObjectModel.dll. And then copy it. Finally an empty report showed up. So I change my code to open the normal report which it was be. It works! All reports show up well on deployment machine now.

Final thoughts

Usually I only copy Microsoft.ReportViewer.Common.dll and Microsoft.ReportViewer.WinForms.dll to deployment machine when using ReportViewer version 10. This time I changed my ReportViewer to version 11 and got this error message “The definition of the report '' is invalid”. I googled everywhere but no useful solution for my case. Although someone mentioned it was ReportViewer version issue. But I still don’t know what’s the key point about version issue or what assembly missed in my application. Well, if you has the same problem, report works well on develop machine but not on deployment machine, try this solution.

2014/07/01

Why not Surface OS ?

微軟在 Windows 8 強推 Metro UI,卻因為使用者無法適應而導致 Windows 8 評價不高。而 Google 即將推出的 Android L 在我眼裡,卻是真正實踐 Windows 8 人機互動願景的作業環境。簡潔,專注,資訊能夠有效傳達的順暢使用體驗。

Google 讓大家慢慢接受 Android,一路從手機平板慢慢進擊到桌面運算設備。微軟卻仍得不斷挑戰自己一手建立的,怠惰、安於現狀、自我滿足、不敢跳出舒適圈的廣大 Windows 用戶。Windows Vista 想大跳躍卻跌得重重的,Windows 8 也被認為即將步上 Vista 的後塵。

其實,Windows 8 的失策,是沒有殺手級裝置搭配。這也一直是 PC 市場會萎縮的原因。整個 PC 市場都以製造業的角度在做電腦,Apple 卻從 iPod、iPhone、iPad 一路為使用者對於運算裝置帶來不同以往的使用體驗。

在我眼裡,Surface Pro 是市面上最能完整提供 Windows 8 使用體驗的裝置,若時光倒移,我覺得微軟該直接發表 Surface Pro 產品,並且將 OS 名稱定為 Surface OS。而 Surface OS 中有一個可以執行完整 Windows 7 App 的 Desktop 的環境。然後,下一個 Windows 版本推出的時候,同步免費提供新版 Surface OS。

並且,暫時不要讓 PC 用戶使用 Surface OS。直到哪些沒有創新力的 PC 廠商,終於也想要認真做觸控裝置的時候,推行 "微軟觸控裝置認證" ,通過認證的電腦,才可以安裝 Surface OS。以避免一堆觸控導向的程式,在鍵盤滑鼠的傳統操作方式下,顯得格格不入,而敗壞 Surface OS 的名聲。

微軟抱著 Windows 太久了,腦袋也和不能接受 "Windows 長的不像 Windows" 的客戶一樣,同樣的東西,換個名字,轉個角度,就能變成美好的生活體驗。

2014/05/27

程式錯誤經驗談:產生列表項目時,每一筆都向 Database 取時間

今天犯了個錯誤,在 ListView 填入資料時,因為要檢查資料日期是否逾時,必須以 Database 的時間為基準,所以在產生每一個 ListVieweItem 的時候,都向 Database 取時間,使得畫面顯示列表的時間拖得非常久。

最初,程式裡面使用 DateTime.Now 為基準,但客戶要求不可以使用 client 電腦的時間,以免 user 自己改到電腦時間。因此,我寫了一個全域的 ServerDate 方法向 Database 取得時間,然後把程式裡面所有 DateTime.Now 取代成 ServerDate 方法。當然包含了產生列表這一段,小小的災難就這樣發生了。

主管安慰我,說一定是後來因為客戶要求,臨時改掉原來的程式,才會令人疏忽,發生這樣的問題。的確,這是個無心之過。但! 身為一個程式設計師,應該對使用 Database 的資源充滿警覺性。

後來,小小改了一下,程式效能變得 40 幾倍快。哀~ 要小心。

2013/10/22

Visual Studio 2013 can download now. BUT where is my license?

VS2013 was released at Oct.17, 2013. I download it from MSDN Subscription. After installed vs2013, I can’t get my license. I used vs2013 from preview version to RC. I don’t know the license information showed in the screen, “Prerelease software”, is any matters ? It is strange. How do developers get valid license in my company ?

image

2012/07/04

[WPF] Asynchronously Loading Image From Internet using C# 5.0 (.NET Framework 4.5)


Requirement

I want to create a WPF window which displays many images from internet. I put many <Image> element on the window and set the Source of every <Image>. When I run the application, the UI is locked till all images loaded completely. This, UI locked, is not what I want. Is there any way to load image asynchronously?

Old Implement by ThreadPool

Then, an idea come to my head: make a user control and load image in another thread. My first implement use ThreadPool, Dispather to achieve this goal. But that is not the point of this article.

C# 5.0 and .NET Framework 4.5 do more for you

Recently I read that C# 5.0 introduced new keyword async and await. It make asynchronous programming simple. So I want to take a try. And I just want to share with you what I found.

The markup of <AsyncImage>

I create a user control named AsyncImage and put an Image element named “img” inside. The xaml is very simple.

<UserControl ...>
  <Grid>
    <Image name="img" />
  </Grid>
</UserControl>

As it is a control that can be put on the UI. I want to set the image source string when using it. Therefor I add a dependency property into the code.

public string ImageSource {
    get { return (string)GetValue(ImageSourceProperty); }
    set { SetValue(ImageSourceProperty, value); }
}

public static readonly DependencyProperty ImageSourceProperty =
    DependencyProperty.Register("ImageSource", typeof(string), typeof(AsyncImage), new UIPropertyMetadata("", OnImageSourceChanged));

When ImageSouce is set, it will call OnImageSourceChanged. It looks like this.

private static void OnImageSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    AsyncImage v = d as AsyncImage;
    if (v == null)
        return;

    v.ShowImageAsync(e.NewValue.ToString());
}

The key word async And await

Let’s see the rest codes.

protected async void ShowImageAsync(string source) {
    img.Source = await LoadImageSourceAsync(source);
}

private async Task<ImageSource> LoadImageSourceAsync(string address) {
    ImageSource imgSource = null;

    try {
        MemoryStream ms = new MemoryStream(await new WebClient().DownloadDataTaskAsync(new Uri(address)));
        ImageSourceConverter imageSourceConverter = new ImageSourceConverter();
        imgSource = (ImageSource)imageSourceConverter.ConvertFrom(ms);

    } catch (Exception ex) {
        Debug.WriteLine(ex.Message);
        Debug.WriteLine(ex.StackTrace);
    }

    return imgSource;
}

ShowImageAsync is a void method modified by async keyword. Because there is a await call in the method block. 

LoadImageSource is another asynchronous method. It downloads the image by await call to WebClent.DownloadDataTaskAsync. WebClient class in .Net Framework 4.5 adds several asynchronous method which adding the word, “Async”, behind the original method name. The new method of the WebClient is the key point. It can do works in the background and return value when works completed. When I set img.Source by await call to LoadImageSourceAsync, it will do its works in the background, too.

I put AsyncImage in a test window. When downloading image, the window can still drag, move, change size. Only one thing. UI will still be locked in a very short time when Image.ImageSource is setting.

Responsive UI is easy

With async and await, asynchronous method call is like normal method. It reduced the cross thread call exceptions. A high performance and responsive UI is more and more easy.

2011/07/26

我的資訊觀察 : 平板電腦崛起後, 受惠者是... 顯示器.

我已經開始想像在不遠的未來 (就是明年 2012 年), 更多性能強大的平板上市後, 桌面上的裝置會是個怎麼樣的情景.

一個具有四核心, 64G 快閃記憶體硬碟的 7" 平版運行 Windows 8 作業系統, 效能接近我目前的 NB, 放在桌面上使用時, 會接一個 dock, dock 連接了鍵盤滑鼠等週邊, 還有 Lightpeak 連接的儲存裝置 (至少 2TB) 與 "外接顯卡", 使用 24" Full HD 雙螢幕輸出 (7" 加上 24""). 7" 螢幕顯示 Metro 介面, 當成純平版的樣子, 輸出到 24" 的螢幕是傳統 Winddows 畫面, 可以執行我現在做的一般事物 : 開 VM, 寫程式, 聽音樂.

離開桌面 (下班) 就把平板拔下來, dock 包一包放進袋子裡, 通勤時拿著 7" 小螢幕看雜誌, 小說. 回到家一樣放回 dock 上, 在書房就接電腦顯示器, 到客廳就接電視.

若照我這想像的樣子, 在一天的生活中, 這部平板會接觸到三部顯示裝置, 可見顯示器需求應該會增加的.