【问题标题】:Displaying a grid of thumbnails with C#/WPF使用 C#/WPF 显示缩略图网格
【发布时间】:2014-10-02 07:47:10
【问题描述】:

在我的 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 控件来放入所有这些?网格?我可以在其中添加行并使其在我递归并发现更多文件时滚动吗?或者我应该走两次树 - 一次是为了计数并配置我页面中的行和列,第二次是实际显示缩略图?还是我认为这一切都错了?

我觉得这个问题在过去一定已经解决了足够多的时间,所以肯定有一个规范的设计模式,但我找不到。

【问题讨论】:

  • 使用带有 UniformGrid 的 ListBox 作为其项目面板。
  • ... 然后在我进行时填充它,即在 WalkTree() 例程中进行 1 次传递?这听起来很合理。这里的其他人怎么看?有这方面的规范设计模式吗?
  • 填写一个列表,然后数据绑定到它。
  • @poke - 如果我进行数据绑定,不是所有图像都必须先加载到内存中吗?我知道如何对集合类和数据库进行数据绑定,但我不确定如何对目录树中的一组 n 图像文件进行数据绑定,其中既没有文件数也没有拓扑树在运行时被发现之前是已知的。
  • 这在很大程度上取决于您的数据绑定。例如,您可以使用文件名填充列表。这样,列表就不会占用太多内存。

标签: c# wpf


【解决方案1】:

cmets 描述了如何进行布局部分。它相对容易,使用什么解决方案取决于实际布局。 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 绑定到的属性?您拥有的是带有文件名的字符串列表,您需要的是ImageSources 的集合。无法将图像加载到内存中。如果您不希望您的应用程序在图像数量众多且很大时消耗大量内存,您仍然需要高效地执行此操作。一个选项是将图像缩放为缩略图。其次,当您缩放图像时,这也是裁剪它们以获得固定纵横比并使网格看起来不错的好机会。

此处的图像必须是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;
}

【讨论】:

  • 当我尝试粘贴 XAML 时,我得到了可怕的 System.Reflection.TargetInvocationException 异常已被调用的目标抛出。 粘贴后立即
  • 嗯,不知道。我一直在使用大部分代码。你能打包一个示例项目吗?那我去看看。
猜你喜欢
  • 2011-08-20
  • 2011-07-01
  • 1970-01-01
  • 2017-09-11
  • 1970-01-01
  • 2010-09-06
  • 2014-03-23
  • 2017-08-29
  • 2022-11-13
相关资源
最近更新 更多