源项目地址:https://github.com/Microsoft/...
以下是把样例转换为简要说明,同时给出实际运行效果及关键代码剖析:

PhotoViewerDemo图片信息查询器

clipboard.png
主要实现功能:
1 根据选定目录自动加载图片或缩略图,并显示选中图片的元素据信息
2 选中图片进行编辑、缩放、旋转、黑白变
3 对图片容器大小进行自动缩放

  • 数据模型类

图片类

/// <summary>
///     This class describes a single photo - its location, the image and
///     the metadata extracted from the image.
/// </summary>
public class Photo
{
    private readonly Uri _source;

    public Photo(string path)
    {
        Source = path;
        _source = new Uri(path);
        Image = BitmapFrame.Create(_source);
        Metadata = new ExifMetadata(_source);
    }

    public string Source { get; }
    public BitmapFrame Image { get; set; }
    public ExifMetadata Metadata { get; }

    public override string ToString() => _source.ToString();
}
  • 视图模型类

重新选择目录就更新图片集Update(),

/// <summary>
///     This class represents a collection of photos in a directory.
/// </summary>
public class PhotoCollection : ObservableCollection<Photo>
{
    private DirectoryInfo _directory;

    public PhotoCollection()
    {
    }

    public PhotoCollection(string path) : this(new DirectoryInfo(path))
    {
    }

    public PhotoCollection(DirectoryInfo directory)
    {
        _directory = directory;
        Update();
    }

    public string Path
    {
        set
        {
            _directory = new DirectoryInfo(value);
            Update();
        }
        get { return _directory.FullName; }
    }

    public DirectoryInfo Directory
    {
        set
        {
            _directory = value;
            Update();
        }
        get { return _directory; }
    }

    private void Update()
    {
        Clear();
        try
        {
            foreach (var f in _directory.GetFiles("*.jpg"))
                Add(new Photo(f.FullName));
        }
        catch (DirectoryNotFoundException)
        {
            MessageBox.Show("No Such Directory");
        }
    }
}
  • 视图界面xaml

ListBox缩略图阴影由一个Border的错位及阴影来制成效果

<!-- Drop Shadow -->
<Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CornerRadius="4"
        Background="#44000000">
    <Border.RenderTransform>
        <TranslateTransform X="5" Y="5" />
    </Border.RenderTransform>
    <Border.BitmapEffect>
        <BlurBitmapEffect Radius="8" />
    </Border.BitmapEffect>
</Border>

缩略图绑定到BitmapFrame 关联的缩略图图像
缩略图绑定为位图元素据生成日期

<Image Source="{Binding Image.Thumbnail}" />
    <Label Content="{Binding Metadata.DateImageTaken}">

ListBox的容器模板改为WrapPanel,缩放时自动排列其中内容,容器大小绑定到滑动条

<!-- Main photo catalog view -->
<Style TargetType="{x:Type ListBox}" x:Key="PhotoListBoxStyle">
    <Setter Property="Foreground" Value="White" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBox}">
                <WrapPanel Margin="5" IsItemsHost="True" Orientation="Horizontal"
                           ItemHeight="{Binding ElementName=ZoomSlider, Path='Value'}"
                           ItemWidth="{Binding ElementName=ZoomSlider, Path='Value'}"
                           VerticalAlignment="Top" HorizontalAlignment="Stretch" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
  • 后端代码逻辑

初始化主界面时查找资源,进行类型ObjectDataProvider可空转换为PhotoCollection的图片集数据,得到引用。并设置其目录路径

<Application.Resources>
    <ObjectDataProvider x:Key="Photos" ObjectType="{x:Type local:PhotoCollection}" />
</Application.Resources>
public PhotoCollection Photos;

public MainWindow()
{
    InitializeComponent();
    Photos = (PhotoCollection) (Application.Current.Resources["Photos"] as ObjectDataProvider)?.Data;
    Photos.Path = Environment.CurrentDirectory + "\\images";
}

双击图片显示编辑窗口,同时传递选择项这个参数

private void OnPhotoClick(object sender, RoutedEventArgs e)
    {
        var pvWindow = new PhotoViewer {SelectedPhoto = (Photo) PhotosListBox.SelectedItem};
        pvWindow.Show();
    }

图片编辑窗口的初始化,过程直接简单有效

public PhotoViewer()
{
    InitializeComponent();
}

public Photo SelectedPhoto { get; set; }

private void WindowLoaded(object sender, RoutedEventArgs e)
{
    ViewedPhoto.Source = SelectedPhoto.Image;
    ViewedCaption.Content = SelectedPhoto.Source;
}

旋转图片实现代码
新建一个CachedBitmap的新实例,TransformedBitmap旋转后再赋值SelectedPhoto的图像类型BitmapFrame,最后重新绑定

private void Rotate(object sender, RoutedEventArgs e)
{
    BitmapSource img = SelectedPhoto.Image;

    var cache = new CachedBitmap(img, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
    SelectedPhoto.Image = BitmapFrame.Create(new TransformedBitmap(cache, new RotateTransform(90.0)));

    ViewedPhoto.Source = SelectedPhoto.Image;
}

裁剪放大实现代码
根据裁剪尺寸实例化一个新位图进行更新绑定

private void Crop(object sender, RoutedEventArgs e)
{
    BitmapSource img = SelectedPhoto.Image;
    var halfWidth = img.PixelWidth/2;
    var halfHeight = img.PixelHeight/2;
    SelectedPhoto.Image =
        BitmapFrame.Create(new CroppedBitmap(img,
            new Int32Rect((halfWidth - (halfWidth/2)), (halfHeight - (halfHeight/2)), halfWidth, halfHeight)));

    ViewedPhoto.Source = SelectedPhoto.Image;
}

黑白图片实现代码
通过像素格式转换FormatConvertedBitmap对 位图的像素格式化:调色板、alpha通道阈值

private void BlackAndWhite(object sender, RoutedEventArgs e)
{
    BitmapSource img = SelectedPhoto.Image;
    SelectedPhoto.Image =
        BitmapFrame.Create(new FormatConvertedBitmap(img, PixelFormats.Gray8, BitmapPalettes.Gray256, 1.0));

    ViewedPhoto.Source = SelectedPhoto.Image;
}

以上----
PS:元素据BitmapMetadata 类:
与图像关联的元数据是描述图像(但不一定会显示图像)的数据。 每种支持的位图图像格式都采用不同的方式处理元数据,但读取和写入元数据的功能相同。

Windows Presentation Foundation (WPF) 支持以下图像元数据架构:可交换图像文件 (Exif)、tEXt(PNG 文本数据)、图像文件目录 (IFD)、国际新闻通信委员会 (IPTC) 以及可扩展元数据平台 (XMP)。

如果 BitmapMetadata 是由通过使用 BitmapDecoder 获取的 BitmapFrame 公开的,则默认情况下它是只读的,并且可变操作将引发异常。 如果它是由包装另一个 BitmapSource 的 BitmapFrame 公开的,则它在构造上是可变的。

可以使用 SetQuery 和 GetQuery 方法来构造和读取元数据查询。


李志玮
22 声望34 粉丝

求索~~