Browse Source

Add project files.

Robert 1 year ago
parent
commit
28af700b9d

+ 25 - 0
VideoImageBuilder.sln

@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.4.33403.182
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VideoImageBuilder", "VideoImageBuilder\VideoImageBuilder.csproj", "{EC3BEA10-F8C0-44B3-9D41-99BC94229892}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{EC3BEA10-F8C0-44B3-9D41-99BC94229892}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{EC3BEA10-F8C0-44B3-9D41-99BC94229892}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{EC3BEA10-F8C0-44B3-9D41-99BC94229892}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{EC3BEA10-F8C0-44B3-9D41-99BC94229892}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {3BDF915A-3DA8-42D4-9F01-95E620422F67}
+	EndGlobalSection
+EndGlobal

+ 8 - 0
VideoImageBuilder/App.config

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+    <startup> 
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
+    </startup>
+  <runtime>
+  </runtime>
+</configuration>

+ 19 - 0
VideoImageBuilder/App.xaml

@@ -0,0 +1,19 @@
+<Application
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="clr-namespace:VideoImageBuilder"
+    x:Class="VideoImageBuilder.App"
+    StartupUri="MainWindow.xaml">
+    <Application.Resources>
+        <ResourceDictionary>
+            <ResourceDictionary.MergedDictionaries>
+                <!--  FluentWPF Controls  -->
+                <ResourceDictionary Source="pack://application:,,,/FluentWPF;component/Styles/Controls.xaml" />
+
+                <!--  Material Design Controls  -->
+                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
+                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
+            </ResourceDictionary.MergedDictionaries>
+        </ResourceDictionary>
+    </Application.Resources>
+</Application>

+ 21 - 0
VideoImageBuilder/App.xaml.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace VideoImageBuilder
+{
+    /// <summary>
+    /// App.xaml 的互動邏輯
+    /// </summary>
+    public partial class App : Application
+    {
+        public App()
+        {
+            //InitializeComponent();
+        }
+    }
+}

+ 28 - 0
VideoImageBuilder/DpiDecorator.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+namespace VideoImageBuilder
+{
+    public class DpiDecorator : Decorator
+    {
+        public DpiDecorator()
+        {
+            this.Loaded += (s, e) =>
+            {
+                Matrix m = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice;
+                ScaleTransform dpiTransform = new ScaleTransform(1.25 / m.M11, 1.25 / m.M22);
+                if (dpiTransform.CanFreeze)
+                {
+                    dpiTransform.Freeze();
+                }
+                this.LayoutTransform = dpiTransform;
+            };
+        }
+    }
+}

+ 155 - 0
VideoImageBuilder/MainWindow.xaml

