This commit is contained in:
龙腾猫跃
2024-10-16 03:24:00 +08:00
parent fb1caf0c8f
commit c5ae532fd5
49 changed files with 1881 additions and 1401 deletions
+140 -72
View File
@@ -1,7 +1,7 @@
Public Class MyImage
Public Class MyImage
Inherits Image
#Region "辅助属性注册"
#Region "公开属性"
''' <summary>
''' 网络图片的缓存有效期。
@@ -22,14 +22,6 @@ Public Class MyImage
End Property
Private _EnableCache As Boolean = True
'将 Source 属性映射到 XAML
Public Shared Shadows ReadOnly SourceProperty As DependencyProperty = DependencyProperty.Register(
"Source", GetType(String), GetType(MyImage), New PropertyMetadata(New PropertyChangedCallback(
Sub(sender, e) If sender IsNot Nothing Then CType(sender, MyImage).Source = e.NewValue.ToString())))
#End Region
Private _Source As String = ""
''' <summary>
''' 与 Image 的 Source 类似。
''' 若输入以 http 开头的字符串,则会尝试下载图片然后显示,图片会保存为本地缓存。
@@ -43,74 +35,150 @@ Public Class MyImage
If value = "" Then value = Nothing
If _Source = value Then Exit Property
_Source = value
Dim TempPath As String = $"{PathTemp}MyImage\{GetHash(value)}.png"
If Not IsInitialized Then Exit Property '属性读取顺序修正:在完成 XAML 属性读取后再触发图片加载(#4868)
Load()
End Set
End Property
Private _Source As String = ""
Public Shared Shadows ReadOnly SourceProperty As DependencyProperty = DependencyProperty.Register(
"Source", GetType(String), GetType(MyImage), New PropertyMetadata(New PropertyChangedCallback(
Sub(sender, e) If sender IsNot Nothing Then CType(sender, MyImage).Source = e.NewValue.ToString())))
''' <summary>
''' 当 Source 首次下载失败时,会从该备用地址加载图片。
''' </summary>
Public Property FallbackSource As String
Get
Return _FallbackSource
End Get
Set(value As String)
_FallbackSource = value
End Set
End Property
Private _FallbackSource As String = Nothing
''' <summary>
''' 正在下载网络图片时显示的本地图片。
''' </summary>
Public Property LoadingSource As String
Get
Return _LoadingSource
End Get
Set(value As String)
_LoadingSource = value
End Set
End Property
Private _LoadingSource As String = "pack://application:,,,/images/Icons/NoIcon.png"
#End Region
''' <summary>
''' 实际被呈现的图片地址。
''' </summary>
Public Property ActualSource As String
Get
Return _ActualSource
End Get
Set(value As String)
If value = "" Then value = Nothing
If _ActualSource = value Then Exit Property
_ActualSource = value
Try
'空
If value Is Nothing Then
MyBase.Source = Nothing
Exit Property
End If
'本地图片
If Not value.StartsWithF("http") Then
MyBase.Source = New MyBitmap(value)
Exit Property
End If
'从缓存加载网络图片
If EnableCache AndAlso File.Exists(TempPath) Then
MyBase.Source = New MyBitmap(TempPath)
If (Date.Now - File.GetCreationTime(TempPath)) < FileCacheExpiredTime Then
Exit Property '无需刷新缓存
Else
File.Delete(TempPath) '需要刷新缓存
End If
Else
MyBase.Source = Nothing '清空显示
End If
'下载网络图片
RunInNewThread(
Sub()
Dim Url As String = value '重新捕获变量,以检测在下载过程中 Source 被修改的情况
Dim Retried As Boolean = False
RetryStart:
Dim TempDownloadingPath As String = TempPath & RandomInteger(0, 10000000)
Try
Log("[MyImage] 正在下载图片:" & Url)
NetDownload(Url, TempDownloadingPath, True)
If Url <> Source Then
'若 Source 在下载时被修改,则不显示
File.Delete(TempDownloadingPath)
ElseIf EnableCache Then
'保存缓存并显示
Rename(TempDownloadingPath, TempPath)
RunInUi(Sub() MyBase.Source = New MyBitmap(TempPath))
Else
'直接显示
RunInUiWait(Sub() MyBase.Source = New MyBitmap(TempDownloadingPath))
File.Delete(TempDownloadingPath)
End If
Catch ex As Exception
Try
File.Delete(TempDownloadingPath)
Catch
End Try
If Not Retried Then
Log(ex, $"下载图片可重试地失败({Url})", LogLevel.Developer)
Retried = True
Thread.Sleep(1000)
GoTo RetryStart
Else
Log(ex, $"下载图片失败({Url})", LogLevel.Hint)
End If
End Try
End Sub, "MyImage PicLoader " & GetUuid() & "#", ThreadPriority.BelowNormal)
Dim Bitmap As MyBitmap = If(value Is Nothing, Nothing, New MyBitmap(value)) '在这里先触发可能的文件读取,尽量避免在 UI 线程中读取文件
RunInUiWait(Sub() MyBase.Source = Bitmap)
Catch ex As Exception
Log(ex, $"加载图片失败({value})", LogLevel.Hint)
Log(ex, $"加载图片失败({value})")
Try
File.Delete(TempPath) '删除缓存,以免缓存出现问题导致一直加载失败
If value.StartsWithF(PathTemp) AndAlso File.Exists(value) Then File.Delete(value)
Catch
End Try
End Try
End Set
End Property
Private _ActualSource As String = Nothing
End Class
Private Sub Load() _
Handles Me.Initialized '属性读取顺序修正:在完成 XAML 属性读取后再触发图片加载(#4868)
'空
If Source Is Nothing Then
ActualSource = Nothing
Exit Sub
End If
'本地图片
If Not Source.StartsWithF("http") Then
ActualSource = Source
Exit Sub
End If
'从缓存加载网络图片
Dim Url As String = Source
Dim Retried As Boolean = False
Dim TempPath As String = GetTempPath(Url)
Dim TempFile As New FileInfo(TempPath)
If EnableCache AndAlso TempFile.Exists Then
ActualSource = TempPath
If (Date.Now - TempFile.CreationTime) < FileCacheExpiredTime Then Exit Sub '无需刷新缓存
End If
RunInNewThread(
Sub()
Dim TempDownloadingPath As String = Nothing
Try
RetryStart:
'下载
ActualSource = LoadingSource '显示加载中图片
TempDownloadingPath = TempPath & RandomInteger(0, 10000000)
NetDownload(Url, TempDownloadingPath, True)
If Url <> Source AndAlso Url <> FallbackSource Then
'已经更换了地址
File.Delete(TempDownloadingPath)
ElseIf EnableCache Then
'保存缓存并显示
If File.Exists(TempPath) Then File.Delete(TempPath)
Rename(TempDownloadingPath, TempPath)
RunInUi(Sub() ActualSource = TempPath)
Else
'直接显示
RunInUiWait(Sub() ActualSource = TempDownloadingPath)
File.Delete(TempDownloadingPath)
End If
Catch ex As Exception
Try
If TempPath IsNot Nothing Then File.Delete(TempPath)
If TempDownloadingPath IsNot Nothing Then File.Delete(TempDownloadingPath)
Catch
End Try
If Not Retried Then
'更换备用地址
Log(ex, $"下载图片可重试地失败({Url})", LogLevel.Developer)
Retried = True
Url = If(FallbackSource, Source)
'空
If Url Is Nothing Then
ActualSource = Nothing
Exit Sub
End If
'本地图片
If Not Url.StartsWithF("http") Then
ActualSource = Url
Exit Sub
End If
'从缓存加载网络图片
TempPath = GetTempPath(Url)
TempFile = New FileInfo(TempPath)
If EnableCache AndAlso TempFile.Exists() Then
ActualSource = TempPath
If (Date.Now - TempFile.CreationTime) < FileCacheExpiredTime Then Exit Sub '无需刷新缓存
End If
'下载
If Source = Url Then Thread.Sleep(1000) '延迟 1s 重试
GoTo RetryStart
Else
Log(ex, $"下载图片失败({Url})", LogLevel.Hint)
End If
End Try
End Sub, "MyImage PicLoader " & GetUuid() & "#", ThreadPriority.BelowNormal)
End Sub
Public Shared Function GetTempPath(Url As String) As String
Return $"{PathTemp}MyImage\{GetHash(Url)}.png"
End Function
End Class