WPF 仿百度云的拖拽到系统的下载

2017-05-29 19:54:02来源:CSDN作者:zq1564171310人点击

大概是两年前做的一个项目,跟百度云盘的拖拽下载功能基本差不多。最近有人问我,我看了一下以前的代码,感觉应该写点什么记录下来,方便以后可以直接使用。记得当时网上查了好久基本没有,问了好多人,答案大多都好像无法实现。公司当时WPF就我一个人,最后没办法,只有观察琢磨百度云的拖拽下载,观察了一周左右,最后终于自己弄出来了。

说下思路:(注意将拖拽拆分为复制和粘贴,拖起为复制,到达系统中鼠标弹起为粘贴)

1.用户在软件界面拖拽了文件或者点击了复制,如果这时候用户在系统中点击了粘贴或者文件拖拽的到系统中,我们是无法捕捉这个操作的,所以这时候我们想办法找到a.用户什么时候在系统中点击了粘贴,或者拖拽到系统中鼠标弹起了(相当于在系统中点击了粘贴);b.用户在系统中哪个文件夹下,点击了粘贴或者拖拽的鼠标弹起了,即最终的我们要下载的目录。
2.至于如何判断用户的点击粘贴动作或者拖拽的鼠标弹起操作,我们需要借助系统剪切板完成。原因:系统的文件拖拽实际也是复制粘贴过程,我们是否可以用一个中间文件,我们在软件中拖起文件时,给系统一个中间文件,到系统剪切板,后面我称这个文件为标记文件,当标记被粘贴了或者被拖拽到某一文件夹,那不就是我们需要的下载目录和最终的下载时间?
3.具体实现:用户在软件中点击复制,或者拖拽了文件(鼠标没有弹起,仅仅是复制,没有粘贴),这时候,我们需要在系统的剪切板中添加一个文件(标记文件),文件路径随意,不要改动,但是一定要隐蔽,而且不要更换,一般是C盘的AppData/Local/Temp/我们的应用程序文件夹,专业一点的都懂的,这个文件夹是干嘛的。将这个文件放入系统的剪切板,剪切板有个类型选择,选剪切,不要选复制,原因:当用户在系统中点击粘贴或者拖拽的鼠标弹起时,该文件会被切剪到用户指定的文件夹中,这个文件夹就是我们需要的文件夹路径,这个事件发生的时候,就是我们需要干活的时候,我们需要干的,最重要的一步,就是监听这个C盘的AppData/Local/Temp/我们的应用程序文件夹,查看中间文件状态(是否被剪切)。
4.最后一步:监听。获取当前系统中被激活的窗口,如果当前被激活的是文件夹,那么就监听,至于怎么获取被激活的窗体,网上有,要用win32函数,我就不多说了,后续代码也可以参考。
5.被监听的文件夹中如果出现那个标记文件被剪切走(删除了),我们就发起下载过程,同时删掉标记文件,这个下载的目的地址,就是标记文件被剪切到达的文件夹。思路证明:把百度云的文件,随意直接往qq好友面板中拖拽,会有类似的一个标记文件存在;或者下载时观察AppData/Local/baidu这个文件夹,也会有这样一个中间文件作为标记文件,在下载的目录中也存在这样一个文件。
好了,思路是思路,最终还是要验证,最简单的验证,上代码:1.先定义一个文件类,这个没啥好纠结的
  class FileName    {        private string _Name;        public string Name        {            get { return _Name; }            set { _Name = value; }        }        public FileName(string name)        {            this.Name = name;        }    }

2.定义一个窗口类,也没啥好多说的
 <Grid>        <ListView Name="listView" ItemsSource="{Binding}" MouseMove="listView_MouseMove" >            <ListView.View>                <GridView>                    <GridViewColumn Header="文件名" Width="320">                        <GridViewColumn.CellTemplate>                            <DataTemplate>                                <StackPanel Orientation="Horizontal">                                    <Label Content="{Binding Name,Mode=OneTime}" Visibility="{Binding NameVisible}"/>                                </StackPanel>                            </DataTemplate>                        </GridViewColumn.CellTemplate>                    </GridViewColumn>                </GridView>            </ListView.View>        </ListView>    </Grid>
