第一个WP8程序,照相机

安装win8 64位,vs2013(包含wp8 sdk),百度各种得到学生开发者账户之后,终于可以试一下将自己的app部署到手机上的感觉了。

首先来个简单练练手的照相机功能

  • 照相

即从主界面进入到照相机界面,进行拍照,并对照片进行保存

  • 相册

对已拍照片管理,查看,删除


  • 界面设计

 MainPage.xaml

<phone:PhoneApplicationPage
    x:Class="OpenCamera.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True">
    
    <Grid>
        <Button Width="200" Height="200" Content="拍照" Click="ButtonBase_OnClick"></Button>
    </Grid>
   

</phone:PhoneApplicationPage>

MainPage.xaml.cs

public partial class MainPage : PhoneApplicationPage
    {
        // 构造函数
        public MainPage()
        {
            InitializeComponent();
        }

        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            this.NavigationService.Navigate(new Uri("/Camera.xaml", UriKind.RelativeOrAbsolute));
        }
    }

意思就很简单了,点击拍照,就进入照相界面

下面拍照界面Camera.xaml

<Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="200"/>
        </Grid.RowDefinitions>
        
        <Canvas>
            <Canvas.Background>
                <VideoBrush x:Name="VideoBrush"></VideoBrush>
            </Canvas.Background>
        </Canvas>
        
        <StackPanel Grid.Row="1">
            <Button Width="200" Height="200" Content="拍照" Click="ButtonBase_OnClick">
            </Button>
        </StackPanel>
    </Grid>

一个Canvas用于显示照相机捕捉到的图像
一个按钮,用于拍照

  • PhotoCamera

上MSDN查询:http://msdn.microsoft.com/zh-cn/library/microsoft.devices.photocamera(v=vs.92).aspx

备注中提到一些实用方法,其中提到了几个方法

OnNavigatedTo(导航到本页面时触发)

OnNavigatedFrom(离开本页面后触发)

OnNavigatingFrom(离开本页面之前触发)

在Camera.xaml.cs中依次重写了3个方法,都打上断点,看执行顺序,发现执行过程是  点击拍照按钮=》OnNavigatedTo=》点击返回=>OnNavigatingFrom=》OnNavigatedFrom

好,现在就明白了,在OnNavigatedTo中来初始化PhotoCamera对象实例,并捕捉图像到Canvas上

protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            _cam = new PhotoCamera(CameraType.Primary);

            VideoBrush.SetSource(_cam);
            
            base.OnNavigatedTo(e);
        }

好,此刻已按捺不住激动的心情了,编译,在模拟器中调试....,但是,程序出错。再次查看MSDN,发现这里

 有个东西需要设置一下

好,现在在编译,运行,模拟器中如下

心想,这该没有什么问题了吧,插上手机,部署

运行,果然能够显示图像了。有点小兴奋。

  • 拍照

 拍照就很简单了,调用一个方法即可

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            _cam.CaptureImage();
        } 
  • 保存照片

调用了CaptrueImage方法,本以为会返回照片,但是继续查看MSDN发现需要注册一个事件,来获得图片。

这里还用到了一个类MediaLibrary:http://msdn.microsoft.com/query/dev12.query?appId=Dev12IDEF1&l=ZH-CN&k=k(Microsoft.Xna.Framework.Media.MediaLibrary);k(TargetFrameworkMoniker-WindowsPhone,Version%3Dv8.0);k(DevLang-csharp)&rd=true

但是,这里需要注意一下,使用MediaLibrary来保存数据还需要这一步设置,如下图:

 这样,MediaLibrary才能正常使用(MSDN上没有说明这个:坑呀)

这里注册图片可用事件

protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
        {

            if (_cam != null)
            {
                _cam.Dispose();

                _cam.Initialized -= _cam_Initialized;
                _cam.CaptureCompleted -= _cam_CaptureCompleted;
                _cam.CaptureImageAvailable -= _cam_CaptureImageAvailable;
                _cam.CaptureThumbnailAvailable -= _cam_CaptureThumbnailAvailable;
            }

            base.OnNavigatingFrom(e);
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            _cam = new PhotoCamera(CameraType.Primary);
            _library = new MediaLibrary();

            VideoBrush.SetSource(_cam);

            _cam.Initialized += _cam_Initialized;
            _cam.CaptureCompleted += _cam_CaptureCompleted;//照相完成事件
            _cam.CaptureImageAvailable += _cam_CaptureImageAvailable;//图片可用事件
            _cam.CaptureThumbnailAvailable += _cam_CaptureThumbnailAvailable;//缩略图可用事件

            base.OnNavigatedTo(e);
        }

在OnNvaigatingForm中取消订阅事件
现在主要关注CaptureImageAvailable事件,该事件当照相生成的图片可以使用时触发,先不要关注注释部分的代码,这样就能将照片保存到手机相册里了

void _cam_CaptureImageAvailable(object sender, ContentReadyEventArgs e)
        {
            string fileName = string.Format("{0}-{1}-{2}", DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second) + ".jpg";

            _library.SavePictureToCameraRoll(fileName, e.ImageStream);

            #region 独立存储
            //string floderName = "Photo";

            //string fileName = string.Format("{0}-{1}-{2}", DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second) + ".jpg";

            //string fullName = Path.Combine(floderName, fileName);

            //using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
            //{
            //    if (!file.DirectoryExists(floderName))
            //    {
            //        file.CreateDirectory(floderName);
            //    }

            //    if (!file.FileExists(fullName))
            //    {
            //        using (IsolatedStorageFileStream fileStream = file.OpenFile(fullName, FileMode.Create, FileAccess.Write))
            //        {
            //            byte[] readBuffer = new byte[4069];
            //            int bytesRead = -1;

            //            while ((bytesRead = e.ImageStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
            //            {
            //                fileStream.Write(readBuffer, 0, bytesRead);
            //            }
            //        }
            //    }
            //} 
            #endregion

            e.ImageStream.Close();
        }

