فهرست منبع

1.实现设置页面布局及逻辑;
2.实现主页页面布局;
3.添加串口工具类;
4.添加全局应用单例工具类。

Yumin 6 سال پیش
والد
کامیت
f35867e94b

+ 212 - 0
.gitignore

@@ -0,0 +1,212 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+build/
+bld/
+[Bb]in/
+[Oo]bj/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# DNX
+project.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+## TODO: Comment the next line if you want to checkin your
+## web deploy settings but do note that will include unencrypted
+## passwords
+#*.pubxml
+
+*.publishproj
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+
+# Windows Azure Build Output
+csx/
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+orleans.codegen.cs
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# LightSwitch generated files
+GeneratedArtifacts/
+_Pvt_Extensions/
+ModelManifest.xml

+ 12 - 0
FaceChange/App.config

@@ -1,6 +1,18 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <configuration>
+    <configSections>
+        <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
+            <section name="FaceChange.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
+        </sectionGroup>
+    </configSections>
     <startup> 
         <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
     </startup>
+    <userSettings>
+        <FaceChange.Properties.Settings>
+            <setting name="Faces" serializeAs="String">
+                <value />
+            </setting>
+        </FaceChange.Properties.Settings>
+    </userSettings>
 </configuration>

+ 121 - 0
FaceChange/AppContext.cs