3.添加监听类,重点来了
  class WatchPasteLogManager    {        private static FileSystemWatcher watcherPasteLog;        private static string PastePath = "";        /// <summary>        /// 开始监听临时路径下的标记文件        /// </summary>        /// <param name="path"></param>        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]        public static void WatchPasteLog(string path)        {            if (null == path)            {                return;            }            watcherPasteLog = new FileSystemWatcher();            watcherPasteLog.Path = path;            watcherPasteLog.EnableRaisingEvents = true;            watcherPasteLog.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite               | NotifyFilters.FileName | NotifyFilters.DirectoryName;            watcherPasteLog.Filter = "Test.temp";            watcherPasteLog.Deleted += new FileSystemEventHandler(OnChanged);        }        /// <summary>        /// 监听事件触发方法        /// </summary>        private static void OnChanged(object source, FileSystemEventArgs e)        {            if (e.ChangeType == WatcherChangeTypes.Deleted)            {                string dstPath = GetForegroundWindowPath() + "Test.temp";                if (File.Exists(dstPath))                {                    //这个路径就是我们要找的用户粘贴的路径,这个方法被触发的时间,就是我们要的下载时间                    PastePath = GetForegroundWindowPath();                    //下载                    System.Net.WebClient client = new System.Net.WebClient();                    //这个地址可以替换,看具体需要,这里我找不到好的链接,直接用qq的下载链接了                    client.DownloadFile("http://dldir1.qq.com/qqfile/qq/QQ6.0/11743/QQ6.0.exe", PastePath + "//qq.exe");                    watcherPasteLog.EnableRaisingEvents = false;                }            }        }        private static string ForegroundWindowPath;        public static string GetForegroundWindowPath()        {            EnumWindows(Report, 0);            return ForegroundWindowPath;        }        #region 获取当前激活的窗口路径        public delegate bool CallBack(int hwnd, int y);        [DllImport("user32.dll")]        public static extern int EnumWindows(CallBack x, int y);        [DllImport("user32.dll")]        public static extern int GetWindowText(int hwnd, StringBuilder lptrString, int nMaxCount);        [DllImport("user32.dll")]        public static extern int GetParent(int hwnd);        [DllImport("user32.dll")]        public static extern bool IsWindowVisible(int hwnd);        [DllImport("user32.Dll ")]        public static extern void GetClassName(IntPtr hwnd, StringBuilder s, int nMaxCount);        [DllImport("user32.dll ")]        public static extern IntPtr FindWindowEx(IntPtr parent, IntPtr childe, string strclass, string FrmText);        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]        public static extern IntPtr GetForegroundWindow();        [DllImport("user32.dll")]        public static extern bool SetForegroundWindow(int hWnd);        [DllImport("user32.dll")]        public static extern IntPtr GetActiveWindow();        private static string GetFormClassName(IntPtr ptr)        {            StringBuilder nameBiulder = new StringBuilder(255);            GetClassName(ptr, nameBiulder, 255);            return nameBiulder.ToString();        }        private static string GetFormTitle(IntPtr ptr)        {            StringBuilder titleBiulder = new StringBuilder(255);            GetWindowText((int)ptr, titleBiulder, 255);            return titleBiulder.ToString();        }        public static bool Report(int hwnd, int lParam)        {            int pHwnds = (int)GetForegroundWindow();            int pHwnd = (int)GetActiveWindow();            if (pHwnd == 0 && hwnd == pHwnds && IsWindowVisible(hwnd) == true)            {                IntPtr cabinetWClassIntPtr = new IntPtr(hwnd);                string cabinetWClassName = GetFormClassName(cabinetWClassIntPtr);                string cabinetWClassTitle = GetFormTitle(cabinetWClassIntPtr);                if (cabinetWClassName.Equals("CabinetWClass", StringComparison.OrdinalIgnoreCase))                {                    IntPtr workerWIntPtr = FindWindowEx(cabinetWClassIntPtr, IntPtr.Zero, "WorkerW", null);                    IntPtr reBarWindow32IntPtr = FindWindowEx(workerWIntPtr, IntPtr.Zero, "ReBarWindow32", null);                    IntPtr addressBandRootIntPtr = FindWindowEx(reBarWindow32IntPtr, IntPtr.Zero, "Address Band Root", null);                    IntPtr msctls_progress32IntPtr = FindWindowEx(addressBandRootIntPtr, IntPtr.Zero, "msctls_progress32", null);                    IntPtr breadcrumbParentIntPtr = FindWindowEx(msctls_progress32IntPtr, IntPtr.Zero, "Breadcrumb Parent", null);                    IntPtr toolbarWindow32IntPtr = FindWindowEx(breadcrumbParentIntPtr, IntPtr.Zero, "ToolbarWindow32", null);                    string title = GetFormTitle(toolbarWindow32IntPtr);                    int index = title.IndexOf(':');                    index++;                    string path = title.Substring(index, title.Length - index);                    if (Directory.Exists(path))                    {                        path = path + "//";                        if (path.StartsWith(" "))                        {                            path = path.Substring(1, path.Length - 1);                        }                        ForegroundWindowPath = path;                    }                }                else if (cabinetWClassName.Equals("Progman", StringComparison.OrdinalIgnoreCase) && cabinetWClassTitle.Equals("Program Manager", StringComparison.OrdinalIgnoreCase))                {                    ForegroundWindowPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "//";                }            }            return true;        }        #endregion    }
4.添加具体的拖拽逻辑
 public partial class MainWindow : Window    {        public MainWindow()        {            InitializeComponent();            List<FileName> list = new List<FileName>();            list.Add(new FileName("1111"));            list.Add(new FileName("2222"));            list.Add(new FileName("3333"));            list.Add(new FileName("4444"));            listView.ItemsSource = list;        }        private void listView_MouseMove(object sender, MouseEventArgs e)        {            if (e.LeftButton == MouseButtonState.Pressed)            {                //FileListItem item = (FileListItem)listView.SelectedItem;                if (!System.IO.Directory.Exists(System.IO.Path.GetTempPath() + "temp//" + "//"))                {                    System.IO.Directory.CreateDirectory(System.IO.Path.GetTempPath() + "temp//" + "//");                }                string localPasteLog = System.IO.Path.GetTempPath() + "temp//" + "//" + "Test.temp";                CreateFiles(localPasteLog, DateTime.Now.ToString());                string[] files = new string[1];                files[0] = System.IO.Path.GetTempPath() + "temp//" + "//" + "Test.temp";
                //@这一步是将我们的标记文件给系统,拖拽鼠标弹起时,系统会将这个文件剪切到指定目录,注意一定要是DragDropEffects.Move,即剪切模式                DragDrop.DoDragDrop(listView, new DataObject(DataFormats.FileDrop, files), DragDropEffects.Move /* | DragDropEffects.Link */);
                //监听temp目录下的标记文件,如果被剪切走(删除),就是发生下载的时机                WatchPasteLogManager.WatchPasteLog(localPasteLog.Replace("Test.temp", ""));            }        }        public void CreateFiles(string path, string context)        {            try            {                StreamWriter f = new StreamWriter(path, true);                f.WriteLine(context);                f.Close();            }            catch (Exception ex)            {                return;            }        }    }


完成上面的,拖拽一下,就可以下载一个qq.exe,将qq.exe换成点击的那个ListViewItem代表的路径,这个对能点进来看博客的大爷来说简直就是小儿科了。用户的点击复制,在系统中去粘贴也是一样的操作,不过要修改一下剪切板,没有DragDrop.DoDragDrop可以直接使用。这里附上剪切板,不具体说明了(copytoClipboard方法取代上面@标记处)
 class FileSetToClipboard    {        public static void CopyToClipboard(string[] files, bool cut)        {            if (files == null) return;            IDataObject data = new DataObject(DataFormats.FileDrop, files);            MemoryStream memo = new MemoryStream(4);            byte[] bytes = new byte[] { (byte)(cut ? 2 : 5), 0, 0, 0 };            memo.Write(bytes, 0, bytes.Length);            data.SetData("Preferred DropEffect", memo);            Clipboard.SetDataObject(data, false);  //false程序退出时不保存在剪切板        }    }
文采不怎么样,而且有bug和异常没捕捉,但是思路大致已经说清了,基本意思已经到了,大家将就着看吧。

总结:程序设计,你看到的,不一定是真的。很多时候我们无法直接获取到的东西,其实都可以用不同的方法去解决。只要有人做到了,说明必定有方法可以解决,只是你想没有想到!也就是没有做不到,只有想不到!

最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台