@@ -0,0 +1,155 @@
+<Window
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:fd="clr-namespace:SourceChord.FluentWPF;assembly=FluentWPF"
+    xmlns:local="clr-namespace:VideoImageBuilder"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
+    Title="Zerova VideoImageBuilder"
+    MinWidth="800"
+    MinHeight="450"
+    fd:AcrylicWindow.AcrylicWindowStyle="NoIcon"
+    fd:AcrylicWindow.Enabled="True"
+    fd:AcrylicWindow.ExtendViewIntoTitleBar="True"
+    fd:AcrylicWindow.NoiseOpacity="0.02"
+    fd:AcrylicWindow.TintColor="#FFFFFF"
+    AllowsTransparency="True"
+    FontFamily="Segoe UI"
+    Foreground="Black"
+    SizeToContent="Manual"
+    WindowStartupLocation="CenterScreen"
+    WindowStyle="None"
+    mc:Ignorable="d"
+    x:Class="VideoImageBuilder.MainWindow">
+    <local:DpiDecorator>
+        <Grid>
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="320" />
+                <ColumnDefinition Width="*" />
+            </Grid.ColumnDefinitions>
+            <Grid.RowDefinitions>
+                <RowDefinition Height="100" />
+                <RowDefinition Height="*" />
+            </Grid.RowDefinitions>
+
+            <Grid
+                x:Name="uxMenuGrid"
+                Grid.RowSpan="2"
+                Grid.Column="0"
+                fd:PointerTracker.Enabled="True"
+                Background="#1FE6E6E6">
+                <StackPanel Orientation="Vertical">
+                    <TextBlock
+                        x:Name="uxTitle"
+                        Margin="16,8,0,12"
+                        HorizontalAlignment="Left"
+                        Text="Phihong EVSE Video Tool" />
+                    <!--  Background  -->
+                    <TextBlock
+                        Margin="16,28,0,18"
+                        HorizontalAlignment="Left"
+                        Text="Setting"
+                        FontSize="15"
+                        FontWeight="SemiBold" />
+                    <ListView
+                        x:Name="uxMenuListView"
+                        Height="150"
+                        FontSize="15"
+                        Background="Transparent"
+                        SelectionChanged="uxMenuListView_SelectionChanged">
+                        <ListViewItem x:Name="uxChargeBoxSettingViewItem">
+                            <StackPanel Orientation="Horizontal">
+                                <md:PackIcon
+                                    Margin="16,12,6,12"
+                                    VerticalAlignment="Center"
+                                    Kind="InformationBoxOutline" />
+                                <TextBlock
+                                    Margin="12"
+                                    HorizontalAlignment="Left"
+                                    VerticalAlignment="Center"
+                                    Text="Target Chargebox"
+                                    FontSize="15" />
+                            </StackPanel>
+                        </ListViewItem>
+                        <ListViewItem x:Name="uxVideoSelectViewItem" Padding="1">
+                            <StackPanel Orientation="Horizontal">
+                                <md:PackIcon
+                                    Margin="16,12,6,12"
+                                    VerticalAlignment="Center"
+                                    Kind="VideoOutline" />
+                                <TextBlock
+                                    Margin="12"
+                                    HorizontalAlignment="Left"
+                                    VerticalAlignment="Center"
+                                    Text="Video Select"
+                                    FontSize="15" />
+                            </StackPanel>
+                        </ListViewItem>
+                    </ListView>
+                    <TextBlock
+                        Margin="17,0,0,0"
+                        HorizontalAlignment="Left"
+                        Text="Image"
+                        FontSize="15"
+                        FontWeight="SemiBold" />
+                    <ListView
+                        x:Name="uxMenuListView2"
+                        Height="50"
+                        FontSize="15"
+                        Background="Transparent"
+                        SelectionChanged="uxMenuListView_SelectionChanged">
+                        <ListViewItem
+                            x:Name="uxGenerateImageViewItem"
+                            Padding="1"
+                            Background="Transparent">
+                            <StackPanel Orientation="Horizontal">
+                                <md:PackIcon
+                                    Margin="16,12,6,12"
+                                    VerticalAlignment="Center"
+                                    Kind="File" />
+                                <TextBlock
+                                    Margin="12"
+                                    HorizontalAlignment="Left"
+                                    VerticalAlignment="Center"
+                                    Text="Generate Image"
+                                    FontSize="15" />
+                            </StackPanel>
+                        </ListViewItem>
+                    </ListView>
+                </StackPanel>
+            </Grid>
+
+            <Grid
+                x:Name="uxTitleGrid"
+                Grid.Row="0"
+                Grid.Column="1"
+                fd:PointerTracker.Enabled="True"
+                Background="White">
+                <StackPanel>
+                    <TextBlock
+                        x:Name="uxTitleTextBlock"
+                        Margin="22,50,0,18"
+                        HorizontalAlignment="Left"
+                        Text="Initial &amp; Idle"
+                        FontSize="30"
+                        FontWeight="Bold" />
+                </StackPanel>
+                <Border
+                    Margin="0,0,0,-1"
+                    BorderBrush="White"
+                    BorderThickness="0,0,0,2" />
+            </Grid>
+
+            <Grid
+                x:Name="uxContentGrid"
+                Grid.Row="1"
+                Grid.Column="1"
+                fd:PointerTracker.Enabled="True"
+                Background="White"
+                ShowGridLines="False">
+                <!--<local:InitialIdleUC></local:InitialIdleUC>-->
+            </Grid>
+        </Grid>
+    </local:DpiDecorator>
+</Window>

+ 117 - 0
VideoImageBuilder/MainWindow.xaml.cs

@@ -0,0 +1,117 @@
+using SourceChord.FluentWPF;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Reflection;
+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.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using VideoImageBuilder.Service;
+using VideoImageBuilder.SubPage;
+using static System.Net.Mime.MediaTypeNames;
+
+namespace VideoImageBuilder
+{
+    /// <summary>
+    /// MainWindow.xaml 的互動邏輯
+    /// </summary>
+    public partial class MainWindow : Window
+    {
+        public MainWindow()
+        {
+            //this.serviceProvider = serviceProvider;
+            //this.appSettingService = appSettingService;
+            this.appSettingService = AppSettingService.Instance;
+
+            appSettingService.LoadSetting();
+
+            InitializeComponent();
+
+            uxTitle.Text = uxTitle.Text + string.Format(" V{0}", Assembly.GetEntryAssembly().GetName().Version);
+
+            uxChargeBoxSettingViewItem.IsSelected = true;
+            uxChargeBoxSettingViewItem.Focus();
+        }
+
+        protected override void OnClosing(CancelEventArgs e)
+        {
+            appSettingService.SaveSetting();
+            base.OnClosing(e);
+        }
+
+        private ListViewItem lastSelectedItem = null;
+        //private readonly IServiceProvider serviceProvider;
+        private readonly AppSettingService appSettingService;
+
+        private void uxMenuListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
+        {
+            if (!(sender is ListView listView) ||
+                listView.SelectedItem is null)
+            {
+                return;
+            }
+
+            if (lastSelectedItem != null)
+            {
+                lastSelectedItem.IsSelected = false;
+                lastSelectedItem.Foreground = new SolidColorBrush(Colors.Black);
+                lastSelectedItem.Background = null;
+            }
+
+            ListViewItem item = listView.SelectedItem as ListViewItem;
+
+            Binding myBinding = new Binding("ImmersiveSystemAccentBrush");
+            myBinding.Source = new AccentColors();
+            item.SetBinding(ListViewItem.ForegroundProperty, myBinding);
+            item.Background = new SolidColorBrush(Color.FromArgb(40, 0, 0, 0));
+
+            uxTitleTextBlock.Text = (item.Content as StackPanel).Children.OfType<TextBlock>().First().Text;
+
+            uxContentGrid.Children.Clear();
+
+            //switch (item.Name)
+            //{
+            //}
+
+            uxContentGrid.Children.Add(GetSubPage(item.Name));
+
+            lastSelectedItem = item;
+        }
+
+        private UserControl GetSubPage(string name)
+        {
+            UserControl subPage = null;
+            switch(name)
+            {
+                case "uxChargeBoxSettingViewItem":
+                    subPage = new TargetChargeBoxUc();
+                    break;
+                case "uxVideoSelectViewItem":
+                    subPage = new VideoSelectUc();
+                    break;
+                case "uxGenerateImageViewItem":
+                    subPage = new GenerateImageUc();
+                    break;
+                default:
+                    subPage = null;
+                    break;
+            }
+
+            if(subPage is null)
+            {
+                return new UserControl();
+            }
+
+            return subPage;
+        }
+    }
+}

