瀏覽代碼

合并-合并绝大多数功能实现

zhuyi 2 年之前
父節點
當前提交
6ed0319533

+ 2 - 0
PDF Office/App.xaml.cs

@@ -46,6 +46,7 @@ using PDF_Office.Views.EditTools.HeaderFooter;
 using PDF_Office.Views.EditTools.Redaction;
 using PDF_Office.Views.Form;
 using PDF_Office.Views.PropertyPanel.PDFEdit;
+using PDF_Office.Views.Dialog.ToolsDialogs;
 
 namespace PDF_Office
 {
@@ -228,6 +229,7 @@ namespace PDF_Office
             containerRegistry.RegisterDialog<VerifyPassWordDialog>(DialogNames.VerifyPassWordDialog);
             containerRegistry.RegisterDialog<FullScreenWindow>(DialogNames.FullScreenDialog);
             containerRegistry.RegisterDialog<CompressDialog>(DialogNames.CompressDialog);
+            containerRegistry.RegisterDialog<MergeDialog>(DialogNames.MergeDialog);
             containerRegistry.RegisterDialog<SetPasswordDialog>(DialogNames.SetPasswordDialog);
             containerRegistry.RegisterDialog<CheckPasswordDialog>(DialogNames.CheckPasswordDialog);
             containerRegistry.RegisterDialog<DeleteSafetySettingsDialog>(DialogNames.DeleteSafetySettingsDialog);

File diff suppressed because it is too large
+ 4740 - 0
PDF Office/ComPDFKit.Desk.xml


File diff suppressed because it is too large
+ 4267 - 0
PDF Office/ComPDFKit.Viewer.xml


+ 27 - 0
PDF Office/DataConvert/IndexConverter .cs

@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Controls;
+using System.Windows.Data;
+
+namespace PDF_Office.DataConvert
+{
+    public class IndexConverter : IValueConverter
+    {
+        public object Convert(object value, Type TargetType, object parameter, CultureInfo culture)
+        {
+            ListViewItem item = (ListViewItem)value;
+            ListView listView = ItemsControl.ItemsControlFromItemContainer(item) as ListView;
+            int index = listView.ItemContainerGenerator.IndexFromContainer(item) + 1;
+            return index.ToString();
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 13 - 0
PDF Office/Helper/CacheFilePath.cs

@@ -23,6 +23,7 @@ namespace PDF_Office.Helper
         List<string> CustomStamp = new List<string> { "CustomStamp" };
         List<string> SignatureFreeHand = new List<string> { "Signature" ,"FreeHand" };
         List<string> SignatureStamp = new List<string> { "Signature", "Stamp" };
+        List<string> MergeFile = new List<string> { "Temp" };
         public static CacheFilePath Instance => instance;
 
         private CacheFilePath()
@@ -61,6 +62,18 @@ namespace PDF_Office.Helper
                return CreateCacheDirectory(SignatureFreeHand);
             }
         }
+
+        /// <summary>
+        /// 合并文件的缓存文件夹
+        /// </summary>
+        public string MergeFilePath
+        {
+            get
+            {
+               return CreateCacheDirectory(MergeFile);
+            }
+        }
+        
         /// <summary>
         ///  在“文档”路径下创建缓存文件夹,传C:\Users\kdan\Documents\PDF Office 以后的文件夹名
         /// </summary>

+ 125 - 0
PDF Office/Model/Dialog/ToolsDialogs/MergeObject.cs

@@ -0,0 +1,125 @@
+using Prism.Mvvm;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PDF_Office.Model.Dialog.ToolsDialogs
+{
+    class MergeObject : BindableBase
+    {
+        private string docName;
+
+        /// <summary>
+        /// 文档名
+        /// </summary>
+        public string DocName
+        {
+            get { return docName; }
+            set
+            {
+                SetProperty(ref docName, value);
+            }
+        }
+
+        private string docSize;
+
+        /// <summary>
+        /// 文档大小
+        /// </summary>
+        public string DocSize
+        {
+            get { return docSize; }
+            set
+            {
+                SetProperty(ref docSize, value);
+            }
+        }
+
+        private string docPageCount;
+
+        /// <summary>
+        /// 文档页数
+        /// </summary>
+        public string DocPageCount
+        {
+            get { return docPageCount; }
+            set
+            {
+                SetProperty(ref docPageCount, value);
+            }
+        }
+
+        private string filePath;
+
+        /// <summary>
+        /// 文档文件路径
+        /// </summary>
+        public string FilePath
+        {
+            get { return filePath; }
+            set
+            {
+                SetProperty(ref filePath, value);
+            }
+        }
+
+        private string setPageRange;
+
+        /// <summary>
+        /// 设置的文档页面范围
+        /// </summary>
+        public string SetPageRange
+        {
+            get { return setPageRange; }
+            set
+            {
+                SetProperty(ref setPageRange, value);
+            }
+        }
+
+        private string password="";
+
+        /// <summary>
+        /// 密码
+        /// </summary>
+        public string Password
+        {
+            get { return password; }
+            set
+            {
+                SetProperty(ref password, value);
+            }
+        }
+
+        /// <summary>
+        /// 显示上半部分线
+        /// </summary>
+        private bool isBackwards = false;
+
+        public bool IsBackwards
+        {
+            get { return isBackwards; }
+            set
+            {
+                SetProperty(ref isBackwards, value);
+            }
+        }
+
+        /// <summary>
+        /// 显示下半部分线
+        /// </summary>
+        private bool isForward=false;
+
+        public bool IsForward
+        {
+            get { return isForward; }
+            set
+            {
+                SetProperty(ref isForward, value);
+            }
+        }
+
+    }
+}

+ 5 - 0
PDF Office/Model/DialogNames.cs

@@ -26,6 +26,11 @@ namespace PDF_Office.Model
         /// </summary>
         public static string CompressDialog = "CompressDialog";
 
+        /// <summary>
+        /// 合并弹窗
+        /// </summary>
+        public static string MergeDialog = "MergeDialog";
+
         /// <summary>
         /// 解压缩弹窗
         /// </summary>

+ 10 - 0
PDF Office/PDF Office.csproj

@@ -247,6 +247,7 @@
     <Compile Include="DataConvert\CreateTimeToDate.cs" />
     <Compile Include="DataConvert\FileFormatToIconConvert.cs" />
     <Compile Include="DataConvert\GroupHeaderConverter.cs" />
+    <Compile Include="DataConvert\IndexConverter .cs" />
     <Compile Include="DataConvert\IntToBooleanConvert.cs" />
     <Compile Include="DataConvert\FileToImageSourceConvert.cs" />
     <Compile Include="DataConvert\IntAndTagToBoolMultiBinding.cs" />
@@ -315,6 +316,7 @@
     <Compile Include="Model\Dialog\HomePageToolsDialogs\HomePageBatchProcessing\HomePageSetPasswordDialogModel.cs" />
     <Compile Include="Model\Dialog\HomePageToolsDialogs\HomePagePictureToPDFDialogModel.cs" />
     <Compile Include="Model\Dialog\ToolsDialogs\CompressDialogModel\CompressDialogModel.cs" />
+    <Compile Include="Model\Dialog\ToolsDialogs\MergeObject.cs" />
     <Compile Include="Model\Dialog\ToolsDialogs\SaftyDialogs\CheckPasswordDialogModel.cs" />
     <Compile Include="Model\Dialog\ToolsDialogs\SaftyDialogs\DeleteSafetySettintgsModel.cs" />
     <Compile Include="Model\Dialog\ToolsDialogs\SaftyDialogs\SetPasswordDialogModel.cs" />
@@ -349,6 +351,7 @@
       <DesignTime>True</DesignTime>
       <AutoGen>True</AutoGen>
     </Compile>
+    <Compile Include="ViewModels\Dialog\ToolsDialogs\MergeDialogViewModel.cs" />
     <Compile Include="ViewModels\EditTools\Bates\BatesContentViewModel.cs" />
     <Compile Include="ViewModels\EditTools\Bates\BatesCreateContentViewModel.cs" />
     <Compile Include="ViewModels\EditTools\Bates\BatesDocumentContentViewModel.cs" />
@@ -619,6 +622,9 @@
     <Compile Include="Views\Dialog\ToolsDialogs\CompressDialogs\CompressProgressBarDialog.xaml.cs">
       <DependentUpon>CompressProgressBarDialog.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Views\Dialog\ToolsDialogs\MergeDialog.xaml.cs">
+      <DependentUpon>MergeDialog.xaml</DependentUpon>
+    </Compile>
     <Compile Include="Views\Dialog\ToolsDialogs\SaftyDialogs\CheckPasswordDialog.xaml.cs">
       <DependentUpon>CheckPasswordDialog.xaml</DependentUpon>
     </Compile>
@@ -1175,6 +1181,10 @@
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
     </Page>
+    <Page Include="Views\Dialog\ToolsDialogs\MergeDialog.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
     <Page Include="Views\EditTools\Bates\BatesContent.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>

+ 295 - 0
PDF Office/ViewModels/Dialog/ToolsDialogs/MergeDialogViewModel.cs

@@ -0,0 +1,295 @@
+using ComPDFKit.PDFDocument;
+using ComPDFKit.PDFPage;
+using Microsoft.Win32;
+using PDF_Office.Model;
+using PDF_Office.Model.Dialog.ToolsDialogs;
+using PDF_Office.Views.Dialog;
+using Prism.Commands;
+using Prism.Mvvm;
+using Prism.Services.Dialogs;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Controls;
+
+namespace PDF_Office.ViewModels.Dialog.ToolsDialogs
+{
+    class MergeDialogViewModel : BindableBase, IDialogAware
+    {
+        enum PageSizeType
+        { 
+            kDefault=0,
+            A4 = 1,
+            A3 = 2,
+            Letter = 3,
+            Legal = 4,
+            Customized = 5,
+        }
+
+        #region 框架内容
+
+        public string Title => "";
+
+        public event Action<IDialogResult> RequestClose;
+
+        public bool CanCloseDialog()
+        {
+            return true;
+        }
+
+        public void OnDialogClosed()
+        {
+
+        }
+
+        public void OnDialogOpened(IDialogParameters parameters)
+        {
+
+        }
+
+        #endregion
+        #region 定义与初始化
+
+        public ObservableCollection<MergeObject> MergeObjectlist { get; set; }
+
+        public DelegateCommand CancelCommand { get; set; }
+        public DelegateCommand MergeCommand { get; set; }
+        public DelegateCommand AddFilesCommand { get; set; }
+        public DelegateCommand ClearCommand { get; set; }
+        public DelegateCommand<object> SetPageSizeTypeCommand { get; set; }
+
+        public IDialogService dialogs;
+
+        private PageSizeType pageSizeType = PageSizeType.kDefault;
+
+
+        public MergeDialogViewModel(IDialogService dialogService)
+        {
+            dialogs = dialogService;
+            MergeObjectlist = new ObservableCollection<MergeObject>();
+            CancelCommand = new DelegateCommand(Cancel);
+            AddFilesCommand = new DelegateCommand(AddFiles);
+            MergeCommand = new DelegateCommand(Merge);
+            ClearCommand = new DelegateCommand(Clear);
+            SetPageSizeTypeCommand = new DelegateCommand<object>(SetPageSizeType);
+        }
+
+        #endregion
+
+        #region 私有方法
+        private void Cancel()
+        {
+            RequestClose.Invoke(new DialogResult(ButtonResult.Cancel));
+        }
+
+        private void AddFiles()
+        {
+            OpenFileDialog openFile = new OpenFileDialog();
+            openFile.Multiselect = true;
+            openFile.Filter = "Files(*pdf;*.bmp;*.gif;*.jpeg;*.jpg;*.png;*.tiff)|*.pdf;*.bmp;*.gif;*.jpeg;*.jpg;*.png;*.tiff|" +
+                "(*.pdf)|*.pdf|" +
+                "(*.bmp)|*.bmp|" +
+                "(*.gif)|*.gif|" +
+                "(*.jpeg)|*.jpeg|" +
+                "(*.jpg)|*.jpg|" +
+                "(*.png)|*.png|" +
+                "(*.tiff)|*.tiff";
+            if (openFile.ShowDialog() == false)
+            {
+                return;
+            }
+            bool result = true;
+            for (int i = 0; i < openFile.FileNames.Length; i++)
+            {
+                MergeObject mergeObject = new MergeObject();
+                mergeObject.FilePath = openFile.FileNames[i];
+                CPDFDocument doc = CPDFDocument.InitWithFilePath(mergeObject.FilePath);
+
+                if (doc.IsLocked)
+                {
+                    DialogParameters value = new DialogParameters();
+                    value.Add(ParameterNames.PDFDocument, doc);
+                    dialogs.ShowDialog(DialogNames.VerifyPassWordDialog, value, e =>
+                    {
+
+                        if (e.Result != ButtonResult.OK)
+                        {
+                            result = false;
+                        }
+                        else
+                        {
+                            mergeObject.Password = e.Parameters.GetValue<string>(ParameterNames.PassWord);
+                        }
+
+                    });
+                    if (!result)
+                    {
+                        doc.Release();
+                        continue;
+                    }
+                }
+                mergeObject.DocName = doc.FileName;
+                mergeObject.DocPageCount = doc.PageCount.ToString() + "页";
+                mergeObject.DocSize = GetFileSize(mergeObject.FilePath);
+                MergeObjectlist.Add(mergeObject);
+                doc.Release();
+            }
+        }
+
+        private string GetFileSize(string path)
+        {
+            System.IO.FileInfo fileInfo = null;
+            try
+            {
+                fileInfo = new System.IO.FileInfo(path);
+            }
+            catch
+            {
+                return "0KB";
+            }
+            if (fileInfo != null && fileInfo.Exists)
+            {
+
+                var size = Math.Round(fileInfo.Length / 1024.0, 0);
+                if (size > 1024)
+                {
+                    var sizeDouble = Math.Round(size / 1024.0, 2);
+                    if (sizeDouble > 1024)
+                    {
+                        sizeDouble = Math.Round(sizeDouble / 1024.0, 2);
+                        return sizeDouble + "G";
+                    }
+                    else
+                        return sizeDouble + "M";
+                }
+                else
+                {
+
+                    return (int)System.Math.Ceiling(size) + "KB";
+                }
+
+            }
+            else
+            {
+                return "0KB";
+            }
+        }
+
+        private void Merge()
+        {
+            bool result = true;
+            CPDFDocument SaveDoc = CPDFDocument.CreateDocument();
+            for (int i = 0; i < MergeObjectlist.Count; i++)
+            {
+                CPDFDocument tempDoc = CPDFDocument.InitWithFilePath(MergeObjectlist[i].FilePath);
+                if (!string.IsNullOrEmpty(MergeObjectlist[i].Password))
+                {
+                    tempDoc.UnlockWithPassword(MergeObjectlist[i].Password);
+                }
+                result = SaveDoc.ImportPages(tempDoc, "1-1"/*MergeObjectlist[i].SetPageRange*/);
+                if (!result)
+                {
+                    SaveDoc.Release();
+                    tempDoc.Release();
+                    return;
+                }
+                tempDoc.Release();
+            }
+
+            string path = App.CachePath.MergeFilePath;
+            string name = Guid.NewGuid().ToString();
+            if (!string.IsNullOrEmpty(path))
+            {
+                path = System.IO.Path.Combine(path, name);
+            }
+            else
+            {
+                return;
+            }
+            System.Windows.Rect rect = new System.Windows.Rect();
+
+            switch (pageSizeType)
+            {
+                case PageSizeType.kDefault:
+                    break;
+                case PageSizeType.A4:
+                    break;
+                case PageSizeType.A3:
+                    break;
+                case PageSizeType.Letter:
+                    break;
+                case PageSizeType.Legal:
+                    break;
+                case PageSizeType.Customized:
+                    break;
+                default:
+                    break;
+            }
+
+            if (rect.Width>0&& rect.Height>0)
+            {
+                //裁剪
+                for (int i = 0; i < SaveDoc.PageCount; i++)
+                {
+                    CPDFPage page = SaveDoc.PageAtIndex(i);
+                    page.CropPage(CPDFDisplayBox.MediaBox, rect);
+                }
+            }
+
+            bool saveResult = SaveDoc.WriteToFilePath(path);
+            SaveDoc.Release();
+
+            DialogParameters valuePairs = new DialogParameters();
+            valuePairs.Add(ParameterNames.FilePath, path);
+            RequestClose.Invoke(new DialogResult(ButtonResult.OK, valuePairs));
+        }
+
+        private void Clear()
+        {
+            MergeObjectlist.Clear();
+        }
+
+        private void SetPageSizeType(object button)
+        {
+            if (button is RadioButton)
+            {
+                pageSizeType= (PageSizeType)Convert.ToInt32((button as RadioButton).Tag);
+            }
+        }
+
+        #endregion
+
+        #region 公开方法
+
+        public void DeleteItem(MergeObject merge)
+        {
+            MergeObjectlist.Remove(merge);
+        }
+
+        public void MoveMerge(MergeObject targetNode, MergeObject soureNode)
+        {
+            int targetindex = MergeObjectlist.IndexOf(targetNode);
+            MergeObjectlist.Remove(soureNode);
+            if (targetNode.IsForward)
+            {
+                if (targetindex+1<MergeObjectlist.Count)
+                {
+                    MergeObjectlist.Insert(targetindex + 1, soureNode);
+                }
+                else
+                {
+                    MergeObjectlist.Add(soureNode);
+                }
+            }
+            else
+            {
+                MergeObjectlist.Insert(targetindex, soureNode);
+            }
+        }
+
+        #endregion
+    }
+}

+ 7 - 0
PDF Office/ViewModels/Tools/ToolsBarContentViewModel.cs

@@ -32,6 +32,8 @@ namespace PDF_Office.ViewModels.Tools
 
         public DelegateCommand CompressCommand { get; set; }
 
+        public DelegateCommand MergeCommand { get; set; }
+
         public DelegateCommand SetPasswordCommand { get; set; }
 
         public DelegateCommand CancelPasswordCommand { get; set; }
@@ -48,6 +50,7 @@ namespace PDF_Office.ViewModels.Tools
             this.eventAggregator= eventAggregator;
             unicode = App.mainWindowViewModel.SelectedItem.Unicode;
             CompressCommand = new DelegateCommand(OpenCompressDialog);
+            MergeCommand = new DelegateCommand(MergeDialog);
             SetPasswordCommand = new DelegateCommand(OpenSetPasswordDialog);
             CancelPasswordCommand = new DelegateCommand(OpenCancelPasswordDialog);
             SetEditToolsCommand = new DelegateCommand<object>(SetEditTools);
@@ -69,6 +72,10 @@ namespace PDF_Office.ViewModels.Tools
             value.Add(ParameterNames.PDFDocument, PDFViewer.Document);
             dialogs.ShowDialog(DialogNames.CompressDialog, value, e => {  });
         }
+        private void MergeDialog()
+        {
+            dialogs.ShowDialog(DialogNames.MergeDialog, null, e => {  });
+        }
 
         private void OpenSetPasswordDialog()
         {

+ 151 - 0
PDF Office/Views/Dialog/ToolsDialogs/MergeDialog.xaml

@@ -0,0 +1,151 @@
+<UserControl x:Class="PDF_Office.Views.Dialog.ToolsDialogs.MergeDialog"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:PDF_Office.Views.Dialog.PageEditDialogs"
+             mc:Ignorable="d" 
+             xmlns:prism="http://prismlibrary.com/" xmlns:customcontrol="clr-namespace:PDF_Office.CustomControl" xmlns:dataconvert="clr-namespace:PDF_Office.DataConvert" xmlns:toolsdialogs="clr-namespace:PDF_Office.ViewModels.Dialog.ToolsDialogs" xmlns:i="http://schemas.microsoft.com/xaml/behaviors" d:DataContext="{d:DesignInstance Type=toolsdialogs:MergeDialogViewModel}"
+             prism:Dialog.WindowStyle="{StaticResource DialogWindowStyle}"
+             Height="596" Width="947">
+    <UserControl.Resources>
+        <dataconvert:IndexConverter x:Key="IndexConverter"/>
+        <dataconvert:BoolToVisible x:Key="BoolToVisible" />
+    </UserControl.Resources>
+    <Grid Background="#FFFFFF">
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition Width="8"/>
+            <ColumnDefinition Width="*"/>
+            <ColumnDefinition Width="8"/>
+            <ColumnDefinition Width="*"/>
+            <ColumnDefinition Width="8"/>
+        </Grid.ColumnDefinitions>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="8"/>
+            <RowDefinition Height="*"/>
+            <RowDefinition Height="8"/>
+            <RowDefinition Height="*"/>
+            <RowDefinition Height="16"/>
+            <RowDefinition Height="*"/>
+            <RowDefinition Height="8"/>
+        </Grid.RowDefinitions>
+        <Button Grid.Column="1" Grid.Row="1" Content="Add Files"  Width="80" Height="32" HorizontalAlignment="Center" VerticalAlignment="Center" Style="{StaticResource btn.sec}" Command="{Binding AddFilesCommand}"/>
+        <Border Grid.Column="1" Grid.Row="3">
+            <Grid >
+                <ListView x:Name="MergeView" AllowDrop="True" 
+                Drop="MergeView_Drop"
+                DragOver="MergeView_DragOver"
+                DragLeave="MergeView_DragLeave"
+                PreviewMouseMove="MergeView_PreviewMouseMove" 
+                ItemsSource="{Binding MergeObjectlist}"
+                ScrollViewer.HorizontalScrollBarVisibility="Disabled"
+                HorizontalContentAlignment="Stretch">
+                    <ListView.ItemTemplate>
+                        <DataTemplate  >
+                            <Grid x:Name="ContentPanel" >
+                                <Grid.ColumnDefinitions>
+                                    <ColumnDefinition Width="*"/>
+                                    <ColumnDefinition Width="*"/>
+                                    <ColumnDefinition Width="*"/>
+                                    <ColumnDefinition Width="*"/>
+                                    <ColumnDefinition Width="*"/>
+                                </Grid.ColumnDefinitions>
+                                <TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, 
+                                         AncestorType={x:Type ListViewItem}}, 
+                                         Converter={StaticResource IndexConverter}}"
+                                 VerticalAlignment="Center"  HorizontalAlignment="Center"/>
+                                <Image Grid.Column="1" Width="50" Height="50" HorizontalAlignment="Center" VerticalAlignment="Center" Source="pack://application:,,,/PDF Office;component/Resources/Dialog/AddImage.png"  />
+                                <StackPanel Grid.Column="2">
+                                    <TextBlock Text="{Binding DocName}"  HorizontalAlignment="Left" VerticalAlignment="Top"/>
+                                    <TextBlock Text="{Binding DocSize}"  HorizontalAlignment="Left" VerticalAlignment="Top"/>
+                                    <TextBlock Text="{Binding DocPageCount}"  HorizontalAlignment="Left" VerticalAlignment="Top"/>
+                                </StackPanel>
+                                <StackPanel Grid.Column="3" HorizontalAlignment="Center" VerticalAlignment="Center">
+                                    <customcontrol:WritableComboBox SelectedIndex="0"/>
+                                </StackPanel>
+                                <StackPanel Grid.Column="4" VerticalAlignment="Center">
+                                    <Button Content="删除" Click="Delete_Click"/>
+                                </StackPanel>
+                                <StackPanel Grid.ColumnSpan="5" Visibility="{Binding IsForward, Converter={StaticResource BoolToVisible}}" Orientation="Horizontal" HorizontalAlignment="Stretch" VerticalAlignment="Bottom">
+                                    <Line  Margin="0,0,0,0" X1="0"  X2="{Binding ElementName=ContentPanel,Path=ActualWidth}" HorizontalAlignment="Stretch" StrokeThickness="1.5" Stroke="#FF0078D7" VerticalAlignment="Bottom"/>
+                                </StackPanel>
+
+                                <StackPanel Grid.ColumnSpan="5" Visibility="{Binding IsBackwards, Converter={StaticResource BoolToVisible}}" Orientation="Horizontal" HorizontalAlignment="Stretch" VerticalAlignment="Top">
+                                    <Line  X1="0"  X2="{Binding ElementName=ContentPanel,Path=ActualWidth}" HorizontalAlignment="Stretch" StrokeThickness="1.5" Stroke="Red" VerticalAlignment="Bottom"/>
+                                </StackPanel>
+                            </Grid>
+                        </DataTemplate>
+                    </ListView.ItemTemplate>
+                </ListView>
+            </Grid>
+        </Border>
+        <Border Grid.Column="3" Grid.Row="3">
+            <Grid>
+                <Grid.RowDefinitions>
+                    <RowDefinition Height="20"/>
+                    <RowDefinition Height="*"/>
+                </Grid.RowDefinitions>
+                <TextBlock Text=" Page Size"/>
+                <StackPanel Grid.Row="1">
+                    <RadioButton Content="Original page size" Tag="0">
+                        <i:Interaction.Triggers>
+                            <i:EventTrigger EventName="Checked">
+                                <i:InvokeCommandAction Command="{Binding SetPageSizeTypeCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=RadioButton}}" PassEventArgsToCommand="True" />
+                            </i:EventTrigger>
+                        </i:Interaction.Triggers>
+                    </RadioButton>
+                    <RadioButton Content="A4" Tag="1">
+                        <i:Interaction.Triggers>
+                            <i:EventTrigger EventName="Checked">
+                                <i:InvokeCommandAction Command="{Binding SetPageSizeTypeCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=RadioButton}}" PassEventArgsToCommand="True" />
+                            </i:EventTrigger>
+                        </i:Interaction.Triggers>
+                    </RadioButton>
+                    <RadioButton Content="A3" Tag="2">
+                        <i:Interaction.Triggers>
+                            <i:EventTrigger EventName="Checked">
+                                <i:InvokeCommandAction Command="{Binding SetPageSizeTypeCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=RadioButton}}" PassEventArgsToCommand="True" />
+                            </i:EventTrigger>
+                        </i:Interaction.Triggers>
+                    </RadioButton>
+                    <RadioButton Content="U.S.Letter" Tag="3">
+                        <i:Interaction.Triggers>
+                            <i:EventTrigger EventName="Checked">
+                                <i:InvokeCommandAction Command="{Binding SetPageSizeTypeCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=RadioButton}}" PassEventArgsToCommand="True" />
+                            </i:EventTrigger>
+                        </i:Interaction.Triggers>
+                    </RadioButton>
+                    <RadioButton Content="U.S.Legal" Tag="4">
+                        <i:Interaction.Triggers>
+                            <i:EventTrigger EventName="Checked">
+                                <i:InvokeCommandAction Command="{Binding SetPageSizeTypeCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=RadioButton}}" PassEventArgsToCommand="True" />
+                            </i:EventTrigger>
+                        </i:Interaction.Triggers>
+                    </RadioButton>
+                    <RadioButton x:Name="Customized" Content="Customized" Tag="5">
+                        <i:Interaction.Triggers>
+                            <i:EventTrigger EventName="Checked">
+                                <i:InvokeCommandAction Command="{Binding SetPageSizeTypeCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=RadioButton}}" PassEventArgsToCommand="True" />
+                            </i:EventTrigger>
+                        </i:Interaction.Triggers>
+                    </RadioButton>
+                    <Grid IsEnabled="{Binding ElementName=Customized, Path=IsChecked}">
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="*"/>
+                            <ColumnDefinition Width="20"/>
+                            <ColumnDefinition Width="*"/>
+                        </Grid.ColumnDefinitions>
+                        <customcontrol:TextBoxEx PlaceholderText="595"/>
+                        <TextBlock Text="X" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
+                        <customcontrol:TextBoxEx PlaceholderText="841" Grid.Column="2"/>
+                    </Grid>
+                </StackPanel>
+            </Grid>
+        </Border>
+        <Button Grid.Column="3" Grid.Row="5"  Content="Clear"  HorizontalAlignment="Left" VerticalAlignment="Bottom" Width="80" Height="32" Style="{StaticResource btn.sec}" Command="{Binding ClearCommand}"/>
+        <StackPanel Grid.Column="3" Grid.Row="5" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Bottom">
+            <Button Content="Cancel" Width="80" Height="32" Style="{StaticResource btn.sec}" Command="{Binding CancelCommand}"/>
+            <Button Content="Add" Width="80" Height="32" Margin="16,0,0,0" Style="{StaticResource Btn.cta}" Command="{Binding MergeCommand}"/>
+        </StackPanel>
+    </Grid>
+</UserControl>

