使用c# /WPF显示缩略图网格

本文关键字:略图 网格 显示 WPF 使用 | 更新日期: 2023-09-27 18:17:05

在我的c#/WPF应用程序中,我想从图像文件的目录树中显示图像缩略图的网格(目前是.bmp的,但最终是其他格式)。在未来,我可能希望允许用户点击缩略图查看更大的版本,或者将鼠标悬停在缩略图上查看一些技术细节,但现在我想做的就是显示缩略图。

图像的数量是不可预测的,我的指示是,如果屏幕上的图像太多,就启用滚动(而不是,比如说,缩小缩略图)。

我有一个递归例程来遍历树并识别要显示的文件…
   private bool WalkTree(String sRoot)
    {
        string sDirectoryName;
        string sFileName;
        int iDirectoryCount = 0;
        DirectoryInfo DirInfo;
        DirInfo = new DirectoryInfo(sRoot);
        // Get a list of all the files in this directory.
        foreach (FileInfo fi in DirInfo.GetFiles("*.bmp"))
        {
            sFileName = fi.Name;
            // DO SOMETHING WITH FILE FOUND HERE
        } 
        // Now get a list of all the subfolders in this directory.
        foreach (DirectoryInfo di in DirInfo.GetDirectories())
        {
            sDirectoryName = di.Name;
            WalkTree(sRoot + "''" + sDirectoryName);  //recurse!!
            iDirectoryCount++;
        }
        return true;
    }  // End WalkTree 

那么有什么好的方法可以做到这一点呢?我应该使用什么XAML控件来放置所有这些?一个网格?我能在其中添加行,并在递归发现更多文件时滚动吗?或者我应该遍历树两次—一次获取计数并配置页面中的行和列,第二次实际显示缩略图?还是我想错了?

我觉得这个问题在过去已经解决了很多次了,一定有一个规范的设计模式,但是我找不到。

使用c# /WPF显示缩略图网格

注释描述了如何做布局部分。这是相对容易的,使用什么解决方案取决于实际布局。UniformGrid很好。在这里使用ItemsControl就足够了:

<ItemsControl ItemsSource="{Binding Images}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid>
                <ScrollViewer>
                    <UniformGrid />
                </ScrollViewer>
            </Grid>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Image Source="{Binding}" Width="100" Height="100" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl> 

问题是:什么是Images,你绑定ItemsSource的属性?您所拥有的是具有文件名的字符串列表,您需要的是ImageSource s的集合。没有办法将图像加载到内存中。如果你不想让你的应用程序在大量的图片中消耗大量的内存,那么你仍然需要高效地完成它。一个选项是将图像缩放为缩略图。其次,当你缩放图像时,这是一个很好的机会来裁剪它们,以获得固定的长宽比,使网格看起来很漂亮。

Images需要是BitmapImage的集合,以便与Image控件的ImageSource属性绑定:

public ObservableCollection<BitmapImage> Images { get; set; }

这基本上就是// DO SOMETHING WITH FILE FOUND HERE:

的内容
var image = CreateBitmap(path);
var width = image.PixelWidth;
var height = image.PixelHeight;
// Crop image (cut the side which is too long)
var expectedHeightAtCurrentWidth = width*4.0/3.0;
var expectedWidthAtCurrentHeight = height*3.0/4.0;
var newWidth = Math.Min(expectedWidthAtCurrentHeight, width);
var newHeight = Math.Min(expectedHeightAtCurrentWidth, height);            
var croppedImage = CropImage(image, (int)newWidth, (int)newHeight);
// Scale to with of 100px
var ratio= 100.0 / newWidth;
var scaledImage = ScaleImage(croppedImage, ratio);
Images.Add(scaledImage);

使用以下函数来创建,缩放和裁剪图像:

private static BitmapImage CreateBitmap(string path)
{
    var bi = new BitmapImage();
    bi.BeginInit();
    bi.UriSource = new Uri(path);
    bi.EndInit();
    return bi;
}
private BitmapImage ScaleImage(BitmapImage original, double scale)
{
    var scaledBitmapSource = new TransformedBitmap();
    scaledBitmapSource.BeginInit();
    scaledBitmapSource.Source = original;
    scaledBitmapSource.Transform = new ScaleTransform(scale, scale);
    scaledBitmapSource.EndInit();
    return BitmapSourceToBitmap(scaledBitmapSource);
}
private BitmapImage CropImage(BitmapImage original, int width, int height)
{
    var deltaWidth = original.PixelWidth - width;
    var deltaHeight = original.PixelHeight - height;
    var marginX = deltaWidth/2;
    var marginY = deltaHeight/2;
    var rectangle = new Int32Rect(marginX, marginY, width, height);
    var croppedBitmap = new CroppedBitmap(original, rectangle);
    return BitmapSourceToBitmap(croppedBitmap);
}
private BitmapImage BitmapSourceToBitmap(BitmapSource source)
{
    var encoder = new PngBitmapEncoder();
    var memoryStream = new MemoryStream();
    var image = new BitmapImage();
    encoder.Frames.Add(BitmapFrame.Create(source));
    encoder.Save(memoryStream);
    image.BeginInit();
    image.StreamSource = new MemoryStream(memoryStream.ToArray());
    image.EndInit();
    memoryStream.Close();
    return image;
}