+ 55 - 0
VideoImageBuilder/Properties/AssemblyInfo.cs

@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// 組件的一般資訊是由下列的屬性集控制。
+// 變更這些屬性的值即可修改組件的相關
+// 資訊。
+[assembly: AssemblyTitle("VideoImageBuilder")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("VideoImageBuilder")]
+[assembly: AssemblyCopyright("Copyright ©  2023")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 將 ComVisible 設為 false 可對 COM 元件隱藏
+// 組件中的類型。若必須從 COM 存取此組件中的類型,
+// 的類型,請在該類型上將 ComVisible 屬性設定為 true。
+[assembly: ComVisible(false)]
+
+//若要開始建置可當地語系化的應用程式,請在
+//.csproj 檔案中的 <UICulture>CultureYouAreCodingWith</UICulture>
+//<UICulture>CultureYouAreCodingWith</UICulture>。例如,如果原始程式檔使用美式英文, 
+//請將 <UICulture> 設為 en-US。然後取消註解下列
+//NeutralResourceLanguage 屬性。在下一行中更新 "en-US",
+//以符合專案檔中的 UICulture 設定。
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+    ResourceDictionaryLocation.None, //主題特定資源字典的位置
+                                     //(在頁面中找不到時使用,
+                                     // 或應用程式資源字典中找不到資源時)
+    ResourceDictionaryLocation.SourceAssembly //泛型資源字典的位置
+                                              //(在頁面中找不到時使用,
+                                              // 或是應用程式或任何主題特定資源字典中找不到資源時)
+)]
+
+
+// 組件的版本資訊由下列四個值所組成:
+//
+//      主要版本
+//      次要版本
+//      組建編號
+//      修訂
+//
+// 您可以指定所有的值,也可以使用 '*' 將組建和修訂編號
+// 設為預設,如下所示:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.1.0")]
+[assembly: AssemblyFileVersion("1.0.1.0")]

+ 63 - 0
VideoImageBuilder/Properties/Resources.Designer.cs

@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace VideoImageBuilder.Properties {
+    using System;
+    
+    
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() {
+        }
+        
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("VideoImageBuilder.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+    }
+}

+ 117 - 0
VideoImageBuilder/Properties/Resources.resx

@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 26 - 0
VideoImageBuilder/Properties/Settings.Designer.cs

@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace VideoImageBuilder.Properties {
+    
+    
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.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 {
+                return defaultInstance;
+            }
+        }
+    }
+}

+ 7 - 0
VideoImageBuilder/Properties/Settings.settings

@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
+  <Profiles>
+    <Profile Name="(Default)" />
+  </Profiles>
+  <Settings />
+</SettingsFile>

+ 69 - 0
VideoImageBuilder/Service/AppSettingService.cs

@@ -0,0 +1,69 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace VideoImageBuilder.Service
+{
+    public class AppSettingService
+    {
+        private static AppSettingService _Instance;
+        public static AppSettingService Instance
+        {
+            get
+            {
+                if (_Instance == null)
+                {
+                    _Instance = new AppSettingService();
+                }
+                return _Instance;
+            }
+        }
+
+        private AppSettingService()
+        {
+            this.context = VideoImageBuilderContext.Instance;
+        }
+
+        public const string settingFileName = "app.ini";
+        private readonly VideoImageBuilderContext context;
+
+        public void LoadSetting()
+        {
+            if (!File.Exists(settingFileName))
+            {
+                return;
+            }
+
+            VideoImageBuilderContext setting = null;
+            try
+            {
+                setting = JsonConvert.DeserializeObject<VideoImageBuilderContext>(File.ReadAllText(settingFileName));
+            }
+            catch
+            {
+                MessageBox.Show($"{settingFileName} format error");
+                return;
+            }
+
+            context.ChargeboxId = setting.ChargeboxId;
+            context.VideoPath1 = setting.VideoPath1;
+            context.VideoPath2 = setting.VideoPath2;
+            context.OutPutFolderPath = setting.OutPutFolderPath;
+        }
+
+        public void SaveSetting()
+        {
+            if (File.Exists(settingFileName))
+            {
+                File.Delete(settingFileName);
+            }
+
+            File.WriteAllText(settingFileName,JsonConvert.SerializeObject(context));
+        }
+    }
+}

+ 154 - 0
VideoImageBuilder/Service/FirmwareHeaderBuilder.cs