+ 156 - 0
PDF Office/Views/Dialog/ToolsDialogs/MergeDialog.xaml.cs

@@ -0,0 +1,156 @@
+using PDF_Office.Helper;
+using PDF_Office.Model.Dialog.ToolsDialogs;
+using PDF_Office.ViewModels.Dialog.ToolsDialogs;
+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;
+
+namespace PDF_Office.Views.Dialog.ToolsDialogs
+{
+    /// <summary>
+    /// MergeDialog.xaml 的交互逻辑
+    /// </summary>
+    public partial class MergeDialog : UserControl
+    {
+        /// <summary>
+        /// 当前显示了线段的Item
+        /// </summary>
+        private ListViewItem listViewItem = null;
+        public MergeDialog()
+        {
+            InitializeComponent();
+        }
+
+        private void Delete_Click(object sender, RoutedEventArgs e)
+        {
+            (DataContext as MergeDialogViewModel).DeleteItem((sender as Button).DataContext as MergeObject);
+        }
+
+        private void MergeView_PreviewMouseMove(object sender, MouseEventArgs e)
+        {
+            if (e.LeftButton == MouseButtonState.Pressed)
+            {
+                var pos = e.GetPosition(MergeView);
+                HitTestResult result = VisualTreeHelper.HitTest(MergeView, pos);
+                if (result == null)
+                {
+                    return;
+                }
+                var treeViewItem = CommonHelper.FindVisualParent<ListViewItem>(result.VisualHit);
+                if (treeViewItem == null)
+                {
+                    return;
+                }
+                DataObject dataObj = new DataObject(treeViewItem);
+                DragDrop.DoDragDrop(MergeView, dataObj, DragDropEffects.Move);
+                return;
+            }
+
+        }
+
+        private void MergeView_Drop(object sender, DragEventArgs e)
+        {
+            ListViewItem souredata = e.Data.GetData(typeof(ListViewItem)) as ListViewItem;
+            MergeObject soureNode = souredata.DataContext as MergeObject;
+            Point pos = e.GetPosition(MergeView);
+            HitTestResult result = VisualTreeHelper.HitTest(MergeView, pos);
+            if (result == null)
+            {
+                HiddenaAllLine();
+                return;
+            }
+            ListViewItem targetitem = CommonHelper.FindVisualParent<ListViewItem>(result.VisualHit);
+            if (targetitem == null)
+            {
+                HiddenaAllLine();
+                return;
+            }
+            MergeObject targetNode = targetitem.DataContext as MergeObject;
+            if (soureNode.Equals(targetNode))
+            {
+                HiddenaAllLine();
+                return;
+            }
+           (DataContext as MergeDialogViewModel).MoveMerge(targetNode, soureNode);
+
+            HiddenaAllLine();
+        }
+
+        private void MergeView_DragOver(object sender, DragEventArgs e)
+        {
+            ListViewItem sourceitem = e.Data.GetData(typeof(ListViewItem)) as ListViewItem;
+            if (sourceitem == null)
+            {
+                return;
+            }
+            Point pos = e.GetPosition(MergeView);
+            HitTestResult result = VisualTreeHelper.HitTest(MergeView, pos);
+            if (result != null)
+            {
+                ListViewItem treeviewitem = CommonHelper.FindVisualParent<ListViewItem>(result.VisualHit);
+                if (treeviewitem != null)
+                {
+                    if (listViewItem == null)
+                    {
+                        listViewItem = treeviewitem;
+                    }
+                    else if (!listViewItem.Equals(treeviewitem))
+                    {
+                        (listViewItem.DataContext as MergeObject).IsBackwards = false;
+                        (listViewItem.DataContext as MergeObject).IsForward = false;
+                        listViewItem = treeviewitem;
+                    }
+                    else
+                    {
+                        (listViewItem.DataContext as MergeObject).IsBackwards = false;
+                        (listViewItem.DataContext as MergeObject).IsForward = false;
+                    }
+                    if (listViewItem.Equals(sourceitem))
+                    {
+                        return;
+                    }
+
+                    //鼠标位于上半部分显示实线,下半部分显示虚线
+                    Point p = treeviewitem.TranslatePoint(new Point(0, 0), MergeView);
+                    if (pos.Y < p.Y + 16)
+                    {
+                        (treeviewitem.DataContext as MergeObject).IsBackwards = true;
+                    }
+                    else
+                    {
+                        (treeviewitem.DataContext as MergeObject).IsForward = true;
+                    }
+                }
+            }
+        }
+
+        private void MergeView_DragLeave(object sender, DragEventArgs e)
+        {
+            HiddenaAllLine();
+        }
+
+        /// <summary>
+        /// 清理线条显示,以及相关缓存对象
+        /// </summary>
+        private void HiddenaAllLine()
+        {
+            if (listViewItem != null)
+            {
+                (listViewItem.DataContext as MergeObject).IsBackwards = false;
+                (listViewItem.DataContext as MergeObject).IsForward = false;
+                listViewItem = null;
+            }
+        }
+    }
+}

+ 0 - 1
PDF Office/Views/PropertyPanel/AnnotPanel/SignatureAnnotProperty.xaml.cs

@@ -34,7 +34,6 @@ namespace PDF_Office.Views.PropertyPanel.AnnotPanel
 
         private void Save_Click(object sender, RoutedEventArgs e)
         {
-
             MenuItem item = sender as MenuItem;
             (DataContext as SignatureAnnotPropertyViewModel).SaveToPath(item.Tag.ToString(), item.DataContext as Signature);
         }

+ 1 - 1
PDF Office/Views/Tools/ToolsBarContent.xaml

@@ -23,7 +23,7 @@
                         <TextBlock Text="压缩" VerticalAlignment="Center"></TextBlock>
                     </StackPanel>
                 </Button>
-                <Button Style="{StaticResource InsideBarBtnStyle }" >
+                <Button Style="{StaticResource InsideBarBtnStyle }"  Command="{Binding MergeCommand}" >
 
                     <StackPanel Orientation="Horizontal" Margin="12 0 12 0">
                         <StackPanel Margin="0,0,8,0">