65.9K
CodeProject 正在变化。 阅读更多。
Home

WPF 中的 PhotoViewer 应用程序(Visual Studio 2008 Beta 2)

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.50/5 (7投票s)

2007年11月19日

CPOL

6分钟阅读

viewsIcon

45795

downloadIcon

1150

本文介绍如何在 Visual Studio 2008 中创建基于 WPF 的 PhotoViewer 应用程序。

引言

使用Windows Presentation Foundation (WPF)进行设计很有趣,而使用它开发功能齐全的应用程序则远不止有趣。实际上,这是一种探索.NET 3.5和WPF功能及类的冒险。尽管我写过几段XAML来领略它的辉煌,但这个应用程序确实让我大部分时间都经历了一场关于其伟大之处的过山车之旅,偶尔也会遇到一些陷阱。

如您所料,PhotoViewer应用程序旨在在一个画廊或文件夹中查看照片。它提供了缩放和导航功能。使用滑块缩放照片是一项简单易用的功能,而导航则可以灵活地从存储在文件夹中的一系列照片文件中选择一张照片。目前,PhotoViewer支持JPEG、GIF、PNG和BMP格式的图片文件。有趣的是,这个PhotoViewer应用程序是使用Visual Studio 2008 (Beta 2)开发的,仅使用了最少的C#语言功能。

在本文中,我想解释该应用程序是如何开发的,如何使用XAML代码设计交互式UI,以及如何为某些事件编写代码隐藏。源代码zip文件可与本文一起下载。总而言之,我希望这个应用程序对初学者学习WPF非常有帮助,本文也将为读者提供一种有趣的方式来学习WPF元素及其行为。

创建WPF Windows应用程序

打开Visual Studio 2008 (Beta 2),然后从“新建项目”对话框右侧面板的模板列表中选择WPF应用程序模板,创建一个新项目。

将项目命名为“PhotoViewer”,然后单击“确定”,您将在Visual Studio IDE中看到解决方案打开。在解决方案资源管理器中,您可以看到App.Xaml,这是任何WPF应用程序的主XAML文件。它实际上是继承自System.Windows.Application类的App类的表示。StartupUri元素表示应用程序的启动对象,在本例中是Window1.Xaml

<Application x:Class="PhotoViewer.App" 
    xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="Window1.xaml"> 

Window1.Xaml是您在解决方案资源管理器中看到的另一个文件,在创建项目时Visual Studio会自动放置它。在设计模式下打开文件,首先,从工具箱中放置一个堆积面板控件。

StackPanel是一个容器控件,用于放置多个控件,就像在WinForms应用程序的面板控件中一样。它支持水平和垂直方向以及更多功能,如资源、触发器、样式等。

InkCanvasImage控件拖放到stackpanel中。InkCanvas控件提供了一个绘图画布,您可以使用鼠标指针在其上绘制任何内容。在InkCanvas内部,Image控件完美地放置,并且应用于InkCanvas(其父控件)的变换也会应用于它本身。

<InkCanvas Width="300" Height="200" 
    Background="Bisque" Name="inkCanvas1">
<Image Source="{Binding ElementName=fnameTextBox, Path=Text}" Width="300" 
    Height="200" Name="image1" 
    HorizontalAlignment="Center" VerticalAlignment="Center" />
<InkCanvas.LayoutTransform>
<ScaleTransform 
CenterX="{Binding ElementName=inkCanvas1, Path=Width, 
    Converter={StaticResource centerXConv}}"
CenterY="{Binding ElementName=inkCanvas1, Path=Height, 
    Converter={StaticResource centerYConv}}" 
ScaleX ="{Binding ElementName=slider1, Path=Value}"
ScaleY="{Binding ElementName=slider1, Path=Value}"
>
</ScaleTransform>
</InkCanvas.LayoutTransform>
</InkCanvas>

缩放效果

LayoutTransform中,您将ScaleTransform元素及其属性CenterXCenterY(图像中心点的X和Y坐标)放在一起。设置这两个属性很重要,以便从图像中心进行缩放,从而提供均匀的缩放。另外两个属性ScaleXScaleY接受增量值,当用户移动滑块时,缩放的进行与此值成比例。

用于缩放的数据绑定

WPF中的数据绑定意味着从数据源(可以是XML文件、控件的属性或任何其他自定义对象)为属性(依赖属性是可绑定的)提供值。这种向控件提供值的功能称为绑定,当您更改属性值或触发事件时,它将生效。

CenterXCenterY属性绑定到InkCanvasWidth属性,并使用一个名为ZoomPercentageConverter的自定义类来计算中心点的坐标。此Converter类必须在StackPanel中预先定义,并继承自IValueConverter接口,该接口通常用于将值从一种类型转换为另一种类型。此转换器在XAML中为CenterXCenterY这两个属性指定。

<StackPanel.Resources>
<local:ZoomPercentageConverter x:Key="centerXConv" /> 
    <local:ZoomPercentageConverter x:Key="centerYConv" />
</StackPanel.Resources>

public class ZoomPercentageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)

{
double center = ((double) value) / 2;
return center;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

{
throw new NotSupportedException("Cannot convert back");
}
}

缩放限制验证

PhotoViewer窗口是可调整大小的,用户只能将InkCanvas内的图像缩放到窗口width的范围内。此处包含验证,通过在InkCanvasLayoutChanged事件处理程序中编写代码来检查限制,如下所示。

加载照片

// This constant used to check the boundaries while zooming
public const int PaddingFactor = 24; 
this.slider1.Value = 1; // Initial value
this.image1.Width = inkCanvas1.Width;
this.image1.Height = inkCanvas1.Height;