@@ -0,0 +1,154 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Phihong_EVSE_UI_Tool
+{
+    public class FirmwareHeaderBuilder
+    {
+        public static void AddFirmwareHeader(string temZipPath, string modelName, string outputFilePath)
+        {
+            var modelNameArray = Encoding.ASCII.GetBytes(modelName);
+            var imageDataArray = File.ReadAllBytes(temZipPath);
+
+            byte[] dataSegment1 = new byte[16];
+            byte[] dataSegment2 = new byte[16];
+            byte[] dataSegment3 = new byte[16];
+
+            Array.Clear(dataSegment1, 0, 16);
+            for (int i = 0; i < 16; i++)
+            {
+                dataSegment2[i] = 0xFF;
+                dataSegment3[i] = 0xFF;
+            }
+
+            var Lcm_Type = new byte[] { 0x10, 0x00, 0x00, 0x0F };
+
+            // 0000h
+            Array.Copy(modelNameArray, 0, dataSegment1, 0, modelNameArray.Length);
+            // 0001h
+            byte[] imgLenBytes = GetImgLenBytes(imageDataArray);
+            byte[] timeBytes = GetCreateTimeBytes();
+            //Array.Copy(imageType.CodeArray, 0, dataSegment2, 0, 4);
+            Array.Copy(Lcm_Type, 0, dataSegment2, 0, 4);
+            Array.Copy(imgLenBytes, 0, dataSegment2, 4, 4);
+            Array.Copy(timeBytes, 0, dataSegment2, 8, 8);
+            // 0002h
+            Array.Copy(timeBytes, 8, dataSegment3, 0, 2);
+            byte[] crcBytes = GetCRC32(dataSegment1, dataSegment2, dataSegment3, imageDataArray);
+            Array.Copy(crcBytes, 0, dataSegment3, 2, 4);
+
+            List<byte> contents = new List<byte>();
+            contents.AddRange(dataSegment1);
+            contents.AddRange(dataSegment2);
+            contents.AddRange(dataSegment3);
+            contents.AddRange(imageDataArray);
+
+            File.WriteAllBytes(outputFilePath, contents.ToArray());
+        }
+
+        private static byte[] GetImgLenBytes(byte[] imageArray)
+        {
+            // Big Endian
+            byte[] imgLenBytes = BitConverter.GetBytes(imageArray.Length);
+            Array.Reverse(imgLenBytes);
+
+            return imgLenBytes;
+        }
+
+        private static byte[] GetCreateTimeBytes()
+        {
+            string strTime = DateTime.Now.ToString("yyyyMMddHH");
+            return Encoding.ASCII.GetBytes(strTime);
+        }
+
+        private static byte[] GetCRC32(byte[] segment1, byte[] segment2, byte[] segment3, byte[] imageArray)
+        {
+            var crc32Handler = CRC32.GetInatance();
+            List<byte> crcData = new List<byte>();
+            crcData.AddRange(segment1);
+            crcData.AddRange(segment2);
+            crcData.AddRange(new byte[] { segment3[0], segment3[1] });
+            crcData.AddRange(imageArray);
+
+            ulong crc32 = crc32Handler.Calculate(crcData.ToArray(), crcData.Count);
+
+            // Big Endian
+            byte[] outcome = new byte[4];
+            outcome[0] = (byte)((crc32 & 0xFF000000) >> 24);
+            outcome[1] = (byte)((crc32 & 0x00FF0000) >> 16);
+            outcome[2] = (byte)((crc32 & 0x0000FF00) >> 8);
+            outcome[3] = (byte)(crc32 & 0x000000FF);
+
+            return outcome;
+        }
+    }
+
+    class CRC32
+    {
+        private static CRC32 inatance;
+
+        /// <summary>取得實體。</summary>
+        /// <returns></returns>
+        public static CRC32 GetInatance()
+        {
+            if (inatance == null)
+            {
+                inatance = new CRC32();
+            }
+
+            return inatance;
+        }
+
+        private ulong[] crc32Table;
+
+        /// <summary>初始化 CRC32 類別的新執行個體。</summary>
+        private CRC32()
+        {
+            crc32Table = new ulong[256];
+            GetCRC32Table();
+        }
+
+        /// <summary>計算。</summary>
+        /// <param name="dataArray"></param>
+        /// <param name="length"></param>
+        /// <returns></returns>
+        public ulong Calculate(byte[] dataArray, int length)
+        {
+            ulong crc = 0xFFFFFFFF;
+
+            for (int i = 0; i < length; i++)
+            {
+                crc = (crc >> 8) ^ crc32Table[(crc & 0xFF) ^ dataArray[i]];
+            }
+
+            return crc ^ 0xFFFFFFFF;
+        }
+
+        private void GetCRC32Table()
+        {
+            ulong crc;
+            int i, j;
+
+            for (i = 0; i < 256; i++)
+            {
+                crc = (ulong)i;
+                for (j = 8; j > 0; j--)
+                {
+                    if ((crc & 1) == 1)
+                    {
+                        crc = (crc >> 1) ^ 0xEDB88320;
+                    }
+                    else
+                    {
+                        crc >>= 1;
+                    }
+                }
+                crc32Table[i] = crc;
+            }
+        }
+    }
+}

+ 93 - 0
VideoImageBuilder/Service/UiGenerateImageService.cs