@@ -0,0 +1,121 @@
+using System;
+using System.Collections.Generic;
+using System.IO.Ports;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace FaceChange
+{
+    class AppContext
+    {
+        // 串口类
+        SerialPort serialPort = null;
+        // 打开串口标志位
+        public bool portOpen = false;
+        // Data
+        private List<FaceInfo> faces = new List<FaceInfo>();
+
+        private static AppContext appContext;
+
+        private AppContext() { }
+
+        public static AppContext GetAppContext()
+        {
+            return appContext ?? (appContext = new AppContext());
+        }
+
+        public SerialPort SerialPort
+        {
+            set
+            {
+                if (value == null)
+                {
+                    portOpen = false;
+                    MainWindow.Status.Content = "未连接";
+                    MainWindow.Status.ToolTip = "设备未连接";
+                    MainWindow.Status.Foreground = new SolidColorBrush(Colors.Red);
+                }
+                else
+                {
+                    portOpen = true;
+                    MainWindow.Status.Content = "已连接";
+                    MainWindow.Status.ToolTip = "设备已连接";
+                    MainWindow.Status.Foreground = new SolidColorBrush(Colors.Green);
+                }
+                serialPort = value;
+            }
+            get
+            {
+                return serialPort;
+            }
+        }
+
+        public bool PortOpen
+        {
+            set
+            {
+                portOpen = value;
+            }
+            get
+            {
+                return portOpen;
+            }
+        }
+
+        public List<FaceInfo> FaceInfos
+        {
+            set
+            {
+                faces = value;
+            }
+            get
+            {
+                // 读取配置
+                var prop = Properties.Settings.Default.Faces;
+                if (prop != null && !prop.Trim().Equals(""))
+                {
+                    var faceStringList = prop.Split('|').ToList();
+                    foreach (var faceString in faceStringList)
+                    {
+                        var fileName = faceString.Substring(
+                            faceString.LastIndexOf(@"\", StringComparison.Ordinal) + 1);
+                        try
+                        {
+                            faces.Add(new FaceInfo(
+                                new BitmapImage(new Uri(faceString)),
+                                fileName,
+                                faceString.Replace(fileName, @"")));
+                        }
+                        catch (Exception exception)
+                        {
+                            Console.WriteLine(exception);
+                        }
+                    }
+                }
+                return faces;
+            }
+        }
+
+        public void DataReceived(object sender, SerialDataReceivedEventArgs e)
+        {
+            try
+            {
+                // Comm.BytesToRead 中为要读入的字节长度
+                int len = serialPort.BytesToRead;
+                Byte[] readBuffer = new Byte[len];
+                // 将数据读入缓存
+                serialPort.Read(readBuffer, 0, len);
+                // 处理 readBuffer 中的数据,自定义处理过程
+                string msg = Encoding.Default.GetString(readBuffer, 0, len);
+                Console.WriteLine(msg);
+            }
+            catch (Exception ex)
+            {
+                MessageBox.Show("接收返回消息异常!具体原因:" + ex.Message, "错误提示");
+            }
+        }
+    }
+}

+ 3 - 1
FaceChange/FaceChange.csproj

@@ -57,6 +57,9 @@
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
     </ApplicationDefinition>
+    <Compile Include="AppContext.cs" />
+    <Compile Include="FaceInfo.cs" />
+    <Compile Include="SerialTool.cs" />
     <Compile Include="SettingsWindow.xaml.cs">
       <DependentUpon>SettingsWindow.xaml</DependentUpon>
     </Compile>
@@ -118,7 +121,6 @@
   <ItemGroup>
     <None Include="App.config" />
   </ItemGroup>
-  <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.

+ 18 - 0
FaceChange/FaceInfo.cs

@@ -0,0 +1,18 @@
+using System.Windows.Media;
+
+namespace FaceChange
+{
+    class FaceInfo
+    {
+        public ImageSource Face { set; get; }
+        public string Name { set; get; }
+        public string Path { set; get; }
+
+        public FaceInfo(ImageSource face, string name, string path)
+        {
+            this.Face = face;
+            this.Name = name;
+            this.Path = path;
+        }
+    }
+}

+ 7 - 5
FaceChange/MainWindow.xaml

@@ -5,15 +5,17 @@
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:local="clr-namespace:FaceChange"
         mc:Ignorable="d"
-        Title="魔幻变脸" Height="500" Width="500" KeyDown="Window_KeyDown">
+        Title="魔幻变脸" Height="500" Width="500" KeyDown="Window_KeyDown" ResizeMode="CanMinimize" Loaded="Window_Loaded">
     <Grid>
-        <ToolBar x:Name="toolBar" VerticalAlignment="Top" Padding="0,0" Height="32">
-            <Button x:Name="settings" Content="&#xF013; 设置" ToolTip="设置" Style="{StaticResource FontAwesome}" Click="settings_Click" VerticalAlignment="Stretch"/>
+        <ToolBar x:Name="ToolBar" VerticalAlignment="Top" Padding="0,0" Height="32">
+            <Button x:Name="Settings" Content="&#xF013; 设置" ToolTip="设置" Style="{StaticResource FontAwesome}" Click="Settings_Click" VerticalAlignment="Stretch"/>
             <Separator/>
-            <Button x:Name="fullScreen" Content="&#xF31E; 全屏" ToolTip="全屏" Style="{StaticResource FontAwesome}" Click="fullScreen_Click" VerticalAlignment="Stretch"/>
+            <Button x:Name="FullScreen" Content="&#xF31E; 全屏" ToolTip="全屏" Style="{StaticResource FontAwesome}" Click="FullScreen_Click" VerticalAlignment="Stretch"/>
+            <Separator/>
+            <Label x:Name="PortStatus" Content="未连接" ToolTip="设备连接" Foreground="Red"/>
         </ToolBar>
         <Viewbox HorizontalAlignment="Center">
-            <Image Source="{Binding ImageSource}" Stretch="UniformToFill" />
+            <Image x:Name="Face" Stretch="UniformToFill" />
         </Viewbox>
     </Grid>
 </Window>

+ 27 - 10
FaceChange/MainWindow.xaml.cs

@@ -1,6 +1,10 @@
 using System.Windows;
 using System.Windows.Input;
 using System.Windows.Media;
+using System.Windows.Controls;
+using System.Windows.Media.Imaging;
+using System;
+using System.Collections.Generic;
 
 namespace FaceChange
 {
@@ -9,27 +13,42 @@ namespace FaceChange
     /// </summary>
     public partial class MainWindow : Window
     {
+        AppContext appContext = AppContext.GetAppContext();
+        SerialTool serialTool = SerialTool.GetSerialTool();
+
+        private List<FaceInfo> faces;
+
+        public static Label Status;
         private DrawingImage imageSource;
 
         public MainWindow()
         {
             InitializeComponent();
+
+            faces = appContext.FaceInfos;
+        }
+
+        private void Window_Loaded(object sender, RoutedEventArgs e)
+        {
+            Status = PortStatus;
         }
 
         private void Window_KeyDown(object sender, KeyEventArgs e)
         {
             if (e.Key == System.Windows.Input.Key.Escape)
             {
-                toolBar.Visibility = Visibility.Visible;
+                ToolBar.Visibility = Visibility.Visible;
                 Thickness thickness = new Thickness();
                 thickness.Top = 30;
                 //mediaElement.Margin = thickness;
                 this.WindowState = WindowState.Normal;
                 this.WindowStyle = WindowStyle.SingleBorderWindow;
             }
-            if (e.Key == System.Windows.Input.Key.Space)
+            if (e.Key == Key.Space)
             {
-                
+                int index = new Random().Next(0, faces.Count);
+                Console.WriteLine(index);
+                Face.Source = faces[index].Face;
             }
         }
 
@@ -37,21 +56,19 @@ namespace FaceChange
         {
             get
             {
-                return this.imageSource;
+                return imageSource;
             }
         }
 
-        private void settings_Click(object sender, RoutedEventArgs e)
+        private void Settings_Click(object sender, RoutedEventArgs e)
         {
             new SettingsWindow().ShowDialog();
-            //imageSource.Drawing = new DrawingImage();
         }
 
-        private void fullScreen_Click(object sender, RoutedEventArgs e)
+        private void FullScreen_Click(object sender, RoutedEventArgs e)
         {
-            toolBar.Visibility = Visibility.Hidden;
-            Thickness thickness = new Thickness();
-            thickness.Top = 0;
+            ToolBar.Visibility = Visibility.Hidden;
+            Thickness thickness = new Thickness {Top = 0};
             //image.Margin = thickness;
             this.Topmost = false;
             this.WindowStyle = WindowStyle.None;

+ 25 - 17
FaceChange/Properties/Settings.Designer.cs

@@ -1,30 +1,38 @@
 //------------------------------------------------------------------------------
 // <auto-generated>
-//     This code was generated by a tool.
-//     Runtime Version:4.0.30319.42000
+//     此代码由工具生成。
+//     运行时版本:4.0.30319.42000
 //
-//     Changes to this file may cause incorrect behavior and will be lost if
-//     the code is regenerated.
+//     对此文件的更改可能会导致不正确的行为,并且如果
+//     重新生成代码,这些更改将会丢失。
 // </auto-generated>
 //------------------------------------------------------------------------------
 
-namespace FaceChange.Properties
-{
-
-
+namespace FaceChange.Properties {
+    
+    
     [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
-    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
-    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
-    {
-
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+        
         private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
-
-        public static Settings Default
-        {
-            get
-            {
+        
+        public static Settings Default {
+            get {
                 return defaultInstance;
             }
         }
+        
+        [global::System.Configuration.UserScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("")]
+        public string Faces {
+            get {
+                return ((string)(this["Faces"]));
+            }
+            set {
+                this["Faces"] = value;
+            }
+        }
     }
 }

+ 7 - 5
FaceChange/Properties/Settings.settings

@@ -1,7 +1,9 @@
 <?xml version='1.0' encoding='utf-8'?>
-<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
-  <Profiles>
-    <Profile Name="(Default)" />
-  </Profiles>
-  <Settings />
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="FaceChange.Properties" GeneratedClassName="Settings">
+  <Profiles />
+  <Settings>
+    <Setting Name="Faces" Type="System.String" Scope="User">
+      <Value Profile="(Default)" />
+    </Setting>
+  </Settings>
 </SettingsFile>

+ 158 - 0
FaceChange/SerialTool.cs

@@ -0,0 +1,158 @@
+using System;
+using System.Collections.Generic;
+using System.IO.Ports;
+
+namespace FaceChange
+{
+    internal class SerialTool
+    {
+        private static SerialTool serialTool = null;
+
+        private SerialTool() { }
+
+        public static SerialTool GetSerialTool()
+        {
+            return serialTool ?? (serialTool = new SerialTool());
+        }
+
+        public List<string> FindPort()
+        {
+            var portList = new List<string>();
+            for (var i = 0; i < 16; i++)
+            {
+                try
+                {
+                    var serialPort = new SerialPort("COM" + (i + 1).ToString());
+                    serialPort.Open();
+                    serialPort.Close();
+                    portList.Add("COM" + (i + 1).ToString());
+                }
+                catch (Exception)
+                {
+                    continue;
+                }
+            }
+            return portList;
+        }
+
+        public delegate void SerialListener(object sender, SerialDataReceivedEventArgs e);
+
+        public SerialPort OpenPort(string portName, string baudRate, string dataBits, string stopBits, string parity, SerialListener serialListener)
+        {
+            // 检测串口设置
+            if (portName == "" || baudRate == "" || dataBits == "" || stopBits == "" || parity == "")
+            {
+                // MessageBox.Show("串口未设置!", "错误提示");
+                return null;
+            }
+            // 设置串口
+            var serialPort = SetPortProperty(portName, baudRate, dataBits, stopBits, parity, serialListener);
+            // 打开串口
+            try
+            {
+                serialPort.Open();
+                return serialPort;
+            }
+            catch (Exception)
+            {
+                // MessageBox.Show("串口无效或已被占用!", "错误提示");
+                return null;
+            }
+        }
+
+        private SerialPort SetPortProperty(string portName, string baudRate, string dataBits, string stopBits, string parity, SerialListener serialListener)
+        {
+            // 设置串口的属性
+            var serialPort = new SerialPort();
+            // 设置串口名
+            serialPort.PortName = portName.Trim();
+            // 设置串口的波特率
+            serialPort.BaudRate = Convert.ToInt32(baudRate.Trim());
+            // 设置停止位
+            float f = Convert.ToSingle(stopBits.Trim());
+            if (f == 0)
+            {
+                serialPort.StopBits = StopBits.None;
+            }
+            else if (f == 1.5)
+            {
+                serialPort.StopBits = StopBits.OnePointFive;
+            }
+            else if (f == 1)
+            {
+                serialPort.StopBits = StopBits.One;
+            }
+            else if (f == 2)
+            {
+                serialPort.StopBits = StopBits.Two;
+            }
+            else
+            {
+                serialPort.StopBits = StopBits.One;
+            }
+            // 设置数据位 
+            serialPort.DataBits = Convert.ToInt16(dataBits.Trim());
+            // 设置奇偶校验位
+            string s = parity.Trim();
+            if (String.Compare(s, "无", StringComparison.Ordinal) == 0)
+            {
+                serialPort.Parity = Parity.None;
+            }
+            else if (String.Compare(s, "奇校验", StringComparison.Ordinal) == 0)
+            {
+                serialPort.Parity = Parity.Odd;
+            }
+            else if (s.CompareTo("偶校验") == 0)
+            {
+                serialPort.Parity = Parity.Even;
+            }
+            else
+            {
+                serialPort.Parity = Parity.None;
+            }
+            // 设置超时读取时间
+            serialPort.ReadTimeout = -1;
+            serialPort.RtsEnable = true;
+            // 定义 DataReceived 事件,当串口收到数据后触发事件
+            serialPort.DataReceived += new SerialDataReceivedEventHandler(serialListener);
+            return serialPort;
+        }
+
+        public bool ClosePort(SerialPort serialPort)
+        {
+            // 关闭串口
+            try
+            {
+                serialPort.Close();
+                return true;
+            }
+            catch (Exception)
+            {
+                return false;
+            }
+        }
+
+        public bool SendToPort(SerialPort serialPort, string data)
+        {
+            // 写串口数据
+            if (serialPort.IsOpen)
+            {
+                try
+                {
+                    serialPort.WriteLine(data);
+                    return true;
+                }
+                catch (Exception)
+                {
+                    // MessageBox.Show("发送数据时发生错误!", "错误提示");
+                    return false;
+                }
+            }
+            else
+            {
+                // MessageBox.Show("串口未打开!", "错误提示");
+                return false;
+            }
+        }
+    }
+}

+ 39 - 15
FaceChange/SettingsWindow.xaml

@@ -5,12 +5,18 @@
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:local="clr-namespace:FaceChange"
         mc:Ignorable="d"
-        Title="设置" Height="500" Width="500" ResizeMode="NoResize">
+        Title="设置" Height="500" Width="500" ResizeMode="NoResize" Loaded="Window_Loaded" Closing="Window_Closing">
     <Grid>
         <GroupBox Header="连接设备" Margin="10,10,10,0" VerticalAlignment="Top" Height="66">
             <Grid Margin="10,10,10,10">
                 <Label Content="设备名称" HorizontalAlignment="Left" Height="24"/>
-                <ComboBox x:Name="comboBox" Margin="64,0,0,0" Height="24" VerticalAlignment="Top" HorizontalAlignment="Left" Width="230">
+                <ComboBox x:Name="Devices" Margin="64,0,0,0" Height="24" VerticalAlignment="Top" HorizontalAlignment="Left" Width="230" BorderBrush="White">
+                    <ComboBox.Background>
+                        <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
+                            <GradientStop Color="#FFF0F0F0" Offset="0"/>
+                            <GradientStop Color="White" Offset="1"/>
+                        </LinearGradientBrush>
+                    </ComboBox.Background>
                     <ComboBox.RenderTransform>
                         <TransformGroup>
                             <ScaleTransform/>
@@ -20,22 +26,40 @@
                         </TransformGroup>
                     </ComboBox.RenderTransform>
                 </ComboBox>
-                <Button x:Name="button" Content="扫描设备" Margin="299,0,0,0" Width="64" Height="24" HorizontalAlignment="Left"/>
-                <Button x:Name="button1" Content="连接设备" Margin="368,0,0,0" Width="64" Height="24" HorizontalAlignment="Left"/>
+                <Button x:Name="Scan" Content="扫描设备" Margin="299,0,0,0" Width="64" Height="24" HorizontalAlignment="Left" Background="White" Foreground="Blue" Click="Scan_Click"/>
+                <Button x:Name="Connect" Content="连接设备" Margin="368,0,0,0" Width="64" Height="24" HorizontalAlignment="Left" Foreground="Lime" Background="White" Click="Connect_Click"/>
             </Grid>
         </GroupBox>
-        <GroupBox x:Name="groupBox" Header="脸谱列表" Margin="10,82,10,10">
+        <GroupBox Header="脸谱列表" Margin="10,82,10,10">
             <Grid Margin="10,10,10,10">
-                <Button x:Name="delete" Content="删除" HorizontalAlignment="Left" Margin="385,274,0,0" VerticalAlignment="Top" Width="47" Height="24" Background="White" Foreground="Red"/>
-                <Button x:Name="add" Content="添加" HorizontalAlignment="Left" Margin="385,303,0,0" VerticalAlignment="Top" Width="47" Height="24" Background="White" Foreground="Lime"/>
-                <ListView x:Name="listView" Margin="0,0,62,0">
-                    <ListView.View>
-                        <GridView>
-                            <GridViewColumn Header="脸谱" Width="72"/>
-                            <GridViewColumn Header="路径" Width="300"/>
-                        </GridView>
-                    </ListView.View>
-                </ListView>
+                <Button x:Name="Delete" Content="删除" HorizontalAlignment="Left" Margin="385,245,0,0" VerticalAlignment="Top" Width="47" Height="24" Background="White" Foreground="Red" Click="Delete_Click"/>
+                <Button x:Name="DeleteALL" Content="清空" HorizontalAlignment="Left" Margin="385,274,0,0" VerticalAlignment="Top" Width="47" Height="24" Background="White" Foreground="Red" Click="DeleteALL_Click"/>
+                <Button x:Name="Add" Content="添加" HorizontalAlignment="Left" Margin="385,303,0,0" VerticalAlignment="Top" Width="47" Height="24" Background="White" Foreground="Lime" Click="Add_Click"/>
+                <ListBox x:Name="ListBox" ItemsSource="{Binding}" Grid.Column="2" Margin="0,0,62,0">
+                    <ListBox.ItemTemplate>
+                        <DataTemplate>
+                            <Grid HorizontalAlignment="Center">
+                                <Grid.ColumnDefinitions>
+                                    <ColumnDefinition Width="80"/>
+                                    <ColumnDefinition Width="255"/>
+                                </Grid.ColumnDefinitions>
+                                <Image Source="{Binding Face}"  Grid.Column="0" Width="80" Height="80" HorizontalAlignment="Left" Stretch="UniformToFill" StretchDirection="DownOnly"/>
+                                <Grid Grid.Column="1" Height="80" VerticalAlignment="Center" Margin="12,0,0,0">
+                                    <Grid.RowDefinitions>
+                                        <RowDefinition Height="38"></RowDefinition>
+                                        <RowDefinition Height="4"></RowDefinition>
+                                        <RowDefinition Height="38"></RowDefinition>
+                                    </Grid.RowDefinitions>
+                                    <TextBlock Text="{Binding Name}" Grid.Row="0" HorizontalAlignment="Left" VerticalAlignment="Bottom"  FontSize="12" TextWrapping="Wrap"/>
+                                    <TextBlock Text="" Grid.Row="1"/>
+                                    <TextBlock Text="{Binding Path}" Grid.Row="2" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="12" TextWrapping="Wrap"/>
+                                </Grid>
+                            </Grid>
+                        </DataTemplate>
+                    </ListBox.ItemTemplate>
+                </ListBox>
+                <Label Content="数量" HorizontalAlignment="Left" Margin="385,0,0,0" VerticalAlignment="Top" FontSize="10"/>
+                <Label x:Name="FaceCount" Content="0" ToolTip="最多9个" Margin="385,23,0,0" FontSize="10" Height="22" VerticalAlignment="Top" HorizontalAlignment="Left" Width="47"/>
             </Grid>
         </GroupBox>
     </Grid>

+ 198 - 7
FaceChange/SettingsWindow.xaml.cs

@@ -1,16 +1,14 @@
 using System;
 using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.IO.Ports;
 using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
-using System.Windows.Input;
+using System.Windows.Forms;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
-using System.Windows.Shapes;
 
 namespace FaceChange
 {
@@ -19,9 +17,202 @@ namespace FaceChange
     /// </summary>
     public partial class SettingsWindow : Window
     {
+        AppContext appContext = AppContext.GetAppContext();
+        SerialTool serialTool = SerialTool.GetSerialTool();
+
+        private List<FaceInfo> faces;
+
         public SettingsWindow()
         {
             InitializeComponent();
+
+            faces = appContext.FaceInfos;
+        }
+
+        private void Window_Loaded(object sender, RoutedEventArgs e)
+        {
+            if (appContext.PortOpen)
+            {
+                Devices.Items.Clear();
+                Devices.Items.Add(appContext.SerialPort.PortName);
+                Devices.SelectedIndex = 0;
+                Devices.IsEnabled = false;
+                Connect.Content = "断开连接";
+                Scan.IsEnabled = false;
+            }
+            else
+            {
+                Connect.Content = "连接设备";
+                Scan_Click(null, null);
+            }
+            // 读取配置
+            var prop = Properties.Settings.Default.Faces;
+            if (prop != null && !prop.Trim().Equals(""))
+            {
+                var faceStringList = prop.Split('|').ToList();
+                foreach (var faceString in faceStringList)
+                {
+                    var fileName = faceString.Substring(
+                        faceString.LastIndexOf(@"\", StringComparison.Ordinal) + 1);
+                    try
+                    {
+                        faces.Add(new FaceInfo(
+                            new BitmapImage(new Uri(faceString)),
+                            fileName,
+                            faceString.Replace(fileName, @"")));
+                    }
+                    catch (Exception exception)
+                    {
+                        Console.WriteLine(exception);
+                    }
+                }
+                foreach (var face in faces)
+                {
+                    ListBox.Items.Add(face);
+                }
+            }
+            FaceCount.Content = ListBox.Items.Count.ToString();
+        }
+
+        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
+        {
+            var list = new List<string>();
+            foreach (var face in faces)
+            {
+                list.Add(face.Path + face.Name);
+            }
+            Properties.Settings.Default.Faces = string.Join("|", list);
+            Properties.Settings.Default.Save();
+            Properties.Settings.Default.Reload();
+        }
+
+        public static BitmapImage BitmapToBitmapImage(Bitmap bitmap)
+        {
+            using (MemoryStream stream = new MemoryStream())
+            {
+                bitmap.Save(stream, ImageFormat.Png);
+                stream.Position = 0;
+                BitmapImage result = new BitmapImage();
+                result.BeginInit();
+                result.CacheOption = BitmapCacheOption.OnLoad;
+                result.StreamSource = stream;
+                result.EndInit();
+                result.Freeze();
+                return result;
+            }
+        }
+
+        private void Scan_Click(object sender, RoutedEventArgs e)
+        {
+            // 清除当前串口号中的所有串口名称
+            Devices.Items.Clear();
+            var portList = serialTool.FindPort();
+            if (portList.Count == 0)
+            {
+                System.Windows.Forms.MessageBox.Show("没有找到可用设备!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
+            }
+            else
+            {
+                portList.ForEach(
+                    delegate (string portName) {
+                        Devices.Items.Add(portName);
+                    });
+                //使 ListBox 显示第1个添加的索引
+                Devices.SelectedIndex = 0;
+            }
+        }
+
+        private void Connect_Click(object sender, RoutedEventArgs e)
+        {
+            if (appContext.PortOpen)
+            {
+                // 关闭串口
+                if (serialTool.ClosePort(appContext.SerialPort))
+                {
+                    Devices.IsEnabled = true;
+                    Scan.IsEnabled = true;
+                    Scan_Click(null, null);
+                    Connect.Content = "连接设备";
+                    appContext.SerialPort = null;
+                }
+                else
+                {
+                    System.Windows.Forms.MessageBox.Show("断开连接失败!", "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
+                }
+            }
+            else
+            {
+                // 检测串口设置
+                string portName = Devices.Text.Trim();
+                if (portName == "")
+                {
+                    System.Windows.Forms.MessageBox.Show("没有发现可连接设备!", "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
+                    return;
+                }
+                // 打开串口
+                SerialPort serialPort = serialTool.OpenPort(portName, "9600", "8", "1", "无", appContext.DataReceived);
+                if (serialPort == null)
+                {
+                    // 打开串口失败
+                    System.Windows.Forms.MessageBox.Show("连接设备失败!", "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
+                }
+                else
+                {
+                    Devices.IsEnabled = false;
+                    Scan.IsEnabled = false;
+                    Connect.Content = "断开连接";
+                    appContext.SerialPort = serialPort;
+                }
+            }
+        }
+
+        private void Delete_Click(object sender, RoutedEventArgs e)
+        {
+            var index = ListBox.SelectedIndex;
+            if (index == -1)
+            {
+                System.Windows.Forms.MessageBox.Show("请先选一项!", "提示", MessageBoxButtons.OK);
+                return;
+            }
+            faces.RemoveAt(index);
+            ListBox.Items.RemoveAt(index);
+            FaceCount.Content = ListBox.Items.Count.ToString();
+        }
+
+        private void DeleteALL_Click(object sender, RoutedEventArgs e)
+        {
+            var messageBoxButtons = MessageBoxButtons.OKCancel;
+            var dr = System.Windows.Forms.MessageBox.Show("将清空列表,确定吗?", "提示", messageBoxButtons, MessageBoxIcon.Warning);
+            if (dr != System.Windows.Forms.DialogResult.OK) return;
+            faces.Clear();
+            ListBox.Items.Clear();
+            FaceCount.Content = ListBox.Items.Count.ToString();
+        }
+
+        private void Add_Click(object sender, RoutedEventArgs e)
+        {
+            var ofd = new OpenFileDialog
+            {
+                Filter = "图片资源(所有类型)|*.jpg;*.jpeg;*.png;*.bmp|所有文件|*.*",
+                ValidateNames = true,
+                CheckPathExists = true,
+                CheckFileExists = true,
+                Multiselect = true
+            };
+            if (ofd.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
+            var strFileNames = ofd.FileNames;
+            foreach (var strFileName in strFileNames)
+            {
+                var fileName = strFileName.Substring(
+                    strFileName.LastIndexOf(@"\", StringComparison.Ordinal) + 1);
+                var faceInfo = new FaceInfo(
+                    new BitmapImage(new Uri(strFileName)),
+                    fileName,
+                    strFileName.Replace(fileName, @""));
+                ListBox.Items.Add(faceInfo);
+                faces.Add(faceInfo);
+            }
+            FaceCount.Content = ListBox.Items.Count.ToString();
         }
     }
 }