void inkCanvas1_LayoutUpdated(object sender, EventArgs e)
{
InkCanvas ic = (InkCanvas)App.Current.Windows[0].FindName("inkCanvas1");
if ((ic.DesiredSize.Width + PaddingFactor) >= this.ActualWidth)
{
MessageBox.Show("You cannot zoom it further beyond the width of the window. 
    Maximize the window and then try!!!", "ZoomLimit");
this.slider1.Value = 1;
}
}

PhotoViewer使用WinForms控件FolderBrowserDialog,从而成为一个混合应用程序。此Dialog用于选择用户希望从中查看照片的文件夹。用户选择文件夹后,所有图像文件都作为数组存储在PhotosCollection对象中。PhotosCollection是一个帮助类,用于收集图像文件并向所需的绑定控件提供当前图像索引和文件名的数据。

public class PhotosCollection 
{
int myindex;
string filename;
public static Array photofiles=null;

public int MyIndex
{
get{return myindex; }
set{myindex = value; }
}

public string FileName
{
get{return filename; }
set{filename = value; }
}

public PhotosCollection()
{
this.MyIndex = 0;
this.FileName = "";
}

public static int GetPhotoCollection()
{
FolderBrowserDialog fbd = new FolderBrowserDialog();
fbd.ShowNewFolderButton=false;
fbd.RootFolder = System.Environment.SpecialFolder.MyComputer;
fbd.ShowDialog();
if (fbd.SelectedPath == "")
{
MessageBox.Show("Click 'Select folder' to choose the folder containing photos 
    and then proceed!");
return 0; // No files or folder
}
else
{
photofiles = System.IO.Directory.GetFiles(fbd.SelectedPath, "*.jpg");
return photofiles.Length;
}
}
}

用于显示图像的数据绑定

XAML中Image元素的Source属性的绑定如下所示。

<Image Source="{Binding ElementName=fnameTextBox, Path=Text}" 
    Width="300" Height="200" Name="image1" 
    HorizontalAlignment="Center" VerticalAlignment="Center" />

绑定到textbox的值,该值存储照片文件的名称,用户通过单击“下一个”和“上一个”按钮进行导航。

<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBox Name="fname" Text="{Binding Source={StaticResource photo}, 
    Path=FileName}" Width="150" HorizontalAlignment="Center" />
<Button Height="22" Width="28" Foreground="Blue" 
    Name="btnNext" ToolTip="Next Photo">

<Button.BitmapEffect>
<BevelBitmapEffect BevelWidth="3" Smoothness="0.3" EdgeProfile="BulgedUp" />
</Button.BitmapEffect> &gt;
</Button>

<Button Height="22" Width="28" Foreground="Blue" 
    Name="btnPrevious" ToolTip="Previous Photo">
<Button.BitmapEffect>
<BevelBitmapEffect BevelWidth="3" Smoothness="0.3" 
    EdgeProfile="BulgedUp" />
</Button.BitmapEffect> &lt;
</Button>
<Button Height="22" Width="75" Foreground="Blue" 
    Name="btnSelect" ToolTip="Select Folder containing Photos">Select Folder</Button>

</StackPanel>

上面的XAML代码定义了第二个StackPanel,其中包含一个TextBox,三个Button;一个用于获取Previous Image文件,另一个用于获取Next文件;第三个用于从用户选择的文件夹加载照片。

TextBoxText属性绑定到您在PhotosCollection类中定义的自定义对象的FileName属性。

导航上一张和下一张图片

下面的代码显示了为NextPrevious按钮编写的事件处理程序。请注意,每次用户单击这两个按钮时,都会设置TextBox的值。

void btnPrevious_Click(object sender, RoutedEventArgs e)
{
if (pc.MyIndex <= 0)
return;
pc.MyIndex -= 1;
pc.FileName = PhotosCollection.photofiles.GetValue(pc.MyIndex).ToString();
fnameTextBox.SetValue(TextBox.TextProperty, pc.FileName);
} 

void btnNext_Click(object sender, RoutedEventArgs e)
{
if (pc.MyIndex >= PhotosCollection.photofiles.Length-1)
return;
pc.MyIndex += 1;
pc.FileName = PhotosCollection.photofiles.GetValue(pc.MyIndex).ToString();

fnameTextBox.SetValue(TextBox.TextProperty, pc.FileName);
}

就是这样。现在保存所有文件并生成解决方案,准备运行应用程序。

运行PhotoViewer应用程序

将zip文件下载到一个文件夹,然后使用Visual Studio 2008 Beta 2加载解决方案。目前,未在运行Visual Studio 2005 WPF Extensions的系统上进行测试。

应用程序启动时,会显示FolderBrowserDialog,用户必须选择存储照片文件的文件夹。

Screenshot - photo2.jpg

窗口出现并显示第一张照片。用户可以使用窗口底部的滑块来放大和缩小图像。最佳选择是最大化窗口到全屏大小并执行缩放操作。>按钮表示Next图像,<按钮表示Previous图像。要选择另一个文件夹来查看照片,请单击“选择文件夹”按钮。

奖励功能

Screenshot - photo1.jpg

您可以在InkCanvas上绘制任何您想要的东西,甚至可以在PhotoViewer应用程序中显示的图片之上绘制,以添加自己的标记并指向您旁边的那个家伙。嘿,不要在他自己的照片上添加额外的动画功能来让他困惑!

Screenshot - photo3.jpg

享受WPF!

历史

  • 2007年11月19日:初次发布
© . All rights reserved.