@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace VideoImageBuilder.Service
+{
+    public class UiGenerateImageService
+    {
+        public UiGenerateImageService()
+        {
+            this.uiContext = VideoImageBuilderContext.Instance;
+            this.videoImageBuilerService = new VideoImageBuilerService();
+        }
+
+        private readonly VideoImageBuilderContext uiContext;
+        private readonly VideoImageBuilerService videoImageBuilerService;
+
+        internal void Start()
+        {
+            if (!CheckContext())
+            {
+                return;
+            }
+
+            Result result = videoImageBuilerService.Generate(
+                uiContext.ChargeboxId,
+                uiContext.VideoPath1,
+                uiContext.VideoPath2,
+                uiContext.OutPutFolderPath
+                );
+
+            if (result == null)
+            {
+                MessageBox.Show("Unexpected Error!");
+                return;
+            }
+
+            if (!result.Success)
+            {
+                MessageBox.Show(result.Message);
+                return;
+            }
+
+            System.Diagnostics.Process process = new System.Diagnostics.Process();
+            process.StartInfo.FileName = uiContext.OutPutFolderPath ;
+            process.Start();
+        }
+
+        private bool CheckContext()
+        {
+            if (string.IsNullOrEmpty(uiContext.ChargeboxId))
+            {
+                MessageBox.Show("ChargeboxId shoud not be empty");
+                return false;
+            }
+            if (string.IsNullOrEmpty(uiContext.VideoPath1))
+            {
+                MessageBox.Show("Vertical screensave video file path should not be empty");
+                return false;
+            }
+            if (!File.Exists(uiContext.VideoPath1))
+            {
+                MessageBox.Show("Vertical screensave video file not found");
+                return false;
+            }
+            if (string.IsNullOrEmpty(uiContext.VideoPath2))
+            {
+                MessageBox.Show("Horizonal AD video file path should not be empty");
+                return false;
+            }
+            if (!File.Exists(uiContext.VideoPath2))
+            {
+                MessageBox.Show("Horizontal AD video file not found");
+                return false;
+            }
+            if (string.IsNullOrEmpty(uiContext.OutPutFolderPath))
+            {
+                MessageBox.Show("Output folder path should not be empty");
+                return false;
+            }
+            if (!Directory.Exists(uiContext.OutPutFolderPath))
+            {
+                MessageBox.Show("Output folder not found");
+                return false;
+            }
+            return true;
+        }
+    }
+}

+ 86 - 0
VideoImageBuilder/Service/VideoImageBuilderContext.cs

@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VideoImageBuilder.Service
+{
+    public class VideoImageBuilderContext : INotifyPropertyChanged
+    {
+        private static VideoImageBuilderContext _Instance;
+        public static VideoImageBuilderContext Instance
+        {
+            get
+            {
+                if (_Instance == null)
+                {
+                    _Instance = new VideoImageBuilderContext();
+                }
+                return _Instance;
+            }
+        }
+
+        private VideoImageBuilderContext() { }
+
+
+        private string _ChargeboxId;
+        public string ChargeboxId
+        {
+            get { return _ChargeboxId; }
+            set 
+            {
+                if (_ChargeboxId != value)
+                {
+                    _ChargeboxId = value;
+                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ChargeboxId)));
+                }
+            }
+        }
+
+        private string _VideoPath1;
+        public string VideoPath1
+        {
+            get { return _VideoPath1; }
+            set
+            {
+                if (_VideoPath1 != value)
+                {
+                    _VideoPath1 = value;
+                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(VideoPath1)));
+                }
+            }
+        }
+
+        private string _VideoPath2;
+        public string VideoPath2
+        {
+            get { return _VideoPath2; }
+            set
+            {
+                if (_VideoPath2 != value)
+                {
+                    _VideoPath2 = value;
+                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(VideoPath2)));
+                }
+            }
+        }
+
+        private string _OutPutFolderPath;
+        public string OutPutFolderPath
+        {
+            get { return _OutPutFolderPath; }
+            set
+            {
+                if (_OutPutFolderPath != value)
+                {
+                    _OutPutFolderPath = value;
+                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(OutPutFolderPath)));
+                }
+            }
+        }
+
+        public event PropertyChangedEventHandler PropertyChanged;
+    }
+}

+ 111 - 0
VideoImageBuilder/Service/VideoImageBuilerService.cs

