本篇文章主要是对文件管理系统的项目梳理及总结:
系统语言本地化
因为系统要求双语,在设计的时候用到了一个窗体参数:
设置了这两个参数后,可以在解决方案里看到两个资源文件,一个是设计语言的,一个是本地化的。
可以在资源文件里修改控件的显示名称。
在系统运行的时候就会根据自己本地的语言进行本地化。但是这个用起来不是很方便,就是如果设置了系统本地化之后,即改变了上面那两个参数的值之后就不能再添加控件,如果需要添加控件要把这两个参数恢复默认,然后添加控件,还需要重新设置本地化资源文件里控件的名称。
而且这种方式只能对前端添加的控件进行本地化设置,如果是后台直接写的,比如GridControl的列名,Messagebox的消息,不能进行本地化设置,这点就让系统用起来显得不太统一。
窗体动画
看一下显示效果,不好截动图,这里设置的从左上角开始显示。
但是如果设置了MID窗体嵌入Panel的话就用不了了
#region AnimateWindow
[DllImport("user32.dll", EntryPoint = "AnimateWindow")]
private static extern bool AnimateWindow(IntPtr handle, int ms, int flags);
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
//AnimateWindow(this.Handle, 1000, 0x20010); // 居中逐渐显示。
AnimateWindow(this.Handle, 1000, 0xA0000); // 淡入淡出效果。
//AnimateWindow(this.Handle, 1000, 0x60004); // 自上向下。
//AnimateWindow(this.Handle, 1000, 0x20004); // 自上向下。
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
//AnimateWindow(this.Handle, 1000, 0x10010); // 居中逐渐隐藏。
AnimateWindow(this.Handle, 1000, 0x90000); // 淡入淡出效果。
//AnimateWindow(this.Handle, 1000, 0x50008); // 自下而上。
//AnimateWindow(this.Handle, 1000, 0x10008); // 自下而上。
}
#endregion
一个是窗体显示,一个是关闭窗体的时候,但是这个动画效果不是很多,然后像淡入淡出的效果用起来很像是卡顿的,本来用动画效果就是为了增强用户体验感,解决窗体建切换的闪屏的问题,但是不是很好用吧,这个功能,两个窗体切换的时候只能一个窗体用动画,不然两个动画显得特别刻意。
这期间还查到一个解决窗体切换时闪屏问题的,但是也是不太好用,和窗体动画一结合就像卡屏了。
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000;
return cp;
}
}
TileControl
一个客户一个组,客户占两格,项目占一格。
后台根据数据库自动实例化分组和项目:
public void Group()
{
CustomerTile.Groups.Clear();
sql = "select ID,CustomerName from Customer ";
DataTable dt;
if (!DB.AF.execSql(sql, sqlconn, out dt))
{
XtraMessageBox.Show("获取服务器客户失败!");
return;
}
for (int i = 0; i < dt.Rows.Count; i++)
{
TileGroup group = new TileGroup();
group.Text = dt.Rows[i]["ID"].ToString();
CustomerTile.Groups.Add(group);
TileItem item = new TileItem();
item.Name = dt.Rows[i]["CustomerName"].ToString();
item.Text = dt.Rows[i]["CustomerName"].ToString();
item.ItemSize = TileItemSize.Wide;//设置组头第一个Item占两格
//内置了一些客户的图片。添加客户的时候Item就会显示这些图片,如果没内置,就是绿色的瓷砖。
switch (item.Name)
{
case "Apple": item.Image = Resources.Apple; break;
case "Amazon": item.Image = Resources.Amazon; break;
case "Facebook": item.Image = Resources.Facebook; break;
case "Google": item.Image = Resources.Google; break;
case "Huawei": item.Image = Resources.Huawei; break;
case "Microsoft": item.Image = Resources.Microsoft; break;
}
item.ImageScaleMode = TileItemImageScaleMode.Stretch;
item.Appearance.BackColor = Color.White;//这里设置了瓷砖的背景色,不然会有边框,没查到怎么让图片完全填充。
item.Appearance.ForeColor = Color.Black;
//瓷砖点击事件,客户和项目点击事件不同,这是客户的点击事件,下面是项目的
item.ItemClick += Item_Click;
group.Items.Add(item);
//这边可以加一个top,因为Item太多了会放在右边,不好看
sql = "select ID,ProjectName from Projects where CusName= '" + dt.Rows[i]["CustomerName"].ToString() + "'";
DataTable dtPro;
if (!DB.AF.execSql(sql, sqlconn, out dtPro))
{
XtraMessageBox.Show("获取服务器项目失败!");
}
for (int j = 0; j < dtPro.Rows.Count; j++)
{
TileItem items = new TileItem();
items = new TileItem();
items.Name = dtPro.Rows[j]["ID"].ToString();
items.Text = dtPro.Rows[j]["ProjectName"].ToString();
items.ItemSize = TileItemSize.Default;
//这里就是项目的点击事件
items.ItemClick += Items_Click;
group.Items.Add(items);
}
}
}
TileControl还是会经常做触屏界面,Win8的风格,后面把项目的Item图片换成文件夹,文字放下面居中也是挺好看的,这个控件还是很好用的,就是网上能查到的资料太少了。
Rowcount是改变一列显示几行Item的。
TreeList和BreadCrumbEdit
Dev里面并没有做TreeList和BreadCrumbEdit的关联,所以只能自己去写,官网上有给出了一个例子,我大致看了一下,后面再补充这块。
文件上传
上传文件有两种方式:
- 从剪切板上获取文件的路径,在程序中粘贴时直接上传
- 点击上传按钮,弹出选择文件的对话框。选中之后上传
关于版本管理:做文件管理系统不仅需要可以方便、快捷、有效、安全的分享当前版本的文件,更需要对使用者上传的历史版本进行管理,目前想到的只是存储,还没有到管理阶段。
这个文件管理系统是把文件通过上传共享文件夹的形式保存,不是转二进制存在数据库里面。
所以对版本的管理暂时想到的方法是在共享服务器里建立两个文件夹,一个用来存放当前版本的文件,用来共享、预览,另外一个文件夹里根据每个文件的名称各自建立自己的文件夹,每次上传文件时,文件以文件名+版本号进行保存。
共享文件夹已经存在同名文件的情况下继续上传是会直接覆盖的,所以存放当前版本的文件夹不需要进行其他操作,如果上传的文件名称和之前版本的一致,就直接升级,但是也会有待升级的版本和共享服务器上该文件的名称不一致的情况,因此做个了单独的界面:
先放一个操作共享文件夹的通用代码,后续会用到这几个函数:
#region 共享文件夹
public static bool connectState(string path)
{
return connectState(path, "", "");
}
/// <summary>
/// 连接远程共享文件夹
/// </summary>
/// <param name="path">远程共享文件夹的路径</param>
/// <param name="userName">用户名</param>
/// <param name="passWord">密码</param>
/// <returns></returns>
public static bool connectState(string path, string userName, string passWord)
{
bool Flag = false;
System.Diagnostics.Process proc = new System.Diagnostics.Process();
try
{
proc.StartInfo.FileName = "cmd.exe";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardInput = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.CreateNoWindow = true;
proc.Start();
proc.StandardInput.WriteLine("net use * /del /y");
string dosLine = "net use " + path + " " + passWord + " /user:" + userName;
proc.StandardInput.WriteLine(dosLine);
proc.StandardInput.WriteLine("exit");
while (!proc.HasExited)
{
proc.WaitForExit(1000);
}
string errormsg = proc.StandardError.ReadToEnd();
proc.StandardError.Close();
if (string.IsNullOrEmpty(errormsg))
{
Flag = true;
}
else
{
MessageBox.Show(errormsg);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
proc.Close();
proc.Dispose();
}
return Flag;
}
/// <summary>
/// 向远程文件夹保存本地内容,或者从远程文件夹下载文件到本地
/// </summary>
/// <param name="src">要保存的文件的路径,如果保存文件到共享文件夹,这个路径就是本地文件路径如:@"D:\1.avi"</param>
/// <param name="dst">保存文件的路径,不含名称及扩展名</param>
/// <param name="fileName">保存文件的名称以及扩展名</param>
public static void Transport(string src, string dst, string fileName)
{
FileStream inFileStream = new FileStream(src, FileMode.Open);
if (!Directory.Exists(dst))
{
Directory.CreateDirectory(dst);
}
dst = dst + fileName;
if (!File.Exists(dst))
{
FileStream outFileStream = new FileStream(dst, FileMode.Create, FileAccess.Write);
byte[] buf = new byte[inFileStream.Length];
int byteCount;
while ((byteCount = inFileStream.Read(buf, 0, buf.Length)) > 0)
{
outFileStream.Write(buf, 0, byteCount);
}
inFileStream.Flush();
inFileStream.Close();
outFileStream.Flush();
outFileStream.Close();
}
else if (File.Exists(dst))
{
File.Delete(dst);
FileStream outFileStream = new FileStream(dst, FileMode.Create, FileAccess.Write);
byte[] buf = new byte[inFileStream.Length];
int byteCount;
while ((byteCount = inFileStream.Read(buf, 0, buf.Length)) > 0)
{
outFileStream.Write(buf, 0, byteCount);
}
inFileStream.Flush();
inFileStream.Close();
outFileStream.Flush();
outFileStream.Close();
}
}
/// <summary>
/// 从远程服务器下载文件到本地
/// </summary>
/// <param name="src">下载到本地后的文件路径,包含文件的扩展名</param>
/// <param name="dst">远程服务器路径(共享文件夹路径)</param>
/// <param name="fileName">远程服务器(共享文件夹)中的文件名称,包含扩展名</param>
public static void TransportRemoteToLocal(string src, string dst, string fileName) //src:下载到本地后的文件路径 dst:远程服务器路径 fileName:远程服务器dst路径下的文件名
{
if (!Directory.Exists(Path.GetDirectoryName(src) + @"\"))
{
Directory.CreateDirectory(Path.GetDirectoryName(src) + @"\");
}
dst = dst + fileName;
FileStream inFileStream = new FileStream(dst, FileMode.Open); //远程服务器文件 此处假定远程服务器共享文件夹下确实包含本文件,否则程序报错
FileStream outFileStream = new FileStream(src, FileMode.OpenOrCreate); //从远程服务器下载到本地的文件
byte[] buf = new byte[inFileStream.Length];
int byteCount;
while ((byteCount = inFileStream.Read(buf, 0, buf.Length)) > 0)
{
outFileStream.Write(buf, 0, byteCount);
}
inFileStream.Flush();
inFileStream.Close();
outFileStream.Flush();
outFileStream.Close();
}
#endregion
获取剪切板文件路径的代码:
if (Clipboard.ContainsFileDropList())
{
//这里是一个等待窗体的页面
WaitDialogForm sdf = new WaitDialogForm("", "正在上传数据......");
GC.Enabled = false;
try
{
//file就是剪切板上的文件路径
foreach (string file in Clipboard.GetFileDropList())
{
//自己写的上传文件的一个方法
upLoad(file);
}
}
catch (Exception ex)
{
XtraMessageBox.Show("Error : " + ex.Message);
}
finally
{
sdf.Close();
GC.Enabled = true;
}
}
上传文件的方法,先在数据库插入数据,然后再将文件保存到共享文件夹
public void upLoad(string file)
{
//string file = Clipboard.GetFileDropList()[0]; //读取剪贴板的第一个文件
FileInfo fileInfo = null;
try
{
fileInfo = new System.IO.FileInfo(file);
}
catch (Exception ex)
{
XtraMessageBox.Show(ex.Message);
// 其他处理异常的代码
}
System.Diagnostics.FileVersionInfo info = System.Diagnostics.FileVersionInfo.GetVersionInfo(file);
string FileName = file.Substring(file.LastIndexOf("\\") + 1);
string FileType = file.Substring(file.LastIndexOf(".") + 1);
DataRowView drv = FileTree.GetDataRecordByNode(FileTree.FocusedNode) as DataRowView;
FousedeptId = drv["ID"].ToString();
sql = "select FileURL from Folder where ID = '" + FousedeptId + "'";
DataTable dt;
if (!DB.AF.execSql(sql, sqlconn, out dt))
{
XtraMessageBox.Show("访问数据库失败!");
return;
}
string url = dt.Rows[0][0].ToString() + @"\";
//先存数据库后存服务器。
sql = "insert into Folder(ProID,CusName,FileName,FileType,FileURL,UpID,FileSize) values('" + ProID + "','" + CusName + "','" + FileName + "','" + FileType + "文件','" + url + FileName + "','" + FousedeptId + "','" + System.Math.Ceiling(fileInfo.Length / 1024.0) + " KB" + "')";
if (!DB.AF.sqlExec(sql, sqlconn, out errMsg))
{
XtraMessageBox.Show(errMsg);
return;
}
bool status = false;
//连接共享文件夹
status = connectState(@"\\ip\Project\ERP", "账号", "密码");
if (status)
{
//执行方法,上面的共享文件夹操作方法
Transport(file, url, FileName);
}
//SetGV();
}
文件预览
文件预览这方面查了很多资料,但是目前来说还没有什么特别好的解决方案。Oracle有个可以预览几百种文件的软件,可以嵌入到C#中,当做控件使用,叫做AutoVue,但是我使用下来感觉不是那么好用,首先是加载时间长,而且在预览Excel的时候如果上传的文件Excel没有最佳列宽,AutoVue是显示不全的,放大缩小也不是很灵活,所以就没有继续用了。
还有的预览方式就是直接调用进程,调用电脑中该文件类型默认打开的程序来打开该文件,不过这种方式不好确定文件的保密性,因为打开之后就不再程序的可控范围之内了,只能再借助别的加密软件来对文件进行管控。
System.Diagnostics.Process.Start(Localpath)//Localpath待预览的文件地址
文件转XPS借助WPF预览
这个txt、pdf、word文件需要写一个窗体,excel需要写一个窗体。
pdf转xps需要先转word,然后转xps,时间也是挺长的,因此设置了保存七天内缓存的xps文件,再次预览的时候就可以直接调用:
前台:
<dx:ThemedWindow
x:Class="Knowledge_Central.Views.XPSPreview"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
Title="XPSPreview" Height="800" Width="1000" WindowState="Maximized"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0" Name="cdTree"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<GroupBox Header="导航目录">
<TreeView Name="tvTree" SelectedItemChanged="tvTree_SelectedItemChanged"/>
</GroupBox>
<GridSplitter Width="3" ResizeBehavior="PreviousAndNext" Grid.Column="1" Background="LightGray"/>
<Grid Grid.Column="3">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DocumentViewer Name="dvShow" Grid.Row="1" Background="#FFF0F0F0" />
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Right">
<CheckBox Content="显示导航" Margin="5,1,5,1.6" Name="cbNav" Width="75" Click="cbNav_Click" />
<Label Content="页面" Margin="0,2,0,-2.2"/>
<Label Name="lblCurPage" Margin="0"/>
<Label Name="lblPage"/>
<Button Content="上一页" Height="23" Name="btnPrev" Width="75" Click="btnPrev_Click" />
<Button Content="下一页" Height="23" Name="btnNext" Width="75" Click="btnNext_Click" />
<!--<Label Content="总字数:" Name="lblWordCount"/>-->
</StackPanel>
</Grid>
</Grid>
</dx:ThemedWindow>
后台:
using System;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Xps.Packaging;
using System.Xml;
using Microsoft.Office.Interop.Word;
using DevExpress.Xpf.Core;
using DevExpress.XtraEditors;
using Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace Knowledge_Central.Views
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class XPSPreview : ThemedWindow
{
string name;
#region 全局变量
/// <summary>
/// 用于存放目录文档各节点OutlineLevel值,并转化为int型
/// </summary>
int[] array = null;
/// <summary>
/// 用于存放目录文档各节点OutlineLevel值
/// </summary>
string[] array1 = null;
/// <summary>
/// 用于存放目录文档各节点Description值,章节信息
/// </summary>
string[] arrayName = null;
/// <summary>
/// 用于存放目录文档各节点OutlineTarget值,页码信息
/// </summary>
string[] pages = null;
#endregion
public XPSPreview()
{
InitializeComponent();
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="strFilePath">文件路径</param>
public XPSPreview(string strFilePath) : this() { OpenFile(strFilePath); }
#region 方法
/// <summary>
/// 读取导航目录
/// </summary>
private void ReadDoc(XpsDocument xpsDoc)
{
IXpsFixedDocumentSequenceReader docSeq = xpsDoc.FixedDocumentSequenceReader;
IXpsFixedDocumentReader docReader = docSeq.FixedDocuments[0];
XpsStructure xpsStructure = docReader.DocumentStructure;
Stream stream = xpsStructure.GetStream();
XmlDocument doc = new XmlDocument();
doc.Load(stream);
//获取节点列表
XmlNodeList nodeList = doc.ChildNodes.Item(0).FirstChild.FirstChild.ChildNodes;
if (nodeList.Count <= 0)//判断是否存在目录节点
{
//tvTree.Visibility = System.Windows.Visibility.Hidden;
tvTree.Items.Add(new TreeViewItem { Header = "没有导航目录" });
return;
}
tvTree.Visibility = System.Windows.Visibility.Visible;
array = new int[nodeList.Count];
array1 = new string[nodeList.Count];
arrayName = new string[nodeList.Count];
pages = new string[nodeList.Count];
for (int i = 0; i < nodeList.Count; i++)
{
array[i] = Convert.ToInt32(nodeList[i].Attributes["OutlineLevel"].Value);
array1[i] = nodeList[i].Attributes["OutlineLevel"].Value.ToString();
arrayName[i] = nodeList[i].Attributes["Description"].Value.ToString();
pages[i] = nodeList[i].Attributes["OutlineTarget"].Value.ToString();
}
for (int i = 0; i < array.Length - 1; i++)
{
//对array进行转换组装成可读的树形结构,通过ASCII值进行增加、转换
array1[0] = "A";
if (array[i + 1] - array[i] == 1)
{
array1[i + 1] = array1[i] + 'A';
}
if (array[i + 1] == array[i])
{
char s = Convert.ToChar(array1[i].Substring((array1[i].Length - 1), 1));
array1[i + 1] = array1[i].Substring(0, array1[i].Length - 1) + (char)(s + 1);
}
if (array[i + 1] < array[i])
{
int m = array[i + 1];
char s = Convert.ToChar(array1[i].Substring(0, m).Substring(m - 1, 1));
array1[i + 1] = array1[i].Substring(0, m - 1) + (char)(s + 1);
}
}
//添加一个节点作为根节点
TreeViewItem parent = new TreeViewItem();
TreeViewItem parent1 = null;
parent.Header = "目录导航";
Boolean flag = false;
for (int i = 0; i < array.Length; i++)
{
if (array[i] == 1)
{
flag = true;
}
if (flag) //如果找到实际根节点,加载树
{
parent1 = new TreeViewItem();
parent1.Header = arrayName[i];
parent1.Tag = array1[i];
parent.Items.Add(parent1);
parent.IsExpanded = true;
parent1.IsExpanded = true;
FillTree(parent1, array1, arrayName);
flag = false;
}
}
tvTree.Items.Clear();
tvTree.Items.Add(parent);
}
/// <summary>
/// 填充树的方法
/// </summary>
/// <param name="parentItem"></param>
/// <param name="str1"></param>
/// <param name="str2"></param>
public void FillTree(TreeViewItem parentItem, string[] str1, string[] str2)
{
string parentID = parentItem.Tag as string;
for (int i = 0; i < str1.Length; i++)
{
if (str1[i].IndexOf(parentID) == 0 && str1[i].Length == (parentID.Length + 1) && str1[i].ElementAt(0).Equals(parentID.ElementAt(0)))
{
TreeViewItem childItem = new TreeViewItem();
childItem.Header = str2[i];
childItem.Tag = str1[i];
parentItem.Items.Add(childItem);
FillTree(childItem, str1, str2);
}
}
}
/// <summary>
/// 打开文件-如果传入路径为空则在此打开选择文件对话框
/// </summary>
/// <param name="strFilepath">传入文件全路径</param>
private void OpenFile(string strFilepath)
{
if (string.IsNullOrEmpty(strFilepath))
{
//Microsoft.Win32.OpenFileDialog openFileDialog = new Microsoft.Win32.OpenFileDialog();
//openFileDialog.DefaultExt = ".doc|.txt|.xps";
//openFileDialog.Filter = "*(.xps)|*.xps|Word documents (.doc)|*.doc|Word(2007-2010)(.docx)|*.docx|*(.txt)|*.txt";
//Nullable<bool> result = openFileDialog.ShowDialog();
//strFilepath = openFileDialog.FileName;
//if (result != true)
//{
return;
//}
}
this.Title = strFilepath.Substring(strFilepath.LastIndexOf("\\") + 1);
if (strFilepath.Length > 0)
{
string newXPSdocName = string.Concat(Path.GetDirectoryName(strFilepath), "\\", Path.GetFileNameWithoutExtension(strFilepath), ".xps");
XpsDocument xpsDoc = null;
//如果是xps文件直接打开,否则需转换格式
if (!strFilepath.EndsWith(".xps"))
{
string FileType = strFilepath.Substring(strFilepath.LastIndexOf(".") + 1);
switch (FileType)
{
case "pdf":
case "doc":
case "docx":
case "txt":
{
xpsDoc = ConvertWordToXPS(strFilepath, newXPSdocName);
}; break;
case "xls":
case "xlsx":
{ xpsDoc = ConvertExcelToXps(strFilepath, newXPSdocName); }
; break;
}
File.Delete(strFilepath);
}
else
{
xpsDoc = new XpsDocument(strFilepath, System.IO.FileAccess.Read);
}
//}
if (xpsDoc != null)
{
dvShow.Document = xpsDoc.GetFixedDocumentSequence();
//读取文档目录
ReadDoc(xpsDoc);
xpsDoc.Close();
}
this.lblCurPage.Content = 1;
this.lblPage.Content = "/" + dvShow.PageCount;
}
}
/// <summary>
/// 将word文档转换为xps文档
/// </summary>
/// <param name="wordDocName">word文档全路径</param>
/// <param name="xpsDocName">xps文档全路径</param>
/// <returns></returns>
private XpsDocument ConvertWordToXPS(string wordDocName, string xpsDocName)
{
XpsDocument result = null;
//创建一个word文档,并将要转换的文档添加到新创建的对象
Microsoft.Office.Interop.Word.Application wordApplication = new Microsoft.Office.Interop.Word.Application();
try
{
wordApplication.Documents.Add(wordDocName);
Document doc = wordApplication.ActiveDocument;
doc.ExportAsFixedFormat(xpsDocName, WdExportFormat.wdExportFormatXPS, false, WdExportOptimizeFor.wdExportOptimizeForPrint, WdExportRange.wdExportAllDocument, 0, 0, WdExportItem.wdExportDocumentContent, true, true, WdExportCreateBookmarks.wdExportCreateHeadingBookmarks, true, true, false, Type.Missing);
result = new XpsDocument(xpsDocName, System.IO.FileAccess.ReadWrite);
}
catch (Exception ex)
{
string error = ex.Message;
wordApplication.Quit(WdSaveOptions.wdDoNotSaveChanges);
}
wordApplication.Quit(WdSaveOptions.wdDoNotSaveChanges);
return result;
}
/// <summary>
/// 将Excel文件转换为XPS文件
/// </summary>
/// <param name="execelFileName">Excel文件名</param>
/// <param name="xpsFileName">转换的xps文件名</param>
public XpsDocument ConvertExcelToXps(string excelFileName, string xpsFileName)
{
XpsDocument result = null;
if (string.IsNullOrWhiteSpace(excelFileName))
throw new ArgumentNullException(excelFileName);
var fileInfo = new FileInfo(xpsFileName);
Microsoft.Office.Interop.Excel.Application app = new Microsoft.Office.Interop.Excel.Application();
app.DisplayAlerts = false;
Workbooks wbs;
Workbook wb;
wbs = app.Workbooks;
wb = wbs.Add(excelFileName);
dynamic Nothing = System.Reflection.Missing.Value;
wb.ExportAsFixedFormat(XlFixedFormatType.xlTypeXPS, xpsFileName, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing);
result = new XpsDocument(xpsFileName, System.IO.FileAccess.ReadWrite);
wb.Close(true);
wbs.Close();
app.Quit();
KillExcelProcess(app);
return result;
}
[DllImport("User32.dll")]
private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int ProcessId);
/// <summary>
/// 结束Excel进程
/// </summary>
/// <param name="obj"></param>
private void KillExcelProcess(Microsoft.Office.Interop.Excel.Application app)
{
if (app == null)
return;
try
{
IntPtr intptr = new IntPtr(app.Hwnd);
int id;
GetWindowThreadProcessId(intptr, out id);
var p = Process.GetProcessById(id);
p.Kill();
}
catch { }
}
#endregion
/// <summary>
/// 导航树跳转事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tvTree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
int x = 0;
TreeViewItem selectTV = this.tvTree.SelectedItem as TreeViewItem;
if (null == selectTV)
return;
if (null == selectTV.Tag)
return;
string page = selectTV.Tag.ToString();
for (int i = 0; i < array1.Length; i++)
{
if (array1[i].Equals(page))
{
x = i;
}
}
string[] strPages = pages[x].Split('_');
dvShow.GoToPage(Int32.Parse(strPages[1]));
}
private void cbNav_Click(object sender, RoutedEventArgs e)
{
this.cdTree.Width = this.cbNav.IsChecked == true ? new GridLength(300) : new GridLength(0);
}
private void btnPrev_Click(object sender, RoutedEventArgs e)
{
this.dvShow.PreviousPage();
if (int.Parse(this.lblCurPage.Content.ToString()) > 1)
this.lblCurPage.Content = int.Parse(this.lblCurPage.Content.ToString()) - 1;
}
private void btnNext_Click(object sender, RoutedEventArgs e)
{
this.dvShow.NextPage();
if (int.Parse(this.lblCurPage.Content.ToString()) < int.Parse(this.lblPage.Content.ToString().Replace("/", "")))
this.lblCurPage.Content = int.Parse(this.lblCurPage.Content.ToString()) + 1;
}
}
}
Excel预览:Excel也可以转成xps文件预览,但是我这边没有转,而是用了SpreadsheetControl这个控件,其实不转的情况下用winform和wpf都是一样的,都是用这个控件显示excel文档。
<dx:ThemedWindow
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:dxsps="http://schemas.devexpress.com/winfx/2008/xaml/spreadsheet"
x:Class="Knowledge_Central.Views.XPSPreview_Excel"
Title="XPSPreview_Excel" Height="800" Width="1000" WindowState="Maximized"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<dxsps:SpreadsheetFormulaBarControl x:Name="formulaBar" SpreadsheetControl="{Binding ElementName=spreadsheetControl1}" />
<dxsps:SpreadsheetControl Grid.Row="1" x:Name="spreadsheetControl1"/>
</Grid>
</dx:ThemedWindow>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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.Shapes;
using DevExpress.Xpf.Core;
using DevExpress.Spreadsheet;
namespace Knowledge_Central.Views
{
/// <summary>
/// Interaction logic for XPSPreview_Excel.xaml
/// </summary>
public partial class XPSPreview_Excel : ThemedWindow
{
public XPSPreview_Excel(string path)
{
InitializeComponent();
PreviewExcel(path);
}
public void PreviewExcel(string filePath)
{
IWorkbook workbook = spreadsheetControl1.Document;
workbook.LoadDocument(filePath);
}
}
}
文件下载
文件下载保存到本地是最简单的了
WaitDialogForm sdf = new WaitDialogForm("", "正在下载文件,请稍等......");
GC.Enabled = false;
string name = GV.GetRowCellValue(GV.FocusedRowHandle, "Name").ToString();
string path = GV.GetRowCellValue(GV.FocusedRowHandle, "URL").ToString();
string Localpath = @"D:\KnowledgeCentralFiles\" + name;
if (File.Exists(path))
{
try
{
bool status = false;
//连接共享文件夹
status = connectState(@"\\ip\Project\ERP", "账户", "密码");
if (status)
{
TransportRemoteToLocal(Localpath, Path.GetDirectoryName(GV.GetRowCellValue(GV.FocusedRowHandle, "URL").ToString()) + @"\", name);
}
}
catch (Exception ex) { XtraMessageBox.Show(ex.Message); return; }
}
sdf.Close();
GC.Enabled = true;
if (XtraMessageBox.Show("下载成功,是否立即打开?", "", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
System.Diagnostics.Process.Start(Localpath);
}
权限管理
权限管理算是这个系统比较重要的模块了。分为三个维度:权限组,人员权限,文件权限
- 权限组
权限组总查询的时候是否显示该权限组明细人员这里,因为要改变列数,所以要重置GridView,用到了两句语法:
上面那句和下面那句用那句都可以
//(GC.DefaultView as GridView).Columns.Clear();//切换前需要先把列清空了。
GC.DataSource = dt;
(GC.DefaultView as GridView).PopulateColumns();
新增权限组:
新版本的DevExpress的GridControl自带粘贴功能了,目前看来是分为更新和追加,如果想既可以更新又可以追加的话,可能还是要自己写。
这里直接用了追加的,因为只需要维护工号列,姓名是自动带出的,不太需要更新:
另外使用了GridLookUpEdit列以及模糊查询的功能:
在工号列值提交的时候,自动给姓名列赋值,不知道这里有没有更简单的方法,其实我这里访问了两次数据库。
private void GV_CellValueChanged(object sender, DevExpress.XtraGrid.Views.Base.CellValueChangedEventArgs e)
{
//XtraMessageBox.Show(e.Column.ToString());
if (e.Column.ToString().Trim() == "工号")
{
sql = "select userName 姓名 from sys_userInf where userID = '" + GV.GetRowCellValue(GV.FocusedRowHandle, "工号").ToString().Trim() + "'";
DataTable dt;
if (!DB.AF.execSql(sql, sqlconnOA, out dt))
{
XtraMessageBox.Show("连接人员信息服务器失败!");
return;
}
if (dt.Rows.Count == 1)
{
GV.SetRowCellValue(GV.FocusedRowHandle, "姓名", dt.Rows[0][0].ToString());
// GV.SetRowCellValue(GV.FocusedRowHandle, "代码", txt_Code.Text.Trim());
}
}
}
GridLookUpEdit列及模糊查询:
public void Initcol()
{
sql = "select userID 工号,userName 姓名,deptName 部门,companyName 公司 from sys_userInf";
DataTable dt;
if (!DB.AF.execSql(sql, sqlconnOA, out dt))
{
XtraMessageBox.Show("连接人员信息服务器失败!");
}
RepositoryItemGridLookUpEdit rc = new RepositoryItemGridLookUpEdit(); //这行是关键
rc.DataSource = new BindingSource(dt, null);
rc.ValueMember = "工号";
rc.DisplayMember = "工号";
rc.AllowNullInput = DevExpress.Utils.DefaultBoolean.True;
rc.View.BestFitColumns();
rc.NullText = "";
rc.ShowFooter = false;
rc.View.OptionsView.ShowAutoFilterRow = false; //显示不显示grid上第一个空行,也是用于检索的应用
rc.AutoComplete = false;
rc.ImmediatePopup = true;
rc.PopupFilterMode = PopupFilterMode.Contains;
rc.TextEditStyle = TextEditStyles.Standard;
SetGridLookUpEditMoreColumnFilter(rc);
GC.RepositoryItems.Add(rc);//这行也是关键
GV.Columns["工号"].ColumnEdit = rc;//这行也是关键
//if (string.IsNullOrEmpty(rc.View.GetFocusedDataRow()["姓名"].ToString()))
// GV.SetRowCellValue(GV.FocusedRowHandle, "姓名", rc.View.GetFocusedDataRow()["姓名"].ToString());
}
/// <summary>
/// GridLookupEdit模糊查询
/// </summary>
/// <param name="repGLUEdit"></param>
private void SetGridLookUpEditMoreColumnFilter(RepositoryItemGridLookUpEdit repGLUEdit)
{
repGLUEdit.EditValueChanging += (sender, e) =>
{
BeginInvoke(new MethodInvoker(() =>
{
GridLookUpEdit edit = sender as GridLookUpEdit;
DevExpress.XtraGrid.Views.Grid.GridView view = edit.Properties.View as DevExpress.XtraGrid.Views.Grid.GridView;
//获取GriView私有变量
System.Reflection.FieldInfo extraFilter = view.GetType().GetField("extraFilter", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
List<DevExpress.Data.Filtering.CriteriaOperator> columnsOperators = new List<DevExpress.Data.Filtering.CriteriaOperator>();
foreach (GridColumn col in view.VisibleColumns)
{
if (col.Visible && col.ColumnType == typeof(string))
columnsOperators.Add(new DevExpress.Data.Filtering.FunctionOperator(DevExpress.Data.Filtering.FunctionOperatorType.Contains,
new DevExpress.Data.Filtering.OperandProperty(col.FieldName),
new DevExpress.Data.Filtering.OperandValue(edit.Text)));
}
string filterCondition = new DevExpress.Data.Filtering.GroupOperator(DevExpress.Data.Filtering.GroupOperatorType.Or, columnsOperators).ToString();
extraFilter.SetValue(view, filterCondition);
//获取GriView中处理列过滤的私有方法
System.Reflection.MethodInfo ApplyColumnsFilterEx = view.GetType().GetMethod("ApplyColumnsFilterEx", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
ApplyColumnsFilterEx.Invoke(view, null);
}));
};
}
建好分组后,然后给分组设置权限:
这里的权限是针对文件夹的,预览、上传、更新版本、下载、删除
- 人员权限
人员权限就比较简单
- 文件权限
文件权限还没做好,做好再更新