但是,我们要制作自己的相册,就需要独立存储。

  • 独立存储

独立存储这一块请看MSDN吧,http://msdn.microsoft.com/zh-cn/library/system.io.isolatedstorage.isolatedstoragefile(v=vs.110).aspx

注释部分的代码就是将图片保存到独立存储中,而不是手机的相册中。

  • 相册

相册就是将所拍的照片展示出来,暂时用ListBox实现

界面Photo.xaml

<Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--TitlePanel 包含应用程序的名称和页标题-->
        <StackPanel Grid.Row="0" Orientation="Horizontal" Margin="12,17,0,28">
            <TextBlock Text="相册" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
            <Image x:Name="TestImage"></Image>
        </StackPanel>

        <!--ContentPanel - 在此处放置其他内容-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <ListBox ItemsSource="{Binding AllPhoto}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <Image Source="{Binding}"></Image>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Grid>

Photo.xaml.cs

#region 字段
        private ObservableCollection<BitmapImage> _allPhoto; 
        #endregion

        #region 属性
        public ObservableCollection<BitmapImage> AllPhoto
        {
            get
            {
                return _allPhoto;
            }
            set
            {
                _allPhoto = value;
                OnPropertyChanged("AllPhoto");
            }
        } 
        #endregion

        public Photos()
        {
            InitializeComponent();

            this.DataContext = this;

            AllPhoto=new ObservableCollection<BitmapImage>();
        }


        #region 导航事件
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            using(IsolatedStorageFile file=IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (file.DirectoryExists("PhotoTH"))
                {
                    string[] fileNames = file.GetFileNames("/PhotoTH/");

                    foreach (var fileName in fileNames)
                    {
                        using (IsolatedStorageFileStream fileStream = file.OpenFile("/PhotoTH/" + fileName, FileMode.Open))
                        {
                            BitmapImage bitmapImage=new BitmapImage();
                            bitmapImage.SetSource(fileStream);

                            AllPhoto.Add(bitmapImage);
                        }
                    }
                }
            }

            base.OnNavigatedTo(e);
        } 
        #endregion

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
            }
        }

这样就能将图所拍照片的图片显示出来了。

  • 相片删除

 这里一般都是长按相片,然后弹出菜单可以选择“删除”选项,这个就用到类似于WPF的ContextMenu,但是弄死在WP8项目中就是没有ContextMenu,各种百度之后,得知需要引用MicroSoft.Phone.Control.ToolKit

该Dll在http://www.nuget.org/packages/wptoolkit

下载之后,我们的Photo界面就要稍微修改,下面是修改之后的Photo.xaml

<!--LayoutRoot 是包含所有页面内容的根网格-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--TitlePanel 包含应用程序的名称和页标题-->
        <StackPanel Grid.Row="0" Orientation="Horizontal" Margin="12,17,0,28">
            <TextBlock Text="相册" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
            <Image x:Name="TestImage"></Image>
        </StackPanel>

        <!--ContentPanel - 在此处放置其他内容-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <ListBox ItemsSource="{Binding AllPhoto}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Button Width="200" Height="200">
                            
                            <Button.Template>
                                <ControlTemplate TargetType="Button">
                                    <Grid>
                                        <Grid.Background>
                                            <ImageBrush ImageSource="{Binding Source}">
                                                <ImageBrush.RelativeTransform>
                                                    <CompositeTransform CenterX="0.5" CenterY="0.5" Rotation="90"></CompositeTransform>
                                                </ImageBrush.RelativeTransform>
                                            </ImageBrush>
                                        </Grid.Background>
                                    </Grid>
                                </ControlTemplate>
                            </Button.Template>
                            
                            
                            <toolkit:ContextMenuService.ContextMenu>
                                <toolkit:ContextMenu>
                                    <toolkit:MenuItem Header="删除" Click="MenuItem_OnClick" Tag="{Binding}"></toolkit:MenuItem>
                                </toolkit:ContextMenu>
                            </toolkit:ContextMenuService.ContextMenu>
                        </Button>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Grid>

这里需要注意一下,一开始我们直接绑定的BitmapImage来获取图片,但是这样在删除的时候,不仅要从集合中删除BitmapImage,还要从独立存储中删除该图片,而BitmapImage获取UriSource不到(希望有同学解释一下),这里只能变通一下,创建类Photo

public class Photo
    {
        public string FileName
        {
            get;
            set;
        }

        public string FullName
        {
            get;
            set;
        }

        public BitmapImage Source
        {
            get;
            set;
        }
    }

这样我们集合中存储的就是该Photo类型的变量,有文件名,还有包含整个路径的文件名,还有BitmapImage对象,这样删除的时候,就非常容易了,下面是删除图片的代码

private void MenuItem_OnClick(object sender, RoutedEventArgs e)
        {
            MenuItem item = sender as MenuItem;
            Photo photo = item.Tag as Photo;

            AllPhoto.Remove(photo);

            using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (file.FileExists(photo.FullName))
                {
                    file.DeleteFile(photo.FullName);
                }
            }
        }

好了,至此,大致的功能就完成了。以为是一个很简单的东西,其实其中还包含了一些未知的东西,如独立存储,还有配置文件(勾选相机功能可用的那个)等等,目前也就是学了个大概,后面还要慢慢深入。

 Demo:http://files.cnblogs.com/HelloMyWorld/OpenCamera.rar

人生还有无限可能,不要放弃努力。

原文地址:https://www.cnblogs.com/HelloMyWorld/p/3797767.html