@@ -0,0 +1,111 @@
+using Phihong_EVSE_UI_Tool;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VideoImageBuilder.Service
+{
+    public class VideoImageBuilerService
+    {
+        public static readonly string ZIP_SOURCE_DIRECTORY = Environment.CurrentDirectory + @"\ZipSource";
+        public static readonly string ZIP_FILE_DIRECTORY = Environment.CurrentDirectory + @"\ZipFile";
+        public const string TEMP_ZIP_FILE_NAME = @"tmp.zip";
+
+        public const string ZIP_Video1_Name = "97_ScreenSave.mp4";
+        public const string ZIP_Video2_Name = "99_AD.mp4";
+
+        public Result Generate(
+            string chargeboxId,
+            string videoPath1,
+            string videoPath2,
+            string outputFolderPath
+            )
+        {
+            var initResult = InitDirectory();
+            if (!initResult)
+            {
+                return new Result() { Success = false, Message = "InitDirectory Failed" };
+            }
+
+            string temZipPath = Path.Combine(ZIP_FILE_DIRECTORY, TEMP_ZIP_FILE_NAME);
+            var createZipFileResult = CreateZipFile(temZipPath, videoPath1, videoPath2);
+            if (!createZipFileResult)
+            {
+                return new Result() { Success = false, Message = "CreateZipFile Failed" };
+            }
+
+            var createHeaderZipFileResult = CreateHeaderZipFile(outputFolderPath, temZipPath, chargeboxId);
+            if (!createHeaderZipFileResult)
+            {
+                return new Result() { Success = false, Message = "CreateHeaderZipFile Failed" };
+            }
+
+            return new Result() { Success = true };
+        }
+
+        private static bool CreateHeaderZipFile(string outputFolderPath, string temZipPath, string chargeboxId)
+        {
+            try
+            {
+
+                string zipFileName = string.Format("Image_{0}_{1}.zip", chargeboxId, DateTime.Now.ToString("yyyyMMddHHmmss"));
+                string tempHeaderBuildedZipPath = Path.Combine(ZIP_FILE_DIRECTORY, zipFileName);
+                FirmwareHeaderBuilder.AddFirmwareHeader(temZipPath, chargeboxId, tempHeaderBuildedZipPath);
+                File.Copy(tempHeaderBuildedZipPath, Path.Combine(outputFolderPath, zipFileName));
+                return true;
+            }
+            catch
+            {
+                return false;
+            }
+        }
+
+        private static bool CreateZipFile(string zipPath, string videoPath1, string videoPath2)
+        {
+            try
+            {
+                File.Copy(videoPath1, Path.Combine(ZIP_SOURCE_DIRECTORY, ZIP_Video1_Name), true);
+                File.Copy(videoPath2, Path.Combine(ZIP_SOURCE_DIRECTORY, ZIP_Video2_Name), true);
+
+                ZipFile.CreateFromDirectory(ZIP_SOURCE_DIRECTORY, zipPath, CompressionLevel.Fastest, false);
+                return true;
+            }
+            catch
+            {
+                return false;
+            }
+        }
+
+        private static bool InitDirectory()
+        {
+            try
+            {
+                if (Directory.Exists(ZIP_SOURCE_DIRECTORY))
+                {
+                    Directory.Delete(ZIP_SOURCE_DIRECTORY, true);
+                }
+                if (Directory.Exists(ZIP_FILE_DIRECTORY))
+                {
+                    Directory.Delete(ZIP_FILE_DIRECTORY, true);
+                }
+                Directory.CreateDirectory(ZIP_SOURCE_DIRECTORY);
+                Directory.CreateDirectory(ZIP_FILE_DIRECTORY);
+                return true;
+            }
+            catch
+            {
+                return false;
+            }
+        }
+    }
+
+    public class Result
+    {
+        public bool Success { get; set; }
+        public string Message { get; set; }
+    }
+}

+ 56 - 0
VideoImageBuilder/SubPage/GenerateImageUc.xaml

@@ -0,0 +1,56 @@
+<UserControl
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:local="clr-namespace:VideoImageBuilder.SubPage"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    d:DesignHeight="450"
+    d:DesignWidth="800"
+    mc:Ignorable="d"
+    x:Class="VideoImageBuilder.SubPage.GenerateImageUc">
+    <StackPanel>
+        <TextBlock
+            Margin="22,12,0,12"
+            HorizontalAlignment="Left"
+            Text="Image Output Folder"
+            FontSize="24" />
+        <StackPanel Margin="22,0,150,12" Orientation="Horizontal">
+            <Button
+                x:Name="uxFolderBtn"
+                Width="90"
+                Height="35"
+                HorizontalAlignment="Left"
+                Click="uxBrowseButton_Click"
+                Content="Browse"
+                Foreground="Black"
+                Style="{StaticResource ButtonRevealStyle}"
+                Tag="Url" />
+            <TextBox
+                x:Name="uxFolderBrowseTextBox"
+                Width="270"
+                Height="35"
+                Margin="1,0,0,0"
+                VerticalContentAlignment="Center"
+                Text="{Binding OutPutFolderPath, Mode=TwoWay}"
+                IsReadOnly="True"
+                Style="{StaticResource TextBoxRevealStyle}" />
+        </StackPanel>
+        <TextBlock
+            Margin="22,12,0,12"
+            HorizontalAlignment="Left"
+            Text="Generate Update Image"
+            FontSize="24" />
+        <StackPanel Margin="22,0,150,12" Orientation="Horizontal">
+            <Button
+                x:Name="uxGenerateBtn"
+                Width="90"
+                Height="35"
+                HorizontalAlignment="Left"
+                Click="uxGenerateButton_Click"
+                Content="Generate"
+                Foreground="Black"
+                Style="{StaticResource ButtonRevealStyle}"
+                Tag="Url" />
+        </StackPanel>
+    </StackPanel>
+</UserControl>

+ 61 - 0
VideoImageBuilder/SubPage/GenerateImageUc.xaml.cs

@@ -0,0 +1,61 @@
+using Microsoft.WindowsAPICodePack.Dialogs;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Remoting.Contexts;
+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.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using VideoImageBuilder.Service;
+
+namespace VideoImageBuilder.SubPage
+{
+    /// <summary>
+    /// GenerateImageUc.xaml 的互動邏輯
+    /// </summary>
+    public partial class GenerateImageUc : UserControl
+    {
+        public GenerateImageUc()
+        {
+            InitializeComponent();
+            this.context = VideoImageBuilderContext.Instance;
+            this.DataContext = this.context;
+
+            this.uiGenerateImageService = new UiGenerateImageService();
+
+        }
+
+        private readonly VideoImageBuilderContext context;
+        private readonly UiGenerateImageService uiGenerateImageService;
+
+        private void uxBrowseButton_Click(object sender, RoutedEventArgs e)
+        {
+            //select file and file path
+            var dlg = new CommonOpenFileDialog()
+            {
+                IsFolderPicker = true,
+                Title = "Select image files path",
+            };
+
+            if (dlg.ShowDialog() != CommonFileDialogResult.Ok)
+            {
+                return;
+            }
+
+            context.OutPutFolderPath = dlg.FileName;
+        }
+
+        private void uxGenerateButton_Click(object sender, RoutedEventArgs e)
+        {
+            uiGenerateImageService.Start();
+        }
+    }
+}

