我們的需求是什么?
答:需要在圖片上增加一些自定義標記,例如:2個圖片對比時,對相同區域進行高亮。
先上效果圖:
設計思路
1.概述
1.通過TargeUpdated事件,重新繪制圖片進行替換。
2.詳細實現
1.我們先綁定ImageTargetUpdated事件。
<Image x:Name="DestImageControl" Source="{Binding Path=Source.Url, NotifyOnTargetUpdated=True}" TargetUpdated="ImageTargetUpdated">
根據微軟官方文檔,如上圖,我們需要設置NotifyOnRargetUpdated的值,官方文檔如下:
2.在響應方法中,對圖像進行繪制。
private void ImageTargetUpdated(object sender, DataTransferEventArgs e) { //todo:繪制圖像:drawingImage = DrawImage...... //設置新圖像:DestImageControl.SetCurrentValue(Image.SourceProperty, drawingImage); }
3.按照上面的思路,很完美,於是我們遇到了問題
什么?有什么問題?在直接讀取Image控件的Source時,我們發現它是沒有值的,所以我們繪制的圖片底圖是沒有的。
下面我們來分析一下:
由於Image控件的Source我們是綁定的Url(類似:http://xxx.xxx.com/xxx.jpg),所以wpf會進行一步下載,通過調試,我們會發現它此時的類是LateBoundBitmapDecoder。
關於類LateBoundBitmapDecoder的官方文檔如下:
我們發現,這個類加載圖片是異步的。所以我們在TargeUpdated事件中直接拿到的Source,此時僅僅是一個空對象,他的數據還在網絡中傳輸。知道原理就好辦了。
我們對Source綁定下載完成事件!
if (((BitmapFrame)imageControl.Source).IsDownloading) { //如果是異步下載,則綁定下載完成后事件 ((BitmapFrame)imageControl.Source).Decoder.DownloadCompleted += (sender2, e2) => { BitmapSource sourceImg = (BitmapSource)((LateBoundBitmapDecoder)sender2).Frames[0]; DrawImage(sourceImg); }; } else { //如果已存在圖片,則直接使用 BitmapSource sourceImg = (BitmapSource)imageControl.Source; DrawImage(sourceImg); }
再試運行一下,就和最前面的效果圖一致了。
總結
1.對於雙向綁定的Image控件,我們在原圖上增加內容時,可以響應TargeUpdated事件進行自定義繪制。
2.在TargeUpdated事件響應中,有可能拿不到Source,此時,我們需要綁定下載完成事件,在事件中進行底圖的獲取。
3.我們需要使用SetCurrentValue方法對Source進行賦值,如此不會影響雙向綁定。
PS:
1.此文僅介紹遇到問題的解決思路及解決過程,並不分享源碼。