一般來說,我們還是比較習慣做成一個控件的,總不可能每次用到圓形圖像的話,去寫上面這麼一大堆。下面我們就
來動手幹!
在 Visual Studio 中新建一個用戶控件(UserControl),我們命名爲 CircleImage。
然後在後臺代碼中定義一個依賴屬性 Source,表示圖片的源。由於 BitmapImage 的 UriSource 是 Uri 類型的,因
此我們的 Source 屬性也是 Uri 類型。Source 變化時,我們設置到 XAML 中的 BitmapImage 的 UriSource 屬性上
去。
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(nameof(Source), typeof(Uri), typeof(CircleImage), new PropertyMetadata(null, SourceChanged));
private static void SourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var obj = (CircleImage)d;
var value = (Uri)e.NewValue;
obj.bitmapImage.UriSource = value;
}
public Uri Source
{
get
{
return (Uri)GetValue(SourceProperty);
}
set
{
SetValue(SourceProperty, value);
}
}
然後開始編寫前臺 XAML:
<UserControl
x:Class="MyApp.CircleImage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Ellipse>
<Ellipse.Fill>
<ImageBrush>
<ImageBrush.ImageSource>
<BitmapImage x:Name="bitmapImage"
DecodePixelWidth="1"
DecodePixelHeight="1" />
</ImageBrush.ImageSource>
</ImageBrush>
</Ellipse.Fill>
</Ellipse>
</UserControl>
將 BitmapImage 命名爲 bitmapImage,給上面的後臺 cs 代碼使用。
這裏可能你會奇怪,爲什麼我將解碼的大小寫成了 1?這是因爲,如果解碼大小寫成小於 1 的整數的話,就等於沒有
自動根據渲染大小來解碼的功能了,那就跟一開始原文作者的效果一樣。所以這裏我先寫成 1,運行的時候再根據控
件的大小來動態調整。
那麼既然要動態調整,那麼我們必須得完善後臺代碼了,添加一些代碼上去,修改成這樣:
public sealed partial class CircleImage : UserControl
{
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(nameof(Source), typeof(Uri), typeof(CircleImage), new PropertyMetadata(null, SourceChanged));
private static void SourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var obj = (CircleImage)d;
var value = (Uri)e.NewValue;
obj.bitmapImage.UriSource = value;
}
public Uri Source
{
get
{
return (Uri)GetValue(SourceProperty);
}
set
{
SetValue(SourceProperty, value);
}
}
public CircleImage()
{
this.InitializeComponent();
this.SizeChanged += CircleImage_SizeChanged;// 監聽控件大小發生變化。
}
private void ReSize()
{
// 計算新的解碼大小,向上取整。
int width = (int)Math.Ceiling(this.ActualWidth);
int height = (int)Math.Ceiling(this.ActualHeight);
// 確保解碼大小必須大於 0,因爲上面的結果可能爲 0。
bitmapImage.DecodePixelWidth = Math.Max(width, 1);
bitmapImage.DecodePixelHeight = Math.Max(height, 1);
// 讓 BitmapImage 重新渲染。
var temp = bitmapImage.UriSource;
bitmapImage.UriSource = null;
bitmapImage.UriSource = temp;
}
private void CircleImage_SizeChanged(object sender, SizeChangedEventArgs e)
{
ReSize();
}
}
這樣我們就完成了這個控件了,接下來我們來測試下究竟這個東西威力有多大。
測試:
測試圖片使用我博客的背景圖片,足夠大的了,1920*1080,相信應該會喫掉不少內存^-^。
圖片地址:http://images.cnblogs.com/cnblogs_com/h82258652/693238/o_wallpaper_summer2013_1920X1080.jpg
測試程序代碼:
前臺 XAML 代碼:
<Page
x:Class="MyApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" HorizontalAlignment="Center" Orientation="Horizontal">
<Button Content="加載舊版" Click="BtnOld_Click" />
<Button Content="加載新版" Click="BtnNew_Click" />
</StackPanel>
<GridView Grid.Row="1" x:Name="gvwBigMemory">
<GridView.ItemTemplate>
<DataTemplate>
<Ellipse Width="100" Height="100">
<Ellipse.Fill>
<ImageBrush ImageSource="http://images.cnblogs.com/cnblogs_com/h82258652/693238/o_wallpaper_summer2013_1920X1080.jpg" />
</Ellipse.Fill>
</Ellipse>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
<GridView Grid.Row="2" x:Name="gvwMinMemory">
<GridView.ItemTemplate>
<DataTemplate>
<local:CircleImage Width="100" Height="100" Source="http://images.cnblogs.com/cnblogs_com/h82258652/693238/o_wallpaper_summer2013_1920X1080.jpg" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Grid>
</Page>
都放在 GridView 裏面,大小都設定爲 100*100。
後臺代碼:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private void BtnOld_Click(object sender, RoutedEventArgs e)
{
gvwBigMemory.ItemsSource = Enumerable.Range(1, 100);
}
private void BtnNew_Click(object sender, RoutedEventArgs e)
{
gvwMinMemory.ItemsSource = Enumerable.Range(1, 100);
}
}
很簡單,就是讓 GridView 加載 100 個。
好,走起!
初始運行佔用 40.6 MB 內存,當然這個值可能下次運行就不一樣了,多少會有點波動。
接下來我們加載舊版:
令人喫驚,一瞬間就跑到 250.6 MB 內存了。
那我們再來看看新的版本,記得先將上面這個關掉,重新打開,否則影響結果。
新版:
44.6 MB!基本沒發生任何的變化。可見威力很強大,說明我們的代碼起作用了。
結語:
可以見到這點小小的優化能帶來多大的影響。另外由於 GridView 默認使用了虛擬化,所以實際上並沒有加載到 100
個,但是仍然可以見到舊版的內存佔用十分厲害, 所以根本沒法想象真真正正加載 100 個的時候有多壯觀。小小的
細節可能會引起巨大的變化,考慮到還有衆多 512 MB 內存的 Windows Phone 用戶,這點小小的細節仍然很有必
要去做的。