+ 28 - 0
VideoImageBuilder/SubPage/TargetChargeBoxUc.xaml

@@ -0,0 +1,28 @@
+<UserControl
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:local="clr-namespace:VideoImageBuilder.SubPage"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    d:DesignHeight="450"
+    d:DesignWidth="800"
+    mc:Ignorable="d"
+    x:Class="VideoImageBuilder.SubPage.TargetChargeBoxUc">
+    <StackPanel>
+        <TextBlock
+            Margin="22,12,0,12"
+            HorizontalAlignment="Left"
+            Text="Enter EVSE Model Name"
+            FontSize="24" />
+        <TextBox
+            x:Name="uxModelNameTestBox"
+            Width="270"
+            Height="35"
+            Margin="23,0,100,12"
+            HorizontalAlignment="Left"
+            VerticalContentAlignment="Center"
+            Text="{Binding ChargeboxId, Mode=TwoWay}"
+            Style="{StaticResource TextBoxRevealStyle}" />
+
+    </StackPanel>
+</UserControl>

+ 34 - 0
VideoImageBuilder/SubPage/TargetChargeBoxUc.xaml.cs

@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+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.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using VideoImageBuilder.Service;
+
+namespace VideoImageBuilder.SubPage
+{
+    /// <summary>
+    /// TargetChargeBoxUc.xaml 的互動邏輯
+    /// </summary>
+    public partial class TargetChargeBoxUc : UserControl
+    {
+        public TargetChargeBoxUc()
+        {
+            InitializeComponent();
+
+            this.context = VideoImageBuilderContext.Instance;
+            this.DataContext = this.context;
+        }
+
+        private readonly VideoImageBuilderContext context;
+    }
+}

+ 66 - 0
VideoImageBuilder/SubPage/VideoSelectUc.xaml

@@ -0,0 +1,66 @@
+<UserControl
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:local="clr-namespace:VideoImageBuilder.SubPage"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    d:DesignHeight="450"
+    d:DesignWidth="800"
+    mc:Ignorable="d"
+    x:Class="VideoImageBuilder.SubPage.VideoSelectUc">
+    <StackPanel>
+        <TextBlock
+            Margin="22,12,0,12"
+            HorizontalAlignment="Left"
+            Text="Vertical ScreenSave Video"
+            FontSize="24" />
+        <StackPanel Margin="22,0,150,12" Orientation="Horizontal">
+            <Button
+                x:Name="uxVideoBtn1"
+                Width="90"
+                Height="35"
+                HorizontalAlignment="Left"
+                Click="uxBrowseButton_Click"
+                Content="Browse"
+                Foreground="Black"
+                Style="{StaticResource ButtonRevealStyle}"
+                Tag="Url" />
+            <TextBox
+                x:Name="uxUrlBrowseTextBox1"
+                Width="270"
+                Height="35"
+                Margin="1,0,0,0"
+                VerticalContentAlignment="Center"
+                Text="{Binding VideoPath1, Mode=TwoWay}"
+                IsReadOnly="True"
+                Style="{StaticResource TextBoxRevealStyle}" />
+        </StackPanel>
+
+        <TextBlock
+            Margin="22,12,0,12"
+            HorizontalAlignment="Left"
+            Text="Horizontal AD Video"
+            FontSize="24" />
+        <StackPanel Margin="22,0,150,12" Orientation="Horizontal">
+            <Button
+                x:Name="uxVideoBtn2"
+                Width="90"
+                Height="35"
+                HorizontalAlignment="Left"
+                Click="uxBrowseButton_Click"
+                Content="Browse"
+                Foreground="Black"
+                Style="{StaticResource ButtonRevealStyle}"
+                Tag="Url" />
+            <TextBox
+                x:Name="uxUrlBrowseTextBox2"
+                Width="270"
+                Height="35"
+                Margin="1,0,0,0"
+                VerticalContentAlignment="Center"
+                Text="{Binding VideoPath2, Mode=TwoWay}"
+                IsReadOnly="True"
+                Style="{StaticResource TextBoxRevealStyle}" />
+        </StackPanel>
+    </StackPanel>
+</UserControl>

+ 62 - 0
VideoImageBuilder/SubPage/VideoSelectUc.xaml.cs

@@ -0,0 +1,62 @@
+using Microsoft.WindowsAPICodePack.Dialogs;
+using System;
+using System.Collections.Generic;
+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.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using VideoImageBuilder.Service;
+
+namespace VideoImageBuilder.SubPage
+{
+    /// <summary>
+    /// VideoSelectUc.xaml 的互動邏輯
+    /// </summary>
+    public partial class VideoSelectUc : UserControl
+    {
+        public VideoSelectUc()
+        {
+            InitializeComponent();
+            this.context = VideoImageBuilderContext.Instance;
+            this.DataContext = this.context;
+        }
+
+        private VideoImageBuilderContext context;
+
+        private void uxBrowseButton_Click(object sender, RoutedEventArgs e)
+        {
+            Button btn = e.Source as Button;
+            if (btn is null)
+            {
+                return;
+            }
+
+            var dlg = new CommonOpenFileDialog()
+            {
+                EnsureFileExists = true,
+                Title = "Select an MP4 file",
+            };
+            dlg.Filters.Add(new CommonFileDialogFilter("mp4", "*.mp4"));
+
+            if (dlg.ShowDialog() == CommonFileDialogResult.Ok)
+            {
+                if (btn == uxVideoBtn1)
+                {
+                    context.VideoPath1 = dlg.FileName;
+                }
+                if (btn == uxVideoBtn2)
+                {
+                    context.VideoPath2 = dlg.FileName;
+                }
+            }
+        }
+    }
+}

+ 153 - 0
VideoImageBuilder/VideoImageBuilder.csproj

@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{EC3BEA10-F8C0-44B3-9D41-99BC94229892}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <RootNamespace>VideoImageBuilder</RootNamespace>
+    <AssemblyName>VideoImageBuilder</AssemblyName>
+    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <WarningLevel>4</WarningLevel>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <Deterministic>true</Deterministic>
+    <NuGetPackageImportStamp>
+    </NuGetPackageImportStamp>
+    <TargetFrameworkProfile />
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="FluentWPF, Version=0.8.0.6, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\packages\FluentWPF.0.8.0\lib\net45\FluentWPF.dll</HintPath>
+    </Reference>
+    <Reference Include="MaterialDesignColors, Version=2.1.4.0, Culture=neutral, PublicKeyToken=df2a72020bd7962a, processorArchitecture=MSIL">
+      <HintPath>..\packages\MaterialDesignColors.2.1.4\lib\net462\MaterialDesignColors.dll</HintPath>
+    </Reference>
+    <Reference Include="MaterialDesignThemes.Wpf, Version=4.9.0.0, Culture=neutral, PublicKeyToken=df2a72020bd7962a, processorArchitecture=MSIL">
+      <HintPath>..\packages\MaterialDesignThemes.4.9.0\lib\net462\MaterialDesignThemes.Wpf.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.WindowsAPICodePack, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\packages\WindowsAPICodePack-Core.1.1.1\lib\Microsoft.WindowsAPICodePack.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.WindowsAPICodePack.Shell, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\packages\WindowsAPICodePack-Shell.1.1.1\lib\Microsoft.WindowsAPICodePack.Shell.dll</HintPath>
+    </Reference>
+    <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
+    </Reference>
+    <Reference Include="PresentationCore" />
+    <Reference Include="System" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.IO.Compression.FileSystem" />
+    <Reference Include="System.Xml" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xaml">
+      <RequiredTargetFramework>4.0</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="WindowsBase" />
+    <Reference Include="PresentationFramework" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Service\AppSettingService.cs" />
+    <Compile Include="Service\FirmwareHeaderBuilder.cs" />
+    <Compile Include="Service\UiGenerateImageService.cs" />
+    <Compile Include="Service\VideoImageBuilerService.cs" />
+    <Compile Include="SubPage\GenerateImageUc.xaml.cs">
+      <DependentUpon>GenerateImageUc.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="SubPage\VideoSelectUc.xaml.cs">
+      <DependentUpon>VideoSelectUc.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Service\VideoImageBuilderContext.cs" />
+    <Compile Include="SubPage\TargetChargeBoxUc.xaml.cs">
+      <DependentUpon>TargetChargeBoxUc.xaml</DependentUpon>
+    </Compile>
+    <Page Include="MainWindow.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <ApplicationDefinition Include="App.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </ApplicationDefinition>
+    <Compile Include="DpiDecorator.cs" />
+    <Compile Include="MainWindow.xaml.cs">
+      <DependentUpon>MainWindow.xaml</DependentUpon>
+      <SubType>Code</SubType>
+    </Compile>
+    <Page Include="SubPage\GenerateImageUc.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+    <Page Include="SubPage\TargetChargeBoxUc.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+    <Page Include="SubPage\VideoSelectUc.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Properties\AssemblyInfo.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Settings.settings</DependentUpon>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+    </Compile>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+    <None Include="packages.config" />
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Import Project="..\packages\MaterialDesignThemes.4.9.0\build\MaterialDesignThemes.targets" Condition="Exists('..\packages\MaterialDesignThemes.4.9.0\build\MaterialDesignThemes.targets')" />
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>此專案參考這部電腦上所缺少的 NuGet 套件。請啟用 NuGet 套件還原,以下載該套件。如需詳細資訊,請參閱 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的檔案是 {0}。</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('..\packages\MaterialDesignThemes.4.9.0\build\MaterialDesignThemes.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MaterialDesignThemes.4.9.0\build\MaterialDesignThemes.targets'))" />
+  </Target>
+</Project>

+ 9 - 0
VideoImageBuilder/packages.config

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="FluentWPF" version="0.8.0" targetFramework="net471" />
+  <package id="MaterialDesignColors" version="1.2.7" targetFramework="net472" />
+  <package id="MaterialDesignThemes" version="3.2.0" targetFramework="net472" />
+  <package id="Newtonsoft.Json" version="13.0.3" targetFramework="net472" />
+  <package id="WindowsAPICodePack-Core" version="1.1.1" targetFramework="net472" />
+  <package id="WindowsAPICodePack-Shell" version="1.1.1" targetFramework="net472" />
+</packages>