Pārlūkot izejas kodu

添加项目文件。

liufei 4 gadi atpakaļ
vecāks
revīzija
cc399e2ef7

+ 14 - 0
App.config

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+    <startup> 
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
+    </startup>
+  <runtime>
+    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+      <dependentAssembly>
+        <assemblyIdentity name="CommonServiceLocator" publicKeyToken="489b6accfaf20ef0" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-2.0.6.0" newVersion="2.0.6.0" />
+      </dependentAssembly>
+    </assemblyBinding>
+  </runtime>
+</configuration>

+ 17 - 0
App.xaml

@@ -0,0 +1,17 @@
+<Application x:Class="GeekDesk.App" 
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
+             xmlns:local="clr-namespace:GeekDesk" 
+             StartupUri="MainWindow.xaml" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             d1p1:Ignorable="d" 
+             xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006">
+  <Application.Resources>
+    <ResourceDictionary>
+            <ResourceDictionary.MergedDictionaries>
+                <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"/>
+                <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml"/>
+            </ResourceDictionary.MergedDictionaries>
+        </ResourceDictionary>
+  </Application.Resources>
+</Application>

+ 17 - 0
App.xaml.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace GeekDesk
+{
+    /// <summary>
+    /// App.xaml 的交互逻辑
+    /// </summary>
+    public partial class App : Application
+    {
+    }
+}

+ 127 - 0
Command/DelegateCommand.cs

@@ -0,0 +1,127 @@
+// Developed by doiTTeam => [email protected]
+using System;
+using System.Windows.Input;
+
+namespace DraggAnimatedPanelExample
+{
+    /// <summary>
+    ///   An <see cref = "ICommand" /> whose delegates can be attached for <see cref = "Execute" /> and <see cref = "CanExecute" />.
+    ///   It also implements the <see cref = "IActiveAware" /> interface, which is useful when registering this command in a <see cref = "CompositeCommand" /> that monitors command's activity.
+    /// </summary>
+    /// <typeparam name = "T">Parameter type.</typeparam>
+    /// <remarks>
+    ///   The constructor deliberately prevent the use of value types.
+    ///   Because ICommand takes an object, having a value type for T would cause unexpected behavior when CanExecute(null) is called during XAML initialization for command bindings.
+    ///   Using default(T) was considered and rejected as a solution because the implementor would not be able to distinguish between a valid and defaulted values.
+    ///   <para />
+    ///   Instead, callers should support a value type by using a nullable value type and checking the HasValue property before using the Value property.
+    ///   <example>
+    ///     <code>
+    ///       public MyClass()
+    ///       {
+    ///       this.submitCommand = new DelegateCommand&lt;int?&gt;(this.Submit, this.CanSubmit);
+    ///       }
+    /// 
+    ///       private bool CanSubmit(int? customerId)
+    ///       {
+    ///       return (customerId.HasValue &amp;&amp; customers.Contains(customerId.Value));
+    ///       }
+    ///     </code>
+    ///   </example>
+    /// </remarks>
+    public class DelegateCommand<T> : DelegateCommandBase
+    {
+        /// <summary>
+        ///   Initializes a new instance of <see cref = "DelegateCommand{T}" />.
+        /// </summary>
+        /// <param name = "executeMethod">Delegate to execute when Execute is called on the command.  This can be null to just hook up a CanExecute delegate.</param>
+        /// <remarks>
+        ///   <seealso cref = "CanExecute" /> will always return true.
+        /// </remarks>
+        public DelegateCommand(Action<T> executeMethod)
+            : this(executeMethod, (o) => true)
+        {
+        }
+
+        /// <summary>
+        ///   Initializes a new instance of <see cref = "DelegateCommand{T}" />.
+        /// </summary>
+        /// <param name = "executeMethod">Delegate to execute when Execute is called on the command.  This can be null to just hook up a CanExecute delegate.</param>
+        /// <param name = "canExecuteMethod">Delegate to execute when CanExecute is called on the command.  This can be null.</param>
+        /// <exception cref = "ArgumentNullException">When both <paramref name = "executeMethod" /> and <paramref name = "canExecuteMethod" /> ar <see langword = "null" />.</exception>
+        public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
+            : base((o) => executeMethod((T) o), (o) => canExecuteMethod((T) o))
+        {
+            if (executeMethod == null || canExecuteMethod == null)
+                throw new ArgumentNullException("executeMethod");
+        }
+
+        ///<summary>
+        ///  Determines if the command can execute by invoked the <see cref = "Func{T,Bool}" /> provided during construction.
+        ///</summary>
+        ///<param name = "parameter">Data used by the command to determine if it can execute.</param>
+        ///<returns>
+        ///  <see langword = "true" /> if this command can be executed; otherwise, <see langword = "false" />.
+        ///</returns>
+        public bool CanExecute(T parameter)
+        {
+            return base.CanExecute(parameter);
+        }
+
+        ///<summary>
+        ///  Executes the command and invokes the <see cref = "Action{T}" /> provided during construction.
+        ///</summary>
+        ///<param name = "parameter">Data used by the command.</param>
+        public void Execute(T parameter)
+        {
+            base.Execute(parameter);
+        }
+    }
+
+    /// <summary>
+    ///   An <see cref = "ICommand" /> whose delegates do not take any parameters for <see cref = "Execute" /> and <see cref = "CanExecute" />.
+    /// </summary>
+    /// <seealso cref = "DelegateCommandBase" />
+    /// <seealso cref = "DelegateCommand{T}" />
+    public class DelegateCommand : DelegateCommandBase
+    {
+        /// <summary>
+        ///   Creates a new instance of <see cref = "DelegateCommand" /> with the <see cref = "Action" /> to invoke on execution.
+        /// </summary>
+        /// <param name = "executeMethod">The <see cref = "Action" /> to invoke when <see cref = "ICommand.Execute" /> is called.</param>
+        public DelegateCommand(Action executeMethod) : this(executeMethod, () => true)
+        {
+        }
+
+        /// <summary>
+        ///   Creates a new instance of <see cref = "DelegateCommand" /> with the <see cref = "Action" /> to invoke on execution
+        ///   and a <see langword = "Func" /> to query for determining if the command can execute.
+        /// </summary>
+        /// <param name = "executeMethod">The <see cref = "Action" /> to invoke when <see cref = "ICommand.Execute" /> is called.</param>
+        /// <param name = "canExecuteMethod">The <see cref = "Func{TResult}" /> to invoke when <see cref = "ICommand.CanExecute" /> is called</param>
+        public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod)
+            : base((o) => executeMethod(), (o) => canExecuteMethod())
+        {
+            if (executeMethod == null || canExecuteMethod == null)
+                throw new ArgumentNullException("executeMethod");
+        }
+
+
+        ///<summary>
+        ///  Executes the command.
+        ///</summary>
+        public void Execute()
+        {
+            Execute(null);
+        }
+
+        /// <summary>
+        ///   Determines if the command can be executed.
+        /// </summary>
+        /// <returns>Returns <see langword = "true" /> if the command can execute,otherwise returns <see langword = "false" />.</returns>
+        public bool CanExecute()
+        {
+            return CanExecute(null);
+        }
+    }
+}

+ 95 - 0
Command/DelegateCommandBase.cs

@@ -0,0 +1,95 @@
+// Developed by doiTTeam => [email protected]
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Windows.Input;
+
+namespace DraggAnimatedPanelExample
+{
+    /// <summary>
+    ///   An <see cref = "ICommand" /> whose delegates can be attached for <see cref = "Execute" /> and <see cref = "CanExecute" />.
+    /// </summary>
+    public abstract class DelegateCommandBase : ICommand
+    {
+        private readonly Func<object, bool> canExecuteMethod;
+        private readonly Action<object> executeMethod;
+
+        /// <summary>
+        ///   Createse a new instance of a <see cref = "DelegateCommandBase" />, specifying both the execute action and the can execute function.
+        /// </summary>
+        /// <param name = "executeMethod">The <see cref = "Action" /> to execute when <see cref = "ICommand.Execute" /> is invoked.</param>
+        /// <param name = "canExecuteMethod">The <see cref = "Func{Object,Bool}" /> to invoked when <see cref = "ICommand.CanExecute" /> is invoked.</param>
+        protected DelegateCommandBase(Action<object> executeMethod, Func<object, bool> canExecuteMethod)
+        {
+            if (executeMethod == null || canExecuteMethod == null)
+                throw new ArgumentNullException("executeMethod");
+
+            this.executeMethod = executeMethod;
+            this.canExecuteMethod = canExecuteMethod;
+        }
+
+        #region ICommand Members
+
+        void ICommand.Execute(object parameter)
+        {
+            Execute(parameter);
+        }
+
+        bool ICommand.CanExecute(object parameter)
+        {
+            return CanExecute(parameter);
+        }
+
+        /// <summary>
+        ///   Occurs when changes occur that affect whether or not the command should execute.
+        /// </summary>
+        public event EventHandler CanExecuteChanged;
+
+        #endregion
+
+        /// <summary>
+        ///   Raises <see cref = "ICommand.CanExecuteChanged" /> on the UI thread so every 
+        ///   command invoker can requery <see cref = "ICommand.CanExecute" /> to check if the
+        ///   <see cref = "CompositeCommand" /> can execute.
+        /// </summary>
+        protected virtual void OnCanExecuteChanged()
+        {
+            var handlers = CanExecuteChanged;
+            if (handlers != null)
+            {
+                handlers(this, EventArgs.Empty);
+            }
+        }
+
+        /// <summary>
+        ///   Raises <see cref = "DelegateCommandBase.CanExecuteChanged" /> on the UI thread so every command invoker
+        ///   can requery to check if the command can execute.
+        ///   <remarks>
+        ///     Note that this will trigger the execution of <see cref = "DelegateCommandBase.CanExecute" /> once for each invoker.
+        ///   </remarks>
+        /// </summary>
+        [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
+        public void RaiseCanExecuteChanged()
+        {
+            OnCanExecuteChanged();
+        }
+
+        /// <summary>
+        ///   Executes the command with the provided parameter by invoking the <see cref = "Action{Object}" /> supplied during construction.
+        /// </summary>
+        /// <param name = "parameter"></param>
+        protected void Execute(object parameter)
+        {
+            executeMethod(parameter);
+        }
+
+        /// <summary>
+        ///   Determines if the command can execute with the provided parameter by invoing the <see cref = "Func{Object,Bool}" /> supplied during construction.
+        /// </summary>
+        /// <param name = "parameter">The parameter to use when determining if this command can execute.</param>
+        /// <returns>Returns <see langword = "true" /> if the command can execute.  <see langword = "False" /> otherwise.</returns>
+        protected bool CanExecute(object parameter)
+        {
+            return canExecuteMethod == null || canExecuteMethod(parameter);
+        }
+    }
+}

+ 18 - 0
Constant/DefaultConstant.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+/// <summary>
+/// 默认参数
+/// </summary>
+namespace GeekDesk.Constant
+{
+    enum DefaultConstant
+    {
+        WINDOW_WIDTH = 650, //默认窗体宽度
+        WINDOW_HEIGHT = 700, //默认窗体高度
+        MENU_CARD_WIDHT = 150 //默认菜单栏宽度
+    }
+}

+ 15 - 0
Constant/SortType.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GeekDesk.Constant
+{
+    enum SortType
+    {
+        CUSTOM = 1, //自定义排序
+        NAME = 2, //按名称排序
+        COUNT = 3 //按使用次数排序
+    }
+}

+ 205 - 0
DraggAnimatedPanel/DraggAnimatedPanel.Drag.cs

@@ -0,0 +1,205 @@
+/*Developed by (doiTTeam)=>doiTTeam.mail = [email protected]*/
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Navigation;
+
+namespace DraggAnimatedPanel
+{
+	/// <summary>
+	/// Description of SafariPanel_Drag.
+	/// </summary>
+	public partial class DraggAnimatedPanel
+	{
+		#region const drag
+		const double mouseDif = 2d;
+		const int mouseTimeDif = 25;
+		#endregion
+		
+		#region private
+		UIElement __draggedElement;
+		
+		public UIElement _draggedElement {
+			get { return __draggedElement; }
+			set 
+			{
+				__draggedElement = value; 
+			}
+		}
+		int _draggedIndex; 		
+		
+		bool _firstScrollRequest = true;
+		ScrollViewer _scrollContainer;
+		ScrollViewer scrollViewer 
+		{
+			get
+			{
+				if (_firstScrollRequest && _scrollContainer == null)
+				{
+					_firstScrollRequest = false;
+					_scrollContainer = (ScrollViewer)GetParent(this as DependencyObject, (ve)=>ve is ScrollViewer);
+				}
+				return _scrollContainer;
+			}
+		}
+		#endregion
+		
+		#region private drag
+		double _lastMousePosX;
+	    double _lastMousePosY;
+		int _lastMouseMoveTime;
+		double _x;
+		double _y;
+		Rect _rectOnDrag;
+		#endregion
+		
+		
+		void OnMouseMove(object sender,MouseEventArgs e)
+		{
+			if (e.LeftButton == MouseButtonState.Pressed && _draggedElement == null && !this.IsMouseCaptured)
+				StartDrag(e);
+			else if (_draggedElement != null)
+				OnDragOver(e);
+		}
+		
+		void OnDragOver(MouseEventArgs e)
+		{
+			Point mousePos = Mouse.GetPosition(this);
+			double difX = mousePos.X - _lastMousePosX;
+			double difY = mousePos.Y - _lastMousePosY;
+			
+			int timeDif = e.Timestamp - _lastMouseMoveTime;
+			if ((Math.Abs(difX) > mouseDif || Math.Abs(difY) > mouseDif) && timeDif > mouseTimeDif)
+			{	
+				//this lines is for keepn draged item inside control bounds
+				DoScroll();
+				
+				if (_x + difX < _rectOnDrag.Location.X)
+					_x = 0;
+				else if (ItemsWidth + _x + difX  > _rectOnDrag.Location.X +  _rectOnDrag.Width)
+					_x = _rectOnDrag.Location.X +  _rectOnDrag.Width - ItemsWidth;
+				else if (mousePos.X > _rectOnDrag.Location.X && mousePos.X < _rectOnDrag.Location.X +  _rectOnDrag.Width)
+					_x += difX;
+				if (_y + difY < _rectOnDrag.Location.Y)
+					_y = 0;
+				else if (ItemsHeight + _y + difY  > _rectOnDrag.Location.Y +  _rectOnDrag.Height)
+					_y = _rectOnDrag.Location.Y +  _rectOnDrag.Height - ItemsHeight;
+				else  if (mousePos.Y > _rectOnDrag.Location.Y && mousePos.Y < _rectOnDrag.Location.Y +  _rectOnDrag.Height) 
+					_y += difY;
+				//lines ends
+				
+				AnimateTo(_draggedElement,_x,_y, 0);
+				_lastMousePosX = mousePos.X;
+				_lastMousePosY = mousePos.Y;
+				_lastMouseMoveTime = e.Timestamp;
+				SwapElement(_x + ItemsWidth/2 , _y + ItemsHeight/2);
+			}
+		}
+		
+		void StartDrag(MouseEventArgs e)
+		{
+			Point mousePos = Mouse.GetPosition(this);
+			_draggedElement = GetChildThatHasMouseOver();
+			if (_draggedElement == null)
+				return;
+			_draggedIndex = Children.IndexOf(_draggedElement);
+			_rectOnDrag = VisualTreeHelper.GetDescendantBounds(this);	
+			Point p = GetItemVisualPoint(_draggedElement);
+			_x = p.X;
+			_y = p.Y;
+			SetZIndex(_draggedElement,1000);
+			_lastMousePosX = mousePos.X;
+			_lastMousePosY = mousePos.Y;
+			_lastMouseMoveTime = e.Timestamp;				
+			this.InvalidateArrange();
+			e.Handled = true;
+			this.CaptureMouse();			
+		}
+		
+		void OnMouseUp(object sender,MouseEventArgs e)
+		{		
+			if (this.IsMouseCaptured)
+				ReleaseMouseCapture();
+		}
+		
+		void SwapElement(double x, double y)
+		{
+			int index = GetIndexFromPoint(x,y);
+			if (index == _draggedIndex || index < 0)
+				return;
+			if (index >= Children.Count)
+				index = Children.Count - 1;
+			
+			int[] parameter = new int[]{_draggedIndex, index};
+			if (SwapCommand != null && SwapCommand.CanExecute(parameter))
+			{
+				SwapCommand.Execute(parameter);
+				_draggedElement = Children[index];				//this is bcause after changing the collection the element is other			
+				FillNewDraggedChild(_draggedElement);
+				_draggedIndex = index;
+			}
+			
+			this.InvalidateArrange();
+		}
+		
+		void FillNewDraggedChild(UIElement child)
+		{
+			if (child.RenderTransform as TransformGroup == null)
+            {
+                child.RenderTransformOrigin = new Point(0.5, 0.5);
+                TransformGroup group = new TransformGroup();
+                child.RenderTransform = group;
+                group.Children.Add(new TranslateTransform());
+            }
+			SetZIndex(child,1000);
+			AnimateTo(child,_x,_y, 0);			//need relocate the element
+		}
+		
+		void OnLostMouseCapture(object sender,MouseEventArgs e)
+		{
+			FinishDrag();
+		}
+		
+		void FinishDrag()
+		{
+			if (_draggedElement != null)
+			{
+				SetZIndex(_draggedElement,0);
+				_draggedElement = null;
+				this.InvalidateArrange();
+			}
+		}
+		
+		void DoScroll()
+        {
+            if (scrollViewer != null)
+            {
+            	Point position = Mouse.GetPosition(scrollViewer);
+                double scrollMargin = Math.Min(scrollViewer.FontSize * 2, scrollViewer.ActualHeight / 2);
+
+                if (position.X >= scrollViewer.ActualWidth - scrollMargin &&
+                    scrollViewer.HorizontalOffset < scrollViewer.ExtentWidth - scrollViewer.ViewportWidth)
+                {
+                    scrollViewer.LineRight();
+                }
+                else if (position.X < scrollMargin && scrollViewer.HorizontalOffset > 0)
+                {
+                    scrollViewer.LineLeft();
+                }
+                else if (position.Y >= scrollViewer.ActualHeight - scrollMargin &&
+                    scrollViewer.VerticalOffset < scrollViewer.ExtentHeight - scrollViewer.ViewportHeight)
+                {
+                    scrollViewer.LineDown();
+                }
+                else if (position.Y < scrollMargin && scrollViewer.VerticalOffset > 0)
+                {
+                    scrollViewer.LineUp();
+                }
+            }
+        }
+	}
+}

+ 237 - 0
DraggAnimatedPanel/DraggAnimatedPanel.cs

@@ -0,0 +1,237 @@
+/*Developed by (doiTTeam)=>doiTTeam.mail = [email protected]*/
+using System;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Navigation;
+
+namespace DraggAnimatedPanel
+{
+	/// <summary>
+	/// Description of DraggAnimatedPanel.
+	/// </summary>
+	public partial class DraggAnimatedPanel : WrapPanel
+	{
+		#region private vars
+		Size _calculatedSize;
+		bool _isNotFirstArrange = false;
+		int columns, rows;
+		#endregion
+		static DraggAnimatedPanel()
+		{
+			DefaultStyleKeyProperty.OverrideMetadata(typeof(DraggAnimatedPanel), new FrameworkPropertyMetadata(typeof(DraggAnimatedPanel)));
+		}
+
+		public DraggAnimatedPanel() : base()
+		{
+			this.AddHandler(Mouse.MouseMoveEvent, new MouseEventHandler(OnMouseMove), false);
+			this.MouseLeftButtonUp += OnMouseUp;
+			this.LostMouseCapture += OnLostMouseCapture;
+		}
+
+		UIElement GetChildThatHasMouseOver()
+		{
+			return GetParent(Mouse.DirectlyOver as DependencyObject, (ve) => Children.Contains(ve as UIElement)) as UIElement;
+		}
+
+		Point GetItemVisualPoint(UIElement element)
+		{
+			TransformGroup group = (TransformGroup)element.RenderTransform;
+			TranslateTransform trans = (TranslateTransform)group.Children[0];
+
+			return new Point(trans.X, trans.Y);
+		}
+
+		int GetIndexFromPoint(double x, double y)
+		{
+			int columnIndex = (int)Math.Truncate(x / itemContainterWidth);
+			int rowIndex = (int)Math.Truncate(y / itemContainterHeight);
+			return columns * rowIndex + columnIndex;
+		}
+		int GetIndexFromPoint(Point p)
+		{
+			return GetIndexFromPoint(p.X, p.Y);
+		}
+
+		#region dependency properties		
+		public static readonly DependencyProperty ItemsWidthProperty =
+		  DependencyProperty.Register(
+				  "ItemsWidth",
+				  typeof(double),
+				  typeof(DraggAnimatedPanel),
+				  new FrameworkPropertyMetadata(150d));
+
+		public static readonly DependencyProperty ItemsHeightProperty =
+		  DependencyProperty.Register(
+				  "ItemsHeight",
+				  typeof(double),
+				  typeof(DraggAnimatedPanel),
+				  new FrameworkPropertyMetadata(60d));
+
+		public static readonly DependencyProperty ItemSeparationProperty =
+		 DependencyProperty.Register(
+				 "ItemSeparation",
+				 typeof(Thickness),
+				 typeof(DraggAnimatedPanel),
+				 new FrameworkPropertyMetadata());
+
+		// Using a DependencyProperty as the backing store for AnimationMilliseconds.  This enables animation, styling, binding, etc...
+		public static readonly DependencyProperty AnimationMillisecondsProperty =
+			DependencyProperty.Register("AnimationMilliseconds", typeof(int), typeof(DraggAnimatedPanel), new FrameworkPropertyMetadata(200));
+
+		public static readonly DependencyProperty SwapCommandProperty =
+		  DependencyProperty.Register(
+				  "SwapCommand",
+				  typeof(ICommand),
+				  typeof(DraggAnimatedPanel),
+				  new FrameworkPropertyMetadata(null));
+
+		#endregion
+
+		#region properties
+		public double ItemsWidth
+		{
+			get { return (double)GetValue(ItemsWidthProperty); }
+			set { SetValue(ItemsWidthProperty, value); }
+		}
+		public double ItemsHeight
+		{
+			get { return (double)GetValue(ItemsHeightProperty); }
+			set { SetValue(ItemsHeightProperty, value); }
+		}
+		public Thickness ItemSeparation
+		{
+			get { return (Thickness)this.GetValue(ItemSeparationProperty); }
+			set { this.SetValue(ItemSeparationProperty, value); }
+		}
+		public int AnimationMilliseconds
+		{
+			get { return (int)GetValue(AnimationMillisecondsProperty); }
+			set { SetValue(AnimationMillisecondsProperty, value); }
+		}
+		private double itemContainterHeight
+		{
+			get { return ItemSeparation.Top + ItemsHeight + ItemSeparation.Bottom; }
+		}
+		private double itemContainterWidth
+		{
+			get { return ItemSeparation.Left + ItemsWidth + ItemSeparation.Right; }
+		}
+		public ICommand SwapCommand
+		{
+			get { return (ICommand)GetValue(SwapCommandProperty); }
+			set { SetValue(SwapCommandProperty, value); }
+		}
+		#endregion
+
+		#region transformation things		
+		private void AnimateAll()
+		{
+			//Apply exactly the same algorithm, but instide of Arrange a call AnimateTo method
+			double colPosition = 0;
+			double rowPosition = 0;
+			foreach (UIElement child in Children)
+			{
+				if (child != _draggedElement)
+					AnimateTo(child, colPosition + ItemSeparation.Left, rowPosition + ItemSeparation.Top, _isNotFirstArrange ? AnimationMilliseconds : 0);
+				//drag will locate dragged element
+				colPosition += itemContainterWidth;
+				if (colPosition + 1 > _calculatedSize.Width)
+				{
+					colPosition = 0;
+					rowPosition += itemContainterHeight;
+				}
+			}
+		}
+
+		private void AnimateTo(UIElement child, double x, double y, int duration)
+		{
+			TransformGroup group = (TransformGroup)child.RenderTransform;
+			TranslateTransform trans = (TranslateTransform)group.Children.First((groupElement) => groupElement is TranslateTransform);
+
+			trans.BeginAnimation(TranslateTransform.XProperty, MakeAnimation(x, duration));
+			trans.BeginAnimation(TranslateTransform.YProperty, MakeAnimation(y, duration));
+		}
+
+		private DoubleAnimation MakeAnimation(double to, int duration)
+		{
+			DoubleAnimation anim = new DoubleAnimation(to, TimeSpan.FromMilliseconds(duration));
+			anim.AccelerationRatio = 0.2;
+			anim.DecelerationRatio = 0.7;
+			return anim;
+		}
+		#endregion
+
+		#region measure
+		protected override Size MeasureOverride(Size availableSize)
+		{
+			Size itemContainerSize = new Size(itemContainterWidth, itemContainterHeight);
+			int count = 0;  //for not call it again
+			foreach (UIElement child in Children)
+			{
+				child.Measure(itemContainerSize);
+				count++;
+			}
+			if (availableSize.Width < itemContainterWidth)
+				_calculatedSize = new Size(itemContainterWidth, count * itemContainterHeight);  //the size of nX1
+			else
+			{
+				columns = (int)Math.Truncate(availableSize.Width / itemContainterWidth);
+				rows = count / columns;
+				if (count % columns != 0)
+					rows++;
+				_calculatedSize = new Size(columns * itemContainterWidth, rows * itemContainterHeight);
+			}
+			return _calculatedSize;
+		}
+		#endregion
+
+		#region arrange
+		protected override Size ArrangeOverride(Size finalSize)
+		{
+			Size _finalItemSize = new Size(ItemsWidth, ItemsHeight);
+			//if is animated then arrange elements to 0,0, and then put them on its location using the transform
+			foreach (UIElement child in InternalChildren)
+			{
+				// If this is the first time we've seen this child, add our transforms
+				if (child.RenderTransform as TransformGroup == null)
+				{
+					child.RenderTransformOrigin = new Point(0.5, 0.5);
+					TransformGroup group = new TransformGroup();
+					child.RenderTransform = group;
+					group.Children.Add(new TranslateTransform());
+				}
+				//locate all children in 0,0 point//TODO: use infinity and then scale each element to items size
+				child.Arrange(new Rect(new Point(0, 0), _finalItemSize));       //when use transformations change to childs.DesireSize
+			}
+			AnimateAll();
+
+			if (!_isNotFirstArrange)
+				_isNotFirstArrange = true;
+
+			return _calculatedSize;
+		}
+		#endregion
+
+		#region Static
+		//this can be an extension method
+		public static DependencyObject GetParent(DependencyObject o, Func<DependencyObject, bool> matchFunction)
+		{
+			DependencyObject t = o;
+			do
+			{
+				t = VisualTreeHelper.GetParent(t);
+			} while (t != null && !matchFunction.Invoke(t));
+			return t;
+		}
+		#endregion
+
+		//TODO: Add IsEditing property
+		//TODO: Add Scale transform to items for fill items area
+	}
+}

+ 146 - 0
GeekDesk.csproj

@@ -0,0 +1,146 @@
+<?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>{B4983CEC-2281-413C-8ECF-92EE0E40A713}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <RootNamespace>GeekDesk</RootNamespace>
+    <AssemblyName>GeekDesk</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>
+    <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>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </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>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="CommonServiceLocator, Version=2.0.6.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0, processorArchitecture=MSIL">
+      <HintPath>packages\CommonServiceLocator.2.0.6\lib\net45\CommonServiceLocator.dll</HintPath>
+    </Reference>
+    <Reference Include="GalaSoft.MvvmLight, Version=5.4.1.0, Culture=neutral, PublicKeyToken=e7570ab207bcb616, processorArchitecture=MSIL">
+      <HintPath>packages\MvvmLightLibs.5.4.1.1\lib\net45\GalaSoft.MvvmLight.dll</HintPath>
+    </Reference>
+    <Reference Include="GalaSoft.MvvmLight.Extras, Version=5.4.1.0, Culture=neutral, PublicKeyToken=669f0b5e8f868abf, processorArchitecture=MSIL">
+      <HintPath>packages\MvvmLightLibs.5.4.1.1\lib\net45\GalaSoft.MvvmLight.Extras.dll</HintPath>
+    </Reference>
+    <Reference Include="GalaSoft.MvvmLight.Platform, Version=5.4.1.0, Culture=neutral, PublicKeyToken=5f873c45e98af8a1, processorArchitecture=MSIL">
+      <HintPath>packages\MvvmLightLibs.5.4.1.1\lib\net45\GalaSoft.MvvmLight.Platform.dll</HintPath>
+    </Reference>
+    <Reference Include="HandyControl, Version=3.1.0.0, Culture=neutral, PublicKeyToken=45be8712787a1e5b, processorArchitecture=MSIL">
+      <HintPath>packages\HandyControl.3.1.0\lib\net452\HandyControl.dll</HintPath>
+    </Reference>
+    <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <HintPath>packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Drawing.Common, Version=4.0.0.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>packages\System.Drawing.Common.5.0.2\lib\net461\System.Drawing.Common.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <HintPath>packages\MvvmLightLibs.5.4.1.1\lib\net45\System.Windows.Interactivity.dll</HintPath>
+    </Reference>
+    <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="PresentationCore" />
+    <Reference Include="PresentationFramework" />
+  </ItemGroup>
+  <ItemGroup>
+    <ApplicationDefinition Include="App.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </ApplicationDefinition>
+    <Compile Include="Command\DelegateCommand.cs" />
+    <Compile Include="Command\DelegateCommandBase.cs" />
+    <Compile Include="Constant\DefaultConstant.cs" />
+    <Compile Include="Constant\SortType.cs" />
+    <Compile Include="DraggAnimatedPanel\DraggAnimatedPanel.cs" />
+    <Compile Include="DraggAnimatedPanel\DraggAnimatedPanel.Drag.cs" />
+    <Compile Include="Util\CommonCode.cs" />
+    <Compile Include="Util\ConsoleManager.cs" />
+    <Compile Include="Util\DragAdorner.cs" />
+    <Compile Include="Util\FileIcon.cs" />
+    <Compile Include="Util\ListViewDragDropManager.cs" />
+    <Compile Include="Util\MenuWidthConvert.cs" />
+    <Compile Include="Util\MouseUtilities.cs" />
+    <Compile Include="Util\SystemIcon.cs" />
+    <Compile Include="ViewModel\AppConfig.cs" />
+    <Compile Include="ViewModel\DataInfos.cs" />
+    <Compile Include="ViewModel\MainModel.cs" />
+    <Compile Include="ViewModel\MainViewModel.cs" />
+    <Compile Include="ViewModel\MenuViewModel.cs" />
+    <Page Include="MainWindow.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Compile Include="App.xaml.cs">
+      <DependentUpon>App.xaml</DependentUpon>
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="MainWindow.xaml.cs">
+      <DependentUpon>MainWindow.xaml</DependentUpon>
+      <SubType>Code</SubType>
+    </Compile>
+  </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" />
+</Project>

+ 25 - 0
GeekDesk.sln

@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Blend for Visual Studio Version 16
+VisualStudioVersion = 16.0.30907.101
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeekDesk", "GeekDesk.csproj", "{B4983CEC-2281-413C-8ECF-92EE0E40A713}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{B4983CEC-2281-413C-8ECF-92EE0E40A713}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B4983CEC-2281-413C-8ECF-92EE0E40A713}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B4983CEC-2281-413C-8ECF-92EE0E40A713}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B4983CEC-2281-413C-8ECF-92EE0E40A713}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {7D844F27-975B-4CFF-A373-E665FB8866DE}
+	EndGlobalSection
+EndGlobal

+ 219 - 0
MainWindow.xaml

@@ -0,0 +1,219 @@
+<Window x:Class="GeekDesk.MainWindow"
+        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:local="clr-namespace:GeekDesk"
+        mc:Ignorable="d"
+        xmlns:util="clr-namespace:GeekDesk.Util"
+        xmlns:DraggAnimatedPanel="clr-namespace:DraggAnimatedPanel" x:Name="window"
+        xmlns:hc="https://handyorg.github.io/handycontrol"
+        Title="MainWindow" Height="450" Width="800">
+    <Window.Resources>
+
+        <Style x:Key="ListBoxStyle" BasedOn="{StaticResource ListBoxBaseStyle}" TargetType="ListBox"/>
+        <Style TargetType="{x:Type ListBoxItem}" x:Key="memuStory" BasedOn="{StaticResource ListBoxStyle}">
+            <Style.Triggers>
+                <!--鼠标移入-->
+                <EventTrigger RoutedEvent="MouseMove">
+                    <BeginStoryboard>
+                        <Storyboard>
+                            <!--鼠标移入放大-->
+                            <DoubleAnimation To="20" Duration="0:0:0.001" Storyboard.TargetProperty="FontSize"/>
+                        </Storyboard>
+                    </BeginStoryboard>
+                </EventTrigger>
+                <!--鼠标移出-->
+                <EventTrigger RoutedEvent="MouseLeave">
+                    <BeginStoryboard>
+                        <Storyboard>
+                            <!--鼠标移出恢复正常大小-->
+                            <DoubleAnimation To="15" Duration="0:0:0.001" Storyboard.TargetProperty="FontSize"/>
+                        </Storyboard>
+                    </BeginStoryboard>
+                </EventTrigger>
+            </Style.Triggers>
+        </Style>
+
+
+        <!--左侧栏样式动画-->
+        <Style x:Key="menuStyle" TargetType="ListBoxItem" BasedOn="{StaticResource ListBoxItemBaseStyle}">
+            <Setter Property="FontSize" Value="15"/>
+            <Setter Property="RenderTransform">
+                <Setter.Value>
+                    <TranslateTransform/>
+                </Setter.Value>
+            </Setter>
+            <Style.Triggers>
+                <MultiTrigger>
+                    <MultiTrigger.Conditions>
+                        <Condition Property="IsMouseOver" Value="True"/>
+                    </MultiTrigger.Conditions>
+                    <MultiTrigger.EnterActions>
+                        <BeginStoryboard>
+                            <Storyboard>
+                                <DoubleAnimation To="20" Duration="0:0:0.001" Storyboard.TargetProperty="FontSize"/>
+                            </Storyboard>
+                        </BeginStoryboard>
+                    </MultiTrigger.EnterActions>
+                    <MultiTrigger.ExitActions>
+                        <BeginStoryboard>
+                            <Storyboard>
+                                <DoubleAnimation To="15" Duration="0:0:0.5" Storyboard.TargetProperty="FontSize"/>
+                            </Storyboard>
+                        </BeginStoryboard>
+                    </MultiTrigger.ExitActions>
+                </MultiTrigger>
+            </Style.Triggers>
+        </Style>
+
+        <!--右侧栏样式动画-->
+        <Style x:Key="imageStyle" TargetType="Image">
+            <Setter Property="Width" Value="60"/>
+            <Setter Property="Height" Value="60"/>
+            <Setter Property="Source" Value="{Binding BitmapImage}"/>
+            <Style.Triggers>
+                <MultiTrigger>
+                    <MultiTrigger.Conditions>
+                        <Condition Property="IsMouseOver" Value="True"/>
+                    </MultiTrigger.Conditions>
+                    <MultiTrigger.EnterActions>
+                        <BeginStoryboard>
+                            <Storyboard>
+                                <DoubleAnimation To="80" Duration="0:0:0.1" Storyboard.TargetProperty="Width"/>
+                                <DoubleAnimation To="80" Duration="0:0:0.1" Storyboard.TargetProperty="Height"/>
+                            </Storyboard>
+                        </BeginStoryboard>
+                    </MultiTrigger.EnterActions>
+                    <MultiTrigger.ExitActions>
+                        <BeginStoryboard>
+                            <Storyboard>
+                                <DoubleAnimation To="60" Duration="0:0:0.5" Storyboard.TargetProperty="Width"/>
+                                <DoubleAnimation To="60" Duration="0:0:0.5" Storyboard.TargetProperty="Height"/>
+                            </Storyboard>
+                        </BeginStoryboard>
+                    </MultiTrigger.ExitActions>
+                </MultiTrigger>
+            </Style.Triggers>
+
+        </Style>
+
+        <util:MenuWidthConvert x:Key="MenuWidthConvert"/>
+
+    </Window.Resources>
+
+
+
+    <Grid>
+        <!--背景图片-->
+        <Grid.Background>
+            <ImageBrush ImageSource="D:\壁纸\18078.jpg"></ImageBrush>
+        </Grid.Background>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="40"></RowDefinition>
+            <RowDefinition Height="*"></RowDefinition>
+        </Grid.RowDefinitions>
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition x:Name="leftColumn" MinWidth="80" Width="150" MaxWidth="200"></ColumnDefinition>
+            <ColumnDefinition Width="*"></ColumnDefinition>
+        </Grid.ColumnDefinitions>
+        <DockPanel Grid.Row="0" Grid.Column="0">
+            <Label>Text</Label>
+        </DockPanel>
+
+        <!--左侧栏-->
+        <hc:Card Opacity="1" x:Name="leftCard" Grid.Row="1" Grid.Column="0"
+                 BorderThickness="1" 
+                 Effect="{DynamicResource EffectShadow2}" 
+                 Margin="5,5,0,5"
+                 >
+            <!--<hc:Card.ContextMenu>
+                <ContextMenu Width="200">
+                    <TextBlock Text="新建菜单"/>
+                </ContextMenu>
+            </hc:Card.ContextMenu>-->
+            <!--<ListBox  x:Name="menu"   BorderThickness="0"  ItemsSource="{Binding}"   Margin="10,8,10,8"
+                      >
+                <ListBox.ItemTemplate>
+                    <DataTemplate>
+                        <TextBlock Text="{Binding menu}" FontSize="15" Style="{StaticResource memuStory}"  PreviewMouseLeftButtonDown="menuClick" RenderTransformOrigin="0.5,0.5">
+                           
+                        </TextBlock>
+                    </DataTemplate>
+                </ListBox.ItemTemplate>
+            </ListBox>-->
+
+            <ListBox x:Name="menu"  ItemsSource="{Binding}">
+                <ListBox.Resources>
+                    <ContextMenu x:Key="menuDialog" Width="200">
+                        <MenuItem Header="新建菜单"/>
+                        <MenuItem Header="重命名"/>
+                        <MenuItem Header="删除" Click="deleteMenu"/>
+                    </ContextMenu>
+                </ListBox.Resources>
+
+                <ListBox.ItemContainerStyle>
+                    <Style TargetType="ListBoxItem" BasedOn="{StaticResource menuStyle}">
+                        <Setter Property="ContextMenu" Value="{StaticResource menuDialog}"/>
+                    </Style>
+                </ListBox.ItemContainerStyle>
+                <ListBox.ItemsPanel>
+                    <ItemsPanelTemplate>
+                        <DraggAnimatedPanel:DraggAnimatedPanel ItemsHeight="30" ItemsWidth="{Binding ElementName=leftColumn, Path=Width, Converter={StaticResource MenuWidthConvert}}"   HorizontalAlignment="Center" VerticalAlignment="Top" SwapCommand="{Binding SwapCommand2, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
+                    </ItemsPanelTemplate>
+                </ListBox.ItemsPanel>
+
+                <ListBox.ItemTemplate>
+                    <DataTemplate>
+                        <TextBlock Text="{Binding menu}"  PreviewMouseLeftButtonDown="menuClick" />
+                    </DataTemplate>
+                </ListBox.ItemTemplate>
+            </ListBox>
+        </hc:Card>
+        <!--分割线-->
+        <GridSplitter Opacity="0" Grid.Row="1" Grid.Column="0" Width="1"  VerticalAlignment="Stretch" HorizontalAlignment="Right"/>
+
+        <!--右侧栏-->
+        <hc:Card AllowDrop="True" Drop="Wrap_Drop" Opacity="1" x:Name="rightCard" Grid.Row="1" Grid.Column="1"  BorderThickness="1" Effect="{DynamicResource EffectShadow2}" Margin="5,5,5,5">
+            <WrapPanel Orientation="Horizontal">
+                <ListBox x:Name="data" ItemsSource="{Binding}" 
+                         BorderThickness="0"
+                         SelectionChanged="data_SelectionChanged"
+                         >
+                    <!--<ListBox.ItemContainerStyle>
+                        <Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource dataStyle}"/>
+                    </ListBox.ItemContainerStyle>-->
+                    <ListBox.ItemsPanel>
+                        <ItemsPanelTemplate>
+                            <DraggAnimatedPanel:DraggAnimatedPanel ItemsHeight="115" ItemsWidth="100"  HorizontalAlignment="Center" SwapCommand="{Binding SwapCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
+                        </ItemsPanelTemplate>
+                    </ListBox.ItemsPanel>
+
+                    <ListBox.ItemTemplate>
+                        <DataTemplate>
+                            <Border Margin="5,5,5,5" CornerRadius="10">
+                                <StackPanel  Tag="{Binding Path}"
+                                             MouseLeftButtonDown="dataClick"
+                                             HorizontalAlignment="Center"
+                                             hc:Poptip.HitMode="None" 
+                                             hc:Poptip.IsOpen="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}"
+                                             hc:Poptip.Content="{Binding Path}" 
+                                             hc:Poptip.Placement="BottomLeft"
+                                             Margin="5,5,5,5"
+                                             Height="115"
+                                             >
+                                    <Image Style="{StaticResource imageStyle}"></Image>
+                                    <TextBlock Width="80" TextWrapping="Wrap" TextAlignment="Center" Height="35" LineHeight="15" FontSize="12" Text="{Binding Name}"/>
+                                </StackPanel>
+                            </Border>
+                        </DataTemplate>
+                    </ListBox.ItemTemplate>
+                </ListBox>
+            </WrapPanel>
+
+        </hc:Card>
+
+
+
+    </Grid>
+</Window>

+ 324 - 0
MainWindow.xaml.cs

@@ -0,0 +1,324 @@
+using System;
+using System.Collections.Generic;
+
+using System.Windows;
+
+using System.Windows.Input;
+using System.Windows.Media.Imaging;
+
+using GeekDesk.ViewModel;
+using System.IO;
+using GeekDesk.Util;
+using GalaSoft.MvvmLight;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Collections.ObjectModel;
+using WPF.JoshSmith.ServiceProviders.UI;
+using DraggAnimatedPanelExample;
+using System.ComponentModel;
+
+namespace GeekDesk
+{
+    /// <summary>
+    /// MainWindow.xaml 的交互逻辑
+    /// </summary>
+    /// 
+    public partial class MainWindow : Window
+    {
+        private static MainModel mainModel;
+
+        ListViewDragDropManager<ViewModel.Menu> dragMgr;
+        ListViewDragDropManager<ViewModel.DataInfos> dragMgr2;
+        public MainWindow()
+        {
+            InitializeComponent();
+
+            mainModel = new MainModel();
+            //this.DataContext = mainModel;
+            //menu.Items = mainModel;
+            //System.Diagnostics.Process.Start(@"D:\SoftWare\WeGame\wegame.exe");
+            this.Loaded += Window_Loaded;
+            this.SizeChanged += MainWindow_Resize;
+        }
+
+        DelegateCommand<int[]> _swap;
+        public DelegateCommand<int[]> SwapCommand
+        {
+            get
+            {
+                if (_swap == null)
+                    _swap = new DelegateCommand<int[]>(
+                        (indexes) =>
+                        {
+                            int fromS = indexes[0];
+                            int to = indexes[1];
+                            var elementSource = data.Items[to];
+                            var dragged = data.Items[fromS];
+                            if (fromS > to)
+                            {
+                                data.Items.Remove(dragged);
+                                data.Items.Insert(to, dragged);
+                            }
+                            else
+                            {
+                                data.Items.Remove(dragged);
+                                data.Items.Insert(to, dragged);
+                            }
+                        }
+                    );
+                return _swap;
+            }
+        }
+        DelegateCommand<int[]> _swap2;
+        public DelegateCommand<int[]> SwapCommand2
+        {
+            get
+            {
+                if (_swap2 == null)
+                    _swap2 = new DelegateCommand<int[]>(
+                        (indexes) =>
+                        {
+                            int fromS = indexes[0];
+                            int to = indexes[1];
+                            var elementSource = menu.Items[to];
+                            var dragged = menu.Items[fromS];
+                            if (fromS > to)
+                            {
+                                menu.Items.Remove(dragged);
+                                menu.Items.Insert(to, dragged);
+                            }
+                            else
+                            {
+                                menu.Items.Remove(dragged);
+                                menu.Items.Insert(to, dragged);
+                            }
+                        }
+                    );
+                return _swap2;
+            }
+        }
+
+
+
+        private void Wrap_Drop(object sender, DragEventArgs e)
+        {
+            Array dropObject = (System.Array)e.Data.GetData(DataFormats.FileDrop);
+            if (dropObject == null) return;
+            string path = (string)dropObject.GetValue(0);
+            if (File.Exists(path))
+            {
+                // 文件
+                BitmapImage bi = FileIcon.GetBitmapImage(path);
+                DataInfos infos = new DataInfos();
+                infos.Path = path;
+                infos.BitmapImage = bi;
+                infos.Name = Path.GetFileNameWithoutExtension(path);
+                data.Items.Add(infos);
+                data.Items.Refresh();
+            }
+            else if (Directory.Exists(path))
+            {
+                //文件夹
+
+            }
+
+        }
+
+        //菜单点击事件
+        private void menuClick(object sender, MouseButtonEventArgs e)
+        {
+
+        }
+
+
+
+        /// <summary>
+        /// 图标点击事件
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void dataClick(object sender, MouseButtonEventArgs e)
+        {
+            //string path = ((StackPanel)sender).Tag.ToString();
+            //System.Diagnostics.Process.Start(path);
+        }
+
+        /// <summary>
+        /// data选中事件 设置不可选中
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void data_SelectionChanged(object sender, SelectionChangedEventArgs e)
+        {
+            if (data.SelectedIndex != -1) data.SelectedIndex = -1;
+        }
+
+        #region Window_Loaded
+        void Window_Loaded(object sender, RoutedEventArgs e)
+        {
+            AppConfig config = CommonCode.GetAppConfig();
+            this.Width = config.WindowWidth;
+            this.Height = config.WindowHeight;
+            this.DataContext = config;
+
+            this.menu.Items.Add(new ViewModel.Menu() { menu = "test1" });
+            this.menu.Items.Add(new ViewModel.Menu() { menu = "test2" });
+            this.menu.Items.Add(new ViewModel.Menu() { menu = "test3" });
+        }
+        #endregion // Window_Loaded
+
+        #region Window_Closing
+        void Window_Closing(object sender, CancelEventArgs e)
+        {
+            Rect rect = this.RestoreBounds;
+            AppConfig config = this.DataContext as AppConfig;
+            config.WindowWidth = rect.Width;
+            config.WindowHeight = rect.Height;
+            CommonCode.SaveAppConfig(config);
+        }
+        #endregion // Window_Closing
+
+        void MainWindow_Resize(object sender, System.EventArgs e)
+        {
+            if (this.DataContext != null)
+            {
+                AppConfig config = this.DataContext as AppConfig;
+                config.WindowWidth = this.Width;
+                config.WindowHeight = this.Height;
+                CommonCode.SaveAppConfig(config);
+            }
+
+        }
+
+
+
+        #region dragMgr_ProcessDrop
+
+        // Performs custom drop logic for the top ListView.
+        void dragMgr_ProcessDrop(object sender, ProcessDropEventArgs<object> e)
+        {
+            // This shows how to customize the behavior of a drop.
+            // Here we perform a swap, instead of just moving the dropped item.
+
+            int higherIdx = Math.Max(e.OldIndex, e.NewIndex);
+            int lowerIdx = Math.Min(e.OldIndex, e.NewIndex);
+
+            if (lowerIdx < 0)
+            {
+                // The item came from the lower ListView
+                // so just insert it.
+                e.ItemsSource.Insert(higherIdx, e.DataItem);
+            }
+            else
+            {
+                // null values will cause an error when calling Move.
+                // It looks like a bug in ObservableCollection to me.
+                if (e.ItemsSource[lowerIdx] == null ||
+                    e.ItemsSource[higherIdx] == null)
+                    return;
+
+                // The item came from the ListView into which
+                // it was dropped, so swap it with the item
+                // at the target index.
+                e.ItemsSource.Move(lowerIdx, higherIdx);
+                e.ItemsSource.Move(higherIdx - 1, lowerIdx);
+            }
+
+            // Set this to 'Move' so that the OnListViewDrop knows to 
+            // remove the item from the other ListView.
+            e.Effects = DragDropEffects.Move;
+        }
+
+        #endregion // dragMgr_ProcessDrop
+
+        #region OnListViewDragEnter
+
+        // Handles the DragEnter event for both ListViews.
+        void OnListViewDragEnter(object sender, DragEventArgs e)
+        {
+            e.Effects = DragDropEffects.Move;
+        }
+
+        #endregion // OnListViewDragEnter
+
+        #region OnListViewDrop
+
+        // Handles the Drop event for both ListViews.
+        void OnListViewDrop(object sender, DragEventArgs e)
+        {
+            if (e.Effects == DragDropEffects.None)
+                return;
+            ViewModel.Menu menuV = e.Data.GetData(typeof(ViewModel.Menu)) as ViewModel.Menu;
+            DataInfos data = e.Data.GetData(typeof(DataInfos)) as DataInfos;
+
+            if (sender == this.menu)
+            {
+                if (this.dragMgr.IsDragInProgress)
+                    return;
+
+                // An item was dragged from the bottom ListView into the top ListView
+                // so remove that item from the bottom ListView.
+                (this.data.ItemsSource as ObservableCollection<DataInfos>).Remove(data);
+            }
+            else
+            {
+                if (this.dragMgr2.IsDragInProgress)
+                    return;
+
+                // An item was dragged from the top ListView into the bottom ListView
+                // so remove that item from the top ListView.
+                (this.menu.ItemsSource as ObservableCollection<ViewModel.Menu>).Remove(menuV);
+            }
+        }
+
+        #endregion // OnListViewDrop
+
+        private void leftCard_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
+        {
+
+        }
+
+        private void deleteMenu(object sender, RoutedEventArgs e)
+        {
+            //if (data.SelectedIndex == -1)
+            //{
+            //    return;
+            //}
+            ViewModel.Menu pojo = (ViewModel.Menu)((ContextMenu)((MenuItem)sender).Parent).DataContext;
+            string menuTitle = pojo.menu;
+            int index = 0;
+            foreach (object obj in menu.Items)
+            {
+                string test = ((ViewModel.Menu)obj).menu;
+                if (test == menuTitle)
+                {
+                    menu.Items.RemoveAt(index);
+                    menu.Items.Refresh();
+                    return;
+                }
+                index++;
+            }
+
+        }
+
+        public Double ConvertString(string val)
+        {
+            return Convert.ToDouble(val);
+        }
+
+    }
+
+
+
+
+    public class MainModel : ViewModelBase
+    {
+        public List<ViewModel.Menu> MenuList { get; set; }
+
+        public List<ViewModel.DataInfos> DataList { get; set; }
+
+
+    }
+
+
+}

+ 55 - 0
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("GeekDesk")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("GeekDesk")]
+[assembly: AssemblyCopyright("Copyright ©  2021")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 将 ComVisible 设置为 false 会使此程序集中的类型
+//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
+//请将此类型的 ComVisible 特性设置为 true。
+[assembly: ComVisible(false)]
+
+//若要开始生成可本地化的应用程序,请设置
+//.csproj 文件中的 <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.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 63 - 0
Properties/Resources.Designer.cs

@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     此代码由工具生成。
+//     运行时版本:4.0.30319.42000
+//
+//     对此文件的更改可能会导致不正确的行为,并且如果
+//     重新生成代码,这些更改将会丢失。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace GeekDesk.Properties {
+    using System;
+    
+    
+    /// <summary>
+    ///   一个强类型的资源类,用于查找本地化的字符串等。
+    /// </summary>
+    // 此类是由 StronglyTypedResourceBuilder
+    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
+    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
+    // (以 /str 作为命令选项),或重新生成 VS 项目。
+    [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>
+        ///   返回此类使用的缓存的 ResourceManager 实例。
+        /// </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("GeekDesk.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   重写当前线程的 CurrentUICulture 属性,对
+        ///   使用此强类型资源类的所有资源查找执行重写。
+        /// </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
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
Properties/Settings.Designer.cs

@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     此代码由工具生成。
+//     运行时版本:4.0.30319.42000
+//
+//     对此文件的更改可能会导致不正确的行为,并且如果
+//     重新生成代码,这些更改将会丢失。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace GeekDesk.Properties {
+    
+    
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.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
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>

+ 59 - 0
Util/CommonCode.cs

@@ -0,0 +1,59 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.Serialization.Formatters.Binary;
+using System.Text;
+using System.Threading.Tasks;
+using GeekDesk.ViewModel;
+
+/// <summary>
+/// 提取一些代码
+/// </summary>
+namespace GeekDesk.Util
+{
+    class CommonCode
+    {
+        private static string appConfigFilePath = AppDomain.CurrentDomain.BaseDirectory.Trim() + "\\config";
+        /// <summary>
+        /// 获取app配置
+        /// </summary>
+        /// <returns></returns>
+        public static AppConfig GetAppConfig()
+        {
+            AppConfig config;
+            if (!File.Exists(appConfigFilePath))
+            {
+                using (FileStream fs = File.Create(appConfigFilePath)) { }
+                config = new AppConfig();
+                SaveAppConfig(config);
+                   
+            }
+            else
+            {
+                using (FileStream fs = new FileStream(appConfigFilePath, FileMode.Open))
+                {
+                    BinaryFormatter bf = new BinaryFormatter();
+                    string json = bf.Deserialize(fs) as string;
+                    config = JsonConvert.DeserializeObject<AppConfig>(json);
+                }
+            }
+            return config;
+        }
+
+        /// <summary>
+        /// 保存app配置
+        /// </summary>
+        /// <param name="config"></param>
+        public static void SaveAppConfig(AppConfig config)
+        {
+            using (FileStream fs = new FileStream(appConfigFilePath, FileMode.Create))
+            {
+                BinaryFormatter bf = new BinaryFormatter();
+                string json = JsonConvert.SerializeObject(config);
+                bf.Serialize(fs, json);
+            }
+        }
+    }
+}

+ 105 - 0
Util/ConsoleManager.cs

@@ -0,0 +1,105 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GeekDesk.Util
+{
+    [SuppressUnmanagedCodeSecurity]
+    public static class ConsoleManager
+    {
+        private const string Kernel32_DllName = "kernel32.dll";
+
+        [DllImport(Kernel32_DllName)]
+        private static extern bool AllocConsole();
+
+        [DllImport(Kernel32_DllName)]
+        private static extern bool FreeConsole();
+
+        [DllImport(Kernel32_DllName)]
+        private static extern IntPtr GetConsoleWindow();
+
+        [DllImport(Kernel32_DllName)]
+        private static extern int GetConsoleOutputCP();
+
+        public static bool HasConsole
+        {
+            get { return GetConsoleWindow() != IntPtr.Zero; }
+        }
+
+        /// <summary>
+        /// Creates a new console instance if the process is not attached to a console already.
+        /// </summary>
+        public static void Show()
+        {
+            //#if DEBUG
+            if (!HasConsole)
+            {
+                AllocConsole();
+                InvalidateOutAndError();
+            }
+            //#endif
+        }
+
+        /// <summary>
+        /// If the process has a console attached to it, it will be detached and no longer visible. Writing to the System.Console is still possible, but no output will be shown.
+        /// </summary>
+        public static void Hide()
+        {
+            //#if DEBUG
+            if (HasConsole)
+            {
+                SetOutAndErrorNull();
+                FreeConsole();
+            }
+            //#endif
+        }
+
+        public static void Toggle()
+        {
+            if (HasConsole)
+            {
+                Hide();
+            }
+            else
+            {
+                Show();
+            }
+        }
+
+        static void InvalidateOutAndError()
+        {
+            Type type = typeof(System.Console);
+
+            System.Reflection.FieldInfo _out = type.GetField("_out",
+                System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
+
+            System.Reflection.FieldInfo _error = type.GetField("_error",
+                System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
+
+            System.Reflection.MethodInfo _InitializeStdOutError = type.GetMethod("InitializeStdOutError",
+                System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
+
+            Debug.Assert(_out != null);
+            Debug.Assert(_error != null);
+
+            Debug.Assert(_InitializeStdOutError != null);
+
+            _out.SetValue(null, null);
+            _error.SetValue(null, null);
+
+            _InitializeStdOutError.Invoke(null, new object[] { true });
+        }
+
+        static void SetOutAndErrorNull()
+        {
+            Console.SetOut(TextWriter.Null);
+            Console.SetError(TextWriter.Null);
+        }
+    }
+}

+ 175 - 0
Util/DragAdorner.cs

@@ -0,0 +1,175 @@
+// Copyright (C) Josh Smith - January 2007
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Documents;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Shapes;
+using System.Windows.Media.Animation;
+using System.Windows.Controls;
+
+namespace WPF.JoshSmith.Adorners
+{
+	/// <summary>
+	/// Renders a visual which can follow the mouse cursor, 
+	/// such as during a drag-and-drop operation.
+	/// </summary>
+	public class DragAdorner : Adorner
+	{
+		#region Data
+
+		private Rectangle child = null;
+		private double offsetLeft = 0;
+		private double offsetTop = 0;
+
+		#endregion // Data
+
+		#region Constructor
+
+		/// <summary>
+		/// Initializes a new instance of DragVisualAdorner.
+		/// </summary>
+		/// <param name="adornedElement">The element being adorned.</param>
+		/// <param name="size">The size of the adorner.</param>
+		/// <param name="brush">A brush to with which to paint the adorner.</param>
+		public DragAdorner( UIElement adornedElement, Size size, Brush brush )
+			: base( adornedElement )
+		{
+			Rectangle rect = new Rectangle();
+			rect.Fill = brush;
+			rect.Width = size.Width;
+			rect.Height = size.Height;
+			rect.IsHitTestVisible = false;
+			this.child = rect;
+		}
+
+		#endregion // Constructor
+
+		#region Public Interface
+
+		#region GetDesiredTransform
+
+		/// <summary>
+		/// Override.
+		/// </summary>
+		/// <param name="transform"></param>
+		/// <returns></returns>
+		public override GeneralTransform GetDesiredTransform( GeneralTransform transform )
+		{
+			GeneralTransformGroup result = new GeneralTransformGroup();
+			result.Children.Add( base.GetDesiredTransform( transform ) );
+			result.Children.Add( new TranslateTransform( this.offsetLeft, this.offsetTop ) );
+			return result;
+		}
+
+		#endregion // GetDesiredTransform
+
+		#region OffsetLeft
+
+		/// <summary>
+		/// Gets/sets the horizontal offset of the adorner.
+		/// </summary>
+		public double OffsetLeft
+		{
+			get { return this.offsetLeft; }
+			set
+			{
+				this.offsetLeft = value;
+				UpdateLocation();
+			}
+		}
+
+		#endregion // OffsetLeft
+
+		#region SetOffsets
+
+		/// <summary>
+		/// Updates the location of the adorner in one atomic operation.
+		/// </summary>
+		/// <param name="left"></param>
+		/// <param name="top"></param>
+		public void SetOffsets( double left, double top )
+		{
+			this.offsetLeft = left;
+			this.offsetTop = top;
+			this.UpdateLocation();
+		}
+
+		#endregion // SetOffsets
+
+		#region OffsetTop
+
+		/// <summary>
+		/// Gets/sets the vertical offset of the adorner.
+		/// </summary>
+		public double OffsetTop
+		{
+			get { return this.offsetTop; }
+			set
+			{
+				this.offsetTop = value;
+				UpdateLocation();
+			}
+		}
+
+		#endregion // OffsetTop
+
+		#endregion // Public Interface
+
+		#region Protected Overrides
+
+		/// <summary>
+		/// Override.
+		/// </summary>
+		/// <param name="constraint"></param>
+		/// <returns></returns>
+		protected override Size MeasureOverride( Size constraint )
+		{
+			this.child.Measure( constraint );
+			return this.child.DesiredSize;
+		}
+
+		/// <summary>
+		/// Override.
+		/// </summary>
+		/// <param name="finalSize"></param>
+		/// <returns></returns>
+		protected override Size ArrangeOverride( Size finalSize )
+		{
+			this.child.Arrange( new Rect( finalSize ) );
+			return finalSize;
+		}
+
+		/// <summary>
+		/// Override.
+		/// </summary>
+		/// <param name="index"></param>
+		/// <returns></returns>
+		protected override Visual GetVisualChild( int index )
+		{
+			return this.child;
+		}
+
+		/// <summary>
+		/// Override.  Always returns 1.
+		/// </summary>
+		protected override int VisualChildrenCount
+		{
+			get { return 1; }
+		}
+
+		#endregion // Protected Overrides
+
+		#region Private Helpers
+
+		private void UpdateLocation()
+		{
+			AdornerLayer adornerLayer = this.Parent as AdornerLayer;
+			if( adornerLayer != null )
+				adornerLayer.Update( this.AdornedElement );
+		}
+
+		#endregion // Private Helpers
+	}
+}

+ 376 - 0
Util/FileIcon.cs

@@ -0,0 +1,376 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using System.Runtime.InteropServices;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Windows.Media.Imaging;
+
+namespace GeekDesk.Util
+{
+    class FileIcon
+    {
+
+
+        public static Icon GetIcon(string filePath)
+        {
+            IntPtr hIcon = GetJumboIcon(GetIconIndex(filePath));
+            Icon ico = Icon.FromHandle(hIcon);
+            return ico;
+        }
+
+        public static BitmapImage GetBitmapImage(string filePath)
+        {
+            //Icon ico;
+            //BitmapImage bmpImage = null;
+            //MemoryStream strm;
+            //using (ico = GetIcon(filePath))
+            //{
+            //    Bitmap bmp = ico.ToBitmap();
+            //    using (strm = new MemoryStream())
+            //    {
+            //        bmp.Save(strm, System.Drawing.Imaging.ImageFormat.Png);
+            //        bmpImage = new BitmapImage();
+            //        bmpImage.BeginInit();
+            //        strm.Seek(0, SeekOrigin.Begin);
+            //        bmpImage.StreamSource = strm;
+            //        bmpImage.EndInit();
+            //    }
+            //}
+            //return bmpImage;
+            Icon ico = GetIcon(filePath);
+            Bitmap bmp = ico.ToBitmap();
+            MemoryStream strm = new MemoryStream();
+            bmp.Save(strm, System.Drawing.Imaging.ImageFormat.Png);
+            BitmapImage  bmpImage = new BitmapImage();
+            bmpImage.BeginInit();
+            strm.Seek(0, SeekOrigin.Begin);
+            bmpImage.StreamSource = strm;
+            bmpImage.EndInit();
+            
+            return bmpImage.Clone();
+        }
+
+        public static int GetIconIndex(string pszFile)
+        {
+            SHFILEINFO sfi = new SHFILEINFO();
+            Shell32.SHGetFileInfo(pszFile
+                , 0
+                , ref sfi
+                , (uint)System.Runtime.InteropServices.Marshal.SizeOf(sfi)
+                , (uint)(SHGFI.SysIconIndex | SHGFI.LargeIcon | SHGFI.UseFileAttributes));
+            return sfi.iIcon;
+        }
+
+        // 256*256
+        public static IntPtr GetJumboIcon(int iImage)
+        {
+            IImageList spiml = null;
+            Guid guil = new Guid(IID_IImageList2);//or IID_IImageList
+
+            Shell32.SHGetImageList(Shell32.SHIL_JUMBO, ref guil, ref spiml);
+            IntPtr hIcon = IntPtr.Zero;
+            spiml.GetIcon(iImage, Shell32.ILD_TRANSPARENT | Shell32.ILD_IMAGE, ref hIcon);
+
+            return hIcon;
+        }
+
+        const string IID_IImageList = "46EB5926-582E-4017-9FDF-E8998DAA0950";
+        const string IID_IImageList2 = "192B9D83-50FC-457B-90A0-2B82A8B5DAE1";
+
+        public static class Shell32
+        {
+
+            public const int SHIL_LARGE = 0x0;
+            public const int SHIL_SMALL = 0x1;
+            public const int SHIL_EXTRALARGE = 0x2;
+            public const int SHIL_SYSSMALL = 0x3;
+            public const int SHIL_JUMBO = 0x4;
+            public const int SHIL_LAST = 0x4;
+
+            public const int ILD_TRANSPARENT = 0x00000001;
+            public const int ILD_IMAGE = 0x00000020;
+
+            [DllImport("shell32.dll", EntryPoint = "#727")]
+            public extern static int SHGetImageList(int iImageList, ref Guid riid, ref IImageList ppv);
+
+            [DllImport("user32.dll", EntryPoint = "DestroyIcon", SetLastError = true)]
+            public static unsafe extern int DestroyIcon(IntPtr hIcon);
+
+            [DllImport("shell32.dll")]
+            public static extern uint SHGetIDListFromObject([MarshalAs(UnmanagedType.IUnknown)] object iUnknown, out IntPtr ppidl);
+
+            [DllImport("Shell32.dll")]
+            public static extern IntPtr SHGetFileInfo(
+                string pszPath,
+                uint dwFileAttributes,
+                ref SHFILEINFO psfi,
+                uint cbFileInfo,
+                uint uFlags
+            );
+        }
+
+        [Flags]
+        enum SHGFI : uint
+        {
+            /// <summary>get icon</summary>
+            Icon = 0x000000100,
+            /// <summary>get display name</summary>
+            DisplayName = 0x000000200,
+            /// <summary>get type name</summary>
+            TypeName = 0x000000400,
+            /// <summary>get attributes</summary>
+            Attributes = 0x000000800,
+            /// <summary>get icon location</summary>
+            IconLocation = 0x000001000,
+            /// <summary>return exe type</summary>
+            ExeType = 0x000002000,
+            /// <summary>get system icon index</summary>
+            SysIconIndex = 0x000004000,
+            /// <summary>put a link overlay on icon</summary>
+            LinkOverlay = 0x000008000,
+            /// <summary>show icon in selected state</summary>
+            Selected = 0x000010000,
+            /// <summary>get only specified attributes</summary>
+            Attr_Specified = 0x000020000,
+            /// <summary>get large icon</summary>
+            LargeIcon = 0x000000000,
+            /// <summary>get small icon</summary>
+            SmallIcon = 0x000000001,
+            /// <summary>get open icon</summary>
+            OpenIcon = 0x000000002,
+            /// <summary>get shell size icon</summary>
+            ShellIconSize = 0x000000004,
+            /// <summary>pszPath is a pidl</summary>
+            PIDL = 0x000000008,
+            /// <summary>use passed dwFileAttribute</summary>
+            UseFileAttributes = 0x000000010,
+            /// <summary>apply the appropriate overlays</summary>
+            AddOverlays = 0x000000020,
+            /// <summary>Get the index of the overlay in the upper 8 bits of the iIcon</summary>
+            OverlayIndex = 0x000000040,
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct SHFILEINFO
+        {
+            public const int NAMESIZE = 80;
+            public IntPtr hIcon;
+            public int iIcon;
+            public uint dwAttributes;
+            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
+            public string szDisplayName;
+            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
+            public string szTypeName;
+        };
+
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct RECT
+        {
+            public int left, top, right, bottom;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct POINT
+        {
+            int x;
+            int y;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct IMAGELISTDRAWPARAMS
+        {
+            public int cbSize;
+            public IntPtr himl;
+            public int i;
+            public IntPtr hdcDst;
+            public int x;
+            public int y;
+            public int cx;
+            public int cy;
+            public int xBitmap;    // x offest from the upperleft of bitmap
+            public int yBitmap;    // y offset from the upperleft of bitmap
+            public int rgbBk;
+            public int rgbFg;
+            public int fStyle;
+            public int dwRop;
+            public int fState;
+            public int Frame;
+            public int crEffect;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct IMAGEINFO
+        {
+            public IntPtr hbmImage;
+            public IntPtr hbmMask;
+            public int Unused1;
+            public int Unused2;
+            public RECT rcImage;
+        }
+        [ComImportAttribute()]
+        [GuidAttribute("46EB5926-582E-4017-9FDF-E8998DAA0950")]
+        [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
+        public interface IImageList
+        {
+            [PreserveSig]
+            int Add(
+            IntPtr hbmImage,
+            IntPtr hbmMask,
+            ref int pi);
+
+            [PreserveSig]
+            int ReplaceIcon(
+            int i,
+            IntPtr hicon,
+            ref int pi);
+
+            [PreserveSig]
+            int SetOverlayImage(
+            int iImage,
+            int iOverlay);
+
+            [PreserveSig]
+            int Replace(
+            int i,
+            IntPtr hbmImage,
+            IntPtr hbmMask);
+
+            [PreserveSig]
+            int AddMasked(
+            IntPtr hbmImage,
+            int crMask,
+            ref int pi);
+
+            [PreserveSig]
+            int Draw(
+            ref IMAGELISTDRAWPARAMS pimldp);
+
+            [PreserveSig]
+            int Remove(int i);
+
+            [PreserveSig]
+            int GetIcon(
+            int i,
+            int flags,
+            ref IntPtr picon);
+
+            [PreserveSig]
+            int GetImageInfo(
+            int i,
+            ref IMAGEINFO pImageInfo);
+
+            [PreserveSig]
+            int Copy(
+            int iDst,
+            IImageList punkSrc,
+            int iSrc,
+            int uFlags);
+
+            [PreserveSig]
+            int Merge(
+            int i1,
+            IImageList punk2,
+            int i2,
+            int dx,
+            int dy,
+            ref Guid riid,
+            ref IntPtr ppv);
+
+            [PreserveSig]
+            int Clone(
+            ref Guid riid,
+            ref IntPtr ppv);
+
+            [PreserveSig]
+            int GetImageRect(
+            int i,
+            ref RECT prc);
+
+            [PreserveSig]
+            int GetIconSize(
+            ref int cx,
+            ref int cy);
+
+            [PreserveSig]
+            int SetIconSize(
+            int cx,
+            int cy);
+
+            [PreserveSig]
+            int GetImageCount(ref int pi);
+
+            [PreserveSig]
+            int SetImageCount(
+            int uNewCount);
+
+            [PreserveSig]
+            int SetBkColor(
+            int clrBk,
+            ref int pclr);
+
+            [PreserveSig]
+            int GetBkColor(
+            ref int pclr);
+
+            [PreserveSig]
+            int BeginDrag(
+            int iTrack,
+            int dxHotspot,
+            int dyHotspot);
+
+            [PreserveSig]
+            int EndDrag();
+
+            [PreserveSig]
+            int DragEnter(
+            IntPtr hwndLock,
+            int x,
+            int y);
+
+            [PreserveSig]
+            int DragLeave(
+            IntPtr hwndLock);
+
+            [PreserveSig]
+            int DragMove(
+            int x,
+            int y);
+
+            [PreserveSig]
+            int SetDragCursorImage(
+            ref IImageList punk,
+            int iDrag,
+            int dxHotspot,
+            int dyHotspot);
+
+            [PreserveSig]
+            int DragShowNolock(
+            int fShow);
+
+            [PreserveSig]
+            int GetDragImage(
+            ref POINT ppt,
+            ref POINT pptHotspot,
+            ref Guid riid,
+            ref IntPtr ppv);
+
+            [PreserveSig]
+            int GetItemFlags(
+            int i,
+            ref int dwFlags);
+
+            [PreserveSig]
+            int GetOverlayImage(
+            int iOverlay,
+            ref int piIndex);
+        };
+
+    }
+
+}

+ 864 - 0
Util/ListViewDragDropManager.cs

@@ -0,0 +1,864 @@
+// Copyright (C) Josh Smith - January 2007
+using System;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Documents;
+using System.Windows.Media;
+using System.Windows.Input;
+using WPF.JoshSmith.Adorners;
+using WPF.JoshSmith.Controls.Utilities;
+
+namespace WPF.JoshSmith.ServiceProviders.UI
+{
+	#region ListViewDragDropManager
+
+	/// <summary>
+	/// Manages the dragging and dropping of ListViewItems in a ListView.
+	/// The ItemType type parameter indicates the type of the objects in
+	/// the ListView's items source.  The ListView's ItemsSource must be 
+	/// set to an instance of ObservableCollection of ItemType, or an 
+	/// Exception will be thrown.
+	/// </summary>
+	/// <typeparam name="ItemType">The type of the ListView's items.</typeparam>
+	public class ListViewDragDropManager<ItemType> where ItemType : class
+	{
+		#region Data
+
+		bool canInitiateDrag;
+		DragAdorner dragAdorner;
+		double dragAdornerOpacity;
+		int indexToSelect;
+		bool isDragInProgress;
+		ItemType itemUnderDragCursor;
+		ListView listView;
+		Point ptMouseDown;
+		bool showDragAdorner;
+
+		#endregion // Data
+
+		#region Constructors
+
+		/// <summary>
+		/// Initializes a new instance of ListViewDragManager.
+		/// </summary>
+		public ListViewDragDropManager()
+		{
+			this.canInitiateDrag = false;
+			this.dragAdornerOpacity = 0.7;
+			this.indexToSelect = -1;
+			this.showDragAdorner = true;
+		}
+
+		/// <summary>
+		/// Initializes a new instance of ListViewDragManager.
+		/// </summary>
+		/// <param name="listView"></param>
+		public ListViewDragDropManager( ListView listView )
+			: this()
+		{
+			this.ListView = listView;
+		}
+
+		/// <summary>
+		/// Initializes a new instance of ListViewDragManager.
+		/// </summary>
+		/// <param name="listView"></param>
+		/// <param name="dragAdornerOpacity"></param>
+		public ListViewDragDropManager( ListView listView, double dragAdornerOpacity )
+			: this( listView )
+		{
+			this.DragAdornerOpacity = dragAdornerOpacity;
+		}
+
+		/// <summary>
+		/// Initializes a new instance of ListViewDragManager.
+		/// </summary>
+		/// <param name="listView"></param>
+		/// <param name="showDragAdorner"></param>
+		public ListViewDragDropManager( ListView listView, bool showDragAdorner )
+			: this( listView )
+		{
+			this.ShowDragAdorner = showDragAdorner;
+		}
+
+		#endregion // Constructors
+
+		#region Public Interface
+
+			#region DragAdornerOpacity
+
+		/// <summary>
+		/// Gets/sets the opacity of the drag adorner.  This property has no
+		/// effect if ShowDragAdorner is false. The default value is 0.7
+		/// </summary>
+		public double DragAdornerOpacity
+		{
+			get { return this.dragAdornerOpacity; }
+			set
+			{
+				if( this.IsDragInProgress )
+					throw new InvalidOperationException( "Cannot set the DragAdornerOpacity property during a drag operation." );
+
+				if( value < 0.0 || value > 1.0 )
+					throw new ArgumentOutOfRangeException( "DragAdornerOpacity", value, "Must be between 0 and 1." );
+
+				this.dragAdornerOpacity = value;
+			}
+		}
+
+			#endregion // DragAdornerOpacity
+
+			#region IsDragInProgress
+
+		/// <summary>
+		/// Returns true if there is currently a drag operation being managed.
+		/// </summary>
+		public bool IsDragInProgress
+		{
+			get { return this.isDragInProgress; }
+			private set { this.isDragInProgress = value; }
+		}
+
+			#endregion // IsDragInProgress
+
+			#region ListView
+
+		/// <summary>
+		/// Gets/sets the ListView whose dragging is managed.  This property
+		/// can be set to null, to prevent drag management from occuring.  If
+		/// the ListView's AllowDrop property is false, it will be set to true.
+		/// </summary>
+		public ListView ListView
+		{
+			get { return listView; }
+			set
+			{
+				if( this.IsDragInProgress )
+					throw new InvalidOperationException( "Cannot set the ListView property during a drag operation." );
+
+				if( this.listView != null )
+				{
+					#region Unhook Events
+
+					this.listView.PreviewMouseLeftButtonDown -= listView_PreviewMouseLeftButtonDown;
+					this.listView.PreviewMouseMove -= listView_PreviewMouseMove;
+					this.listView.DragOver -= listView_DragOver;
+					this.listView.DragLeave -= listView_DragLeave;
+					this.listView.DragEnter -= listView_DragEnter;
+					this.listView.Drop -= listView_Drop;
+
+					#endregion // Unhook Events
+				}
+
+				this.listView = value;
+
+				if( this.listView != null )
+				{
+					if( !this.listView.AllowDrop )
+						this.listView.AllowDrop = true;
+
+					#region Hook Events
+
+					this.listView.PreviewMouseLeftButtonDown += listView_PreviewMouseLeftButtonDown;
+					this.listView.PreviewMouseMove += listView_PreviewMouseMove;
+					this.listView.DragOver += listView_DragOver;
+					this.listView.DragLeave += listView_DragLeave;
+					this.listView.DragEnter += listView_DragEnter;
+					this.listView.Drop += listView_Drop;
+
+					#endregion // Hook Events
+				}
+			}
+		}
+
+			#endregion // ListView
+
+			#region ProcessDrop [event]
+
+		/// <summary>
+		/// Raised when a drop occurs.  By default the dropped item will be moved
+		/// to the target index.  Handle this event if relocating the dropped item
+		/// requires custom behavior.  Note, if this event is handled the default
+		/// item dropping logic will not occur.
+		/// </summary>
+		public event EventHandler<ProcessDropEventArgs<ItemType>> ProcessDrop;
+
+			#endregion // ProcessDrop [event]
+
+			#region ShowDragAdorner
+
+		/// <summary>
+		/// Gets/sets whether a visual representation of the ListViewItem being dragged
+		/// follows the mouse cursor during a drag operation.  The default value is true.
+		/// </summary>
+		public bool ShowDragAdorner
+		{
+			get { return this.showDragAdorner; }
+			set
+			{
+				if( this.IsDragInProgress )
+					throw new InvalidOperationException( "Cannot set the ShowDragAdorner property during a drag operation." );
+
+				this.showDragAdorner = value;
+			}
+		}
+
+			#endregion // ShowDragAdorner
+
+		#endregion // Public Interface
+
+		#region Event Handling Methods
+
+			#region listView_PreviewMouseLeftButtonDown
+
+		void listView_PreviewMouseLeftButtonDown( object sender, MouseButtonEventArgs e )
+		{
+			if( this.IsMouseOverScrollbar )
+			{
+				// 4/13/2007 - Set the flag to false when cursor is over scrollbar.
+				this.canInitiateDrag = false;
+				return;
+			}
+
+			int index = this.IndexUnderDragCursor;
+			this.canInitiateDrag = index > -1;
+
+			if( this.canInitiateDrag )
+			{
+				// Remember the location and index of the ListViewItem the user clicked on for later.
+				this.ptMouseDown = MouseUtilities.GetMousePosition( this.listView );
+				this.indexToSelect = index;
+			}
+			else
+			{
+				this.ptMouseDown = new Point( -10000, -10000 );
+				this.indexToSelect = -1;
+			}
+		}
+
+			#endregion // listView_PreviewMouseLeftButtonDown
+
+			#region listView_PreviewMouseMove
+
+		void listView_PreviewMouseMove( object sender, MouseEventArgs e )
+		{
+			if( !this.CanStartDragOperation )
+				return;
+
+			// Select the item the user clicked on.
+			if( this.listView.SelectedIndex != this.indexToSelect )
+				this.listView.SelectedIndex = this.indexToSelect;
+
+			// If the item at the selected index is null, there's nothing
+			// we can do, so just return;
+			if( this.listView.SelectedItem == null )
+				return;
+
+			ListViewItem itemToDrag = this.GetListViewItem( this.listView.SelectedIndex );
+			if( itemToDrag == null )
+				return;
+
+			AdornerLayer adornerLayer = this.ShowDragAdornerResolved ? this.InitializeAdornerLayer( itemToDrag ) : null;
+
+			this.InitializeDragOperation( itemToDrag );
+			this.PerformDragOperation();
+			this.FinishDragOperation( itemToDrag, adornerLayer );
+		}
+
+			#endregion // listView_PreviewMouseMove
+
+			#region listView_DragOver
+
+		void listView_DragOver( object sender, DragEventArgs e )
+		{
+			e.Effects = DragDropEffects.Move;
+
+			if( this.ShowDragAdornerResolved )
+				this.UpdateDragAdornerLocation();
+
+			// Update the item which is known to be currently under the drag cursor.
+			int index = this.IndexUnderDragCursor;
+			this.ItemUnderDragCursor = index < 0 ? null : this.ListView.Items[index] as ItemType;
+		}
+
+			#endregion // listView_DragOver
+
+			#region listView_DragLeave
+
+		void listView_DragLeave( object sender, DragEventArgs e )
+		{
+			if( !this.IsMouseOver( this.listView ) )
+			{
+				if( this.ItemUnderDragCursor != null )
+					this.ItemUnderDragCursor = null;
+
+				if( this.dragAdorner != null )
+					this.dragAdorner.Visibility = Visibility.Collapsed;
+			}
+		}
+
+			#endregion // listView_DragLeave
+
+			#region listView_DragEnter
+
+		void listView_DragEnter( object sender, DragEventArgs e )
+		{
+			if( this.dragAdorner != null && this.dragAdorner.Visibility != Visibility.Visible )
+			{
+				// Update the location of the adorner and then show it.				
+				this.UpdateDragAdornerLocation();
+				this.dragAdorner.Visibility = Visibility.Visible;
+			}
+		}
+	
+			#endregion // listView_DragEnter
+
+			#region listView_Drop
+
+		void listView_Drop( object sender, DragEventArgs e )
+		{
+			if( this.ItemUnderDragCursor != null )
+				this.ItemUnderDragCursor = null;
+
+			e.Effects = DragDropEffects.None;
+
+			if( !e.Data.GetDataPresent( typeof( ItemType ) ) )
+				return;
+
+			// Get the data object which was dropped.
+			ItemType data = e.Data.GetData( typeof( ItemType ) ) as ItemType;
+			if( data == null )
+				return;
+
+			// Get the ObservableCollection<ItemType> which contains the dropped data object.
+			ObservableCollection<ItemType> itemsSource = this.listView.ItemsSource as ObservableCollection<ItemType>;
+			if( itemsSource == null )
+				throw new Exception(
+					"A ListView managed by ListViewDragManager must have its ItemsSource set to an ObservableCollection<ItemType>." );
+
+			int oldIndex = itemsSource.IndexOf( data );
+			int newIndex = this.IndexUnderDragCursor;
+
+			if( newIndex < 0 )
+			{
+				// The drag started somewhere else, and our ListView is empty
+				// so make the new item the first in the list.
+				if( itemsSource.Count == 0 )
+					newIndex = 0;
+
+				// The drag started somewhere else, but our ListView has items
+				// so make the new item the last in the list.
+				else if( oldIndex < 0 )
+					newIndex = itemsSource.Count;
+
+				// The user is trying to drop an item from our ListView into
+				// our ListView, but the mouse is not over an item, so don't
+				// let them drop it.
+				else
+					return;
+			}
+
+			// Dropping an item back onto itself is not considered an actual 'drop'.
+			if( oldIndex == newIndex )
+				return;
+
+			if( this.ProcessDrop != null )
+			{
+				// Let the client code process the drop.
+				ProcessDropEventArgs<ItemType> args = new ProcessDropEventArgs<ItemType>( itemsSource, data, oldIndex, newIndex, e.AllowedEffects );
+				this.ProcessDrop( this, args );
+				e.Effects = args.Effects;
+			}
+			else
+			{
+				// Move the dragged data object from it's original index to the
+				// new index (according to where the mouse cursor is).  If it was
+				// not previously in the ListBox, then insert the item.
+				if( oldIndex > -1 )
+					itemsSource.Move( oldIndex, newIndex );
+				else
+					itemsSource.Insert( newIndex, data );
+
+				// Set the Effects property so that the call to DoDragDrop will return 'Move'.
+				e.Effects = DragDropEffects.Move;
+			}
+		}
+
+			#endregion // listView_Drop
+
+		#endregion // Event Handling Methods
+
+		#region Private Helpers
+
+			#region CanStartDragOperation
+
+		bool CanStartDragOperation
+		{
+			get
+			{
+				if( Mouse.LeftButton != MouseButtonState.Pressed )
+					return false;
+
+				if( !this.canInitiateDrag )
+					return false;
+
+				if( this.indexToSelect == -1 )
+					return false;
+
+				if( !this.HasCursorLeftDragThreshold )
+					return false;
+
+				return true;
+			}
+		}
+
+			#endregion // CanStartDragOperation
+
+			#region FinishDragOperation
+
+		void FinishDragOperation( ListViewItem draggedItem, AdornerLayer adornerLayer )
+		{
+			// Let the ListViewItem know that it is not being dragged anymore.
+			ListViewItemDragState.SetIsBeingDragged( draggedItem, false );
+
+			this.IsDragInProgress = false;
+
+			if( this.ItemUnderDragCursor != null )
+				this.ItemUnderDragCursor = null;
+
+			// Remove the drag adorner from the adorner layer.
+			if( adornerLayer != null )
+			{
+				adornerLayer.Remove( this.dragAdorner );
+				this.dragAdorner = null;
+			}
+		}
+
+			#endregion // FinishDragOperation
+
+			#region GetListViewItem
+
+		ListViewItem GetListViewItem( int index )
+		{
+			if( this.listView.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated )
+				return null;
+
+			return this.listView.ItemContainerGenerator.ContainerFromIndex( index ) as ListViewItem;
+		}
+
+		ListViewItem GetListViewItem( ItemType dataItem )
+		{
+			if( this.listView.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated )
+				return null;
+
+			return this.listView.ItemContainerGenerator.ContainerFromItem( dataItem ) as ListViewItem;
+		}
+
+			#endregion // GetListViewItem
+
+			#region HasCursorLeftDragThreshold
+
+		bool HasCursorLeftDragThreshold
+		{
+			get
+			{
+				if( this.indexToSelect < 0 )
+					return false;
+
+				ListViewItem item = this.GetListViewItem( this.indexToSelect );
+				Rect bounds = VisualTreeHelper.GetDescendantBounds( item );
+				Point ptInItem = this.listView.TranslatePoint( this.ptMouseDown, item );
+
+				// In case the cursor is at the very top or bottom of the ListViewItem
+				// we want to make the vertical threshold very small so that dragging
+				// over an adjacent item does not select it.
+				double topOffset = Math.Abs( ptInItem.Y );
+				double btmOffset = Math.Abs( bounds.Height - ptInItem.Y );
+				double vertOffset = Math.Min( topOffset, btmOffset );
+
+				double width = SystemParameters.MinimumHorizontalDragDistance * 2;
+				double height = Math.Min( SystemParameters.MinimumVerticalDragDistance, vertOffset ) * 2;
+				Size szThreshold = new Size( width, height );
+
+				Rect rect = new Rect( this.ptMouseDown, szThreshold );
+				rect.Offset( szThreshold.Width / -2, szThreshold.Height / -2 );
+				Point ptInListView = MouseUtilities.GetMousePosition( this.listView );
+				return !rect.Contains( ptInListView );
+			}
+		}
+
+			#endregion // HasCursorLeftDragThreshold
+
+			#region IndexUnderDragCursor
+
+		/// <summary>
+		/// Returns the index of the ListViewItem underneath the
+		/// drag cursor, or -1 if the cursor is not over an item.
+		/// </summary>
+		int IndexUnderDragCursor
+		{
+			get
+			{
+				int index = -1;
+				for( int i = 0; i < this.listView.Items.Count; ++i )
+				{
+					ListViewItem item = this.GetListViewItem( i );
+					if( this.IsMouseOver( item ) )
+					{
+						index = i;
+						break;
+					}
+				}
+				return index;
+			}
+		}
+
+			#endregion // IndexUnderDragCursor
+
+			#region InitializeAdornerLayer
+
+		AdornerLayer InitializeAdornerLayer( ListViewItem itemToDrag )
+		{
+			// Create a brush which will paint the ListViewItem onto
+			// a visual in the adorner layer.
+			VisualBrush brush = new VisualBrush( itemToDrag );
+
+			// Create an element which displays the source item while it is dragged.
+			this.dragAdorner = new DragAdorner( this.listView, itemToDrag.RenderSize, brush );
+			
+			// Set the drag adorner's opacity.		
+			this.dragAdorner.Opacity = this.DragAdornerOpacity;
+
+			AdornerLayer layer = AdornerLayer.GetAdornerLayer( this.listView );
+			layer.Add( dragAdorner );
+
+			// Save the location of the cursor when the left mouse button was pressed.
+			this.ptMouseDown = MouseUtilities.GetMousePosition( this.listView );
+
+			return layer;
+		}
+
+			#endregion // InitializeAdornerLayer
+
+			#region InitializeDragOperation
+
+		void InitializeDragOperation( ListViewItem itemToDrag )
+		{
+			// Set some flags used during the drag operation.
+			this.IsDragInProgress = true;
+			this.canInitiateDrag = false;
+
+			// Let the ListViewItem know that it is being dragged.
+			ListViewItemDragState.SetIsBeingDragged( itemToDrag, true );
+		}
+
+			#endregion // InitializeDragOperation
+
+			#region IsMouseOver
+
+		bool IsMouseOver( Visual target )
+		{
+			// We need to use MouseUtilities to figure out the cursor
+			// coordinates because, during a drag-drop operation, the WPF
+			// mechanisms for getting the coordinates behave strangely.
+
+			Rect bounds = VisualTreeHelper.GetDescendantBounds( target );
+			Point mousePos = MouseUtilities.GetMousePosition( target );
+			return bounds.Contains( mousePos );
+		}
+
+			#endregion // IsMouseOver
+
+			#region IsMouseOverScrollbar
+
+		/// <summary>
+		/// Returns true if the mouse cursor is over a scrollbar in the ListView.
+		/// </summary>
+		bool IsMouseOverScrollbar
+		{
+			get
+			{
+				Point ptMouse = MouseUtilities.GetMousePosition( this.listView );
+				HitTestResult res = VisualTreeHelper.HitTest( this.listView, ptMouse );
+				if( res == null )
+					return false;
+
+				DependencyObject depObj = res.VisualHit;
+				while( depObj != null )
+				{
+					if( depObj is ScrollBar )
+						return true;
+
+					// VisualTreeHelper works with objects of type Visual or Visual3D.
+					// If the current object is not derived from Visual or Visual3D,
+					// then use the LogicalTreeHelper to find the parent element.
+					if( depObj is Visual || depObj is System.Windows.Media.Media3D.Visual3D )
+						depObj = VisualTreeHelper.GetParent( depObj );
+					else
+						depObj = LogicalTreeHelper.GetParent( depObj );
+				}
+
+				return false;
+			}
+		}
+
+			#endregion // IsMouseOverScrollbar
+
+			#region ItemUnderDragCursor
+
+		ItemType ItemUnderDragCursor
+		{
+			get { return this.itemUnderDragCursor; }
+			set
+			{
+				if( this.itemUnderDragCursor == value )
+					return;
+
+				// The first pass handles the previous item under the cursor.
+				// The second pass handles the new one.
+				for( int i = 0; i < 2; ++i )
+				{
+					if( i == 1 )
+						this.itemUnderDragCursor = value;
+
+					if( this.itemUnderDragCursor != null )
+					{
+						ListViewItem listViewItem = this.GetListViewItem( this.itemUnderDragCursor );
+						if( listViewItem != null )
+							ListViewItemDragState.SetIsUnderDragCursor( listViewItem, i == 1 );
+					}
+				}
+			}
+		}
+
+			#endregion // ItemUnderDragCursor
+
+			#region PerformDragOperation
+
+		void PerformDragOperation()
+		{
+			ItemType selectedItem = this.listView.SelectedItem as ItemType;
+			DragDropEffects allowedEffects = DragDropEffects.Move | DragDropEffects.Move | DragDropEffects.Link;
+			if( DragDrop.DoDragDrop( this.listView, selectedItem, allowedEffects ) != DragDropEffects.None )
+			{
+				// The item was dropped into a new location,
+				// so make it the new selected item.
+				this.listView.SelectedItem = selectedItem;
+			}
+		}
+
+			#endregion // PerformDragOperation
+
+			#region ShowDragAdornerResolved
+
+		bool ShowDragAdornerResolved
+		{
+			get { return this.ShowDragAdorner && this.DragAdornerOpacity > 0.0; }
+		}
+
+			#endregion // ShowDragAdornerResolved
+
+			#region UpdateDragAdornerLocation
+
+		void UpdateDragAdornerLocation()
+		{
+			if( this.dragAdorner != null )
+			{
+				Point ptCursor = MouseUtilities.GetMousePosition( this.ListView );
+
+				double left = ptCursor.X - this.ptMouseDown.X;
+
+				// 4/13/2007 - Made the top offset relative to the item being dragged.
+				ListViewItem itemBeingDragged = this.GetListViewItem( this.indexToSelect );
+				Point itemLoc = itemBeingDragged.TranslatePoint( new Point( 0, 0 ), this.ListView );
+				double top = itemLoc.Y + ptCursor.Y - this.ptMouseDown.Y;
+
+				this.dragAdorner.SetOffsets( left, top );
+			}
+		}
+
+			#endregion // UpdateDragAdornerLocation
+
+		#endregion // Private Helpers
+	}
+
+	#endregion // ListViewDragDropManager
+
+	#region ListViewItemDragState
+
+	/// <summary>
+	/// Exposes attached properties used in conjunction with the ListViewDragDropManager class.
+	/// Those properties can be used to allow triggers to modify the appearance of ListViewItems
+	/// in a ListView during a drag-drop operation.
+	/// </summary>
+	public static class ListViewItemDragState
+	{
+		#region IsBeingDragged
+
+		/// <summary>
+		/// Identifies the ListViewItemDragState's IsBeingDragged attached property.  
+		/// This field is read-only.
+		/// </summary>
+		public static readonly DependencyProperty IsBeingDraggedProperty =
+			DependencyProperty.RegisterAttached(
+				"IsBeingDragged",
+				typeof( bool ),
+				typeof( ListViewItemDragState ),
+				new UIPropertyMetadata( false ) );
+
+		/// <summary>
+		/// Returns true if the specified ListViewItem is being dragged, else false.
+		/// </summary>
+		/// <param name="item">The ListViewItem to check.</param>
+		public static bool GetIsBeingDragged( ListViewItem item )
+		{
+			return (bool)item.GetValue( IsBeingDraggedProperty );
+		}
+
+		/// <summary>
+		/// Sets the IsBeingDragged attached property for the specified ListViewItem.
+		/// </summary>
+		/// <param name="item">The ListViewItem to set the property on.</param>
+		/// <param name="value">Pass true if the element is being dragged, else false.</param>
+		internal static void SetIsBeingDragged( ListViewItem item, bool value )
+		{
+			item.SetValue( IsBeingDraggedProperty, value );
+		}
+
+		#endregion // IsBeingDragged
+
+		#region IsUnderDragCursor
+
+		/// <summary>
+		/// Identifies the ListViewItemDragState's IsUnderDragCursor attached property.  
+		/// This field is read-only.
+		/// </summary>
+		public static readonly DependencyProperty IsUnderDragCursorProperty =
+			DependencyProperty.RegisterAttached(
+				"IsUnderDragCursor",
+				typeof( bool ),
+				typeof( ListViewItemDragState ),
+				new UIPropertyMetadata( false ) );
+
+		/// <summary>
+		/// Returns true if the specified ListViewItem is currently underneath the cursor 
+		/// during a drag-drop operation, else false.
+		/// </summary>
+		/// <param name="item">The ListViewItem to check.</param>
+		public static bool GetIsUnderDragCursor( ListViewItem item )
+		{
+			return (bool)item.GetValue( IsUnderDragCursorProperty );
+		}
+
+		/// <summary>
+		/// Sets the IsUnderDragCursor attached property for the specified ListViewItem.
+		/// </summary>
+		/// <param name="item">The ListViewItem to set the property on.</param>
+		/// <param name="value">Pass true if the element is underneath the drag cursor, else false.</param>
+		internal static void SetIsUnderDragCursor( ListViewItem item, bool value )
+		{
+			item.SetValue( IsUnderDragCursorProperty, value );
+		}
+
+		#endregion // IsUnderDragCursor
+	}
+
+	#endregion // ListViewItemDragState
+
+	#region ProcessDropEventArgs
+
+	/// <summary>
+	/// Event arguments used by the ListViewDragDropManager.ProcessDrop event.
+	/// </summary>
+	/// <typeparam name="ItemType">The type of data object being dropped.</typeparam>
+	public class ProcessDropEventArgs<ItemType> : EventArgs where ItemType : class
+	{
+		#region Data
+
+		ObservableCollection<ItemType> itemsSource;
+		ItemType dataItem;
+		int oldIndex;
+		int newIndex;
+		DragDropEffects allowedEffects = DragDropEffects.None;
+		DragDropEffects effects = DragDropEffects.None;
+
+		#endregion // Data
+
+		#region Constructor
+
+		internal ProcessDropEventArgs( 
+			ObservableCollection<ItemType> itemsSource, 
+			ItemType dataItem, 
+			int oldIndex, 
+			int newIndex, 
+			DragDropEffects allowedEffects )
+		{
+			this.itemsSource = itemsSource;
+			this.dataItem = dataItem;
+			this.oldIndex = oldIndex;
+			this.newIndex = newIndex;
+			this.allowedEffects = allowedEffects;
+		}
+
+		#endregion // Constructor
+
+		#region Public Properties
+
+		/// <summary>
+		/// The items source of the ListView where the drop occurred.
+		/// </summary>
+		public ObservableCollection<ItemType> ItemsSource
+		{
+			get { return this.itemsSource; }
+		}
+
+		/// <summary>
+		/// The data object which was dropped.
+		/// </summary>
+		public ItemType DataItem
+		{
+			get { return this.dataItem; }
+		}
+
+		/// <summary>
+		/// The current index of the data item being dropped, in the ItemsSource collection.
+		/// </summary>
+		public int OldIndex
+		{
+			get { return this.oldIndex; }
+		}
+
+		/// <summary>
+		/// The target index of the data item being dropped, in the ItemsSource collection.
+		/// </summary>
+		public int NewIndex
+		{
+			get { return this.newIndex; }
+		}
+
+		/// <summary>
+		/// The drag drop effects allowed to be performed.
+		/// </summary>
+		public DragDropEffects AllowedEffects
+		{
+			get { return allowedEffects; }
+		}
+
+		/// <summary>
+		/// The drag drop effect(s) performed on the dropped item.
+		/// </summary>
+		public DragDropEffects Effects
+		{
+			get { return effects; }
+			set { effects = value; }
+		}
+
+		#endregion // Public Properties
+	}
+
+	#endregion // ProcessDropEventArgs
+}

+ 29 - 0
Util/MenuWidthConvert.cs

@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+
+namespace GeekDesk.Util
+{
+    class MenuWidthConvert : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if (value != null && value.ToString().Length>0)
+            {
+                return System.Convert.ToDouble(value.ToString()) - 10d;
+            } else
+            {
+                return 0d;
+            }
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 58 - 0
Util/MouseUtilities.cs

@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Windows;
+using System.Windows.Media;
+
+namespace WPF.JoshSmith.Controls.Utilities
+{
+	/// <summary>
+	/// Provides access to the mouse location by calling unmanaged code.
+	/// </summary>
+	/// <remarks>
+	/// This class was written by Dan Crevier (Microsoft).  
+	/// http://blogs.msdn.com/llobo/archive/2006/09/06/Scrolling-Scrollviewer-on-Mouse-Drag-at-the-boundaries.aspx
+	/// </remarks>
+	public class MouseUtilities
+	{
+		[StructLayout( LayoutKind.Sequential )]
+		private struct Win32Point
+		{
+			public Int32 X;
+			public Int32 Y;
+		};
+
+		[DllImport( "user32.dll" )]
+		private static extern bool GetCursorPos( ref Win32Point pt );
+
+		[DllImport( "user32.dll" )]
+		private static extern bool ScreenToClient( IntPtr hwnd, ref Win32Point pt );
+
+		/// <summary>
+		/// Returns the mouse cursor location.  This method is necessary during 
+		/// a drag-drop operation because the WPF mechanisms for retrieving the
+		/// cursor coordinates are unreliable.
+		/// </summary>
+		/// <param name="relativeTo">The Visual to which the mouse coordinates will be relative.</param>
+		public static Point GetMousePosition( Visual relativeTo )
+		{
+			Win32Point mouse = new Win32Point();
+			GetCursorPos( ref mouse );
+
+			// Using PointFromScreen instead of Dan Crevier's code (commented out below)
+			// is a bug fix created by William J. Roberts.  Read his comments about the fix
+			// here: http://www.codeproject.com/useritems/ListViewDragDropManager.asp?msg=1911611#xx1911611xx
+			return relativeTo.PointFromScreen( new Point( (double)mouse.X, (double)mouse.Y ) );
+
+			#region Commented Out
+			//System.Windows.Interop.HwndSource presentationSource =
+			//    (System.Windows.Interop.HwndSource)PresentationSource.FromVisual( relativeTo );
+			//ScreenToClient( presentationSource.Handle, ref mouse );
+			//GeneralTransform transform = relativeTo.TransformToAncestor( presentationSource.RootVisual );
+			//Point offset = transform.Transform( new Point( 0, 0 ) );
+			//return new Point( mouse.X - offset.X, mouse.Y - offset.Y );
+			#endregion // Commented Out
+		}
+	}
+}

+ 152 - 0
Util/SystemIcon.cs

@@ -0,0 +1,152 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Drawing;
+using Microsoft.Win32;
+using System.Runtime.InteropServices;
+using System.Drawing.Imaging;
+using GeekDesk.Util;
+
+namespace GeekDesk.Util
+{
+    class SystemIcon
+    {
+        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+        public struct SHFILEINFO
+        {
+            public IntPtr hIcon;
+            public int iIcon;
+            public uint dwAttributes;
+            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
+            public string szDisplayName;
+            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
+            public string szTypeName;
+        }
+        [DllImport("Shell32.dll", EntryPoint = "SHGetFileInfo", SetLastError = true, CharSet = CharSet.Auto)]
+        public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint uFlags);
+        [DllImport("User32.dll", EntryPoint = "DestroyIcon")]
+        public static extern int DestroyIcon(IntPtr hIcon);
+        #region API 参数的常量定义
+        public enum FileInfoFlags : uint
+        {
+            SHGFI_ICON = 0x000000100, // get icon
+            SHGFI_DISPLAYNAME = 0x000000200, // get display name
+            SHGFI_TYPENAME = 0x000000400, // get type name
+            SHGFI_ATTRIBUTES = 0x000000800, // get attributes
+            SHGFI_ICONLOCATION = 0x000001000, // get icon location
+            SHGFI_EXETYPE = 0x000002000, // return exe type
+            SHGFI_SYSICONINDEX = 0x000004000, // get system icon index
+            SHGFI_LINKOVERLAY = 0x000008000, // put a link overlay on icon
+            SHGFI_SELECTED = 0x000010000, // show icon in selected state
+            SHGFI_ATTR_SPECIFIED = 0x000020000, // get only specified attributes
+            SHGFI_LARGEICON = 0x000000000, // get large icon
+            SHGFI_SMALLICON = 0x000000001, // get small icon
+            SHGFI_OPENICON = 0x000000002, // get open icon
+            SHGFI_SHELLICONSIZE = 0x000000004, // get shell size icon
+            SHGFI_PIDL = 0x000000008, // pszPath is a pidl
+            SHGFI_USEFILEATTRIBUTES = 0x000000010, // use passed dwFileAttribute
+            SHGFI_ADDOVERLAYS = 0x000000020, // apply the appropriate overlays
+            SHGFI_OVERLAYINDEX = 0x000000040 // Get the index of the overlay
+        }
+        public enum FileAttributeFlags : uint
+        {
+            FILE_ATTRIBUTE_READONLY = 0x00000001,
+            FILE_ATTRIBUTE_HIDDEN = 0x00000002,
+            FILE_ATTRIBUTE_SYSTEM = 0x00000004,
+            FILE_ATTRIBUTE_DIRECTORY = 0x00000010,
+            FILE_ATTRIBUTE_ARCHIVE = 0x00000020,
+            FILE_ATTRIBUTE_DEVICE = 0x00000040,
+            FILE_ATTRIBUTE_NORMAL = 0x00000080,
+            FILE_ATTRIBUTE_TEMPORARY = 0x00000100,
+            FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200,
+            FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400,
+            FILE_ATTRIBUTE_COMPRESSED = 0x00000800,
+            FILE_ATTRIBUTE_OFFLINE = 0x00001000,
+            FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000,
+            FILE_ATTRIBUTE_ENCRYPTED = 0x00004000
+        }
+        #endregion
+        /// <summary>
+        /// 获取文件类型的关联图标
+        /// </summary>
+        /// <param name="fileName">文件类型的扩展名或文件的绝对路径</param>
+        /// <param name="isLargeIcon">是否返回大图标</param>
+        /// <returns>获取到的图标</returns>
+        public static Icon GetIcon(string fileName, bool isLargeIcon)
+        {
+            //SHFILEINFO shfi = new SHFILEINFO();
+            //IntPtr hI;
+            //if (isLargeIcon)
+            //    hI = SHGetFileInfo(fileName, 0, ref shfi, (uint)Marshal.SizeOf(shfi), (uint)FileInfoFlags.SHGFI_ICON | (uint)FileInfoFlags.SHGFI_USEFILEATTRIBUTES | (uint)FileInfoFlags.SHGFI_LARGEICON);
+            //else
+            //    hI = SHGetFileInfo(fileName, 0, ref shfi, (uint)Marshal.SizeOf(shfi), (uint)FileInfoFlags.SHGFI_ICON | (uint)FileInfoFlags.SHGFI_USEFILEATTRIBUTES | (uint)FileInfoFlags.SHGFI_SMALLICON);
+            //Icon icon = Icon.FromHandle(shfi.hIcon).Clone() as Icon;
+            //DestroyIcon(shfi.hIcon); //释放资源
+
+            //选中文件中的图标总数
+            //var iconTotalCount = PrivateExtractIcons(fileName, 0, 0, 0, null, null, 1, 0);
+            //用于接收获取到的图标指针
+            //IntPtr[] hIcons = new IntPtr[1];
+            ////对应的图标id
+            //int[] ids = new int[1];
+            ////成功获取到的图标个数
+            //int successCount = PrivateExtractIcons(fileName, 0, 0, 0, hIcons, ids, 1, 0);
+            //Icon ico = Icon.FromHandle(hIcons[0]);
+            //var myIcon = ico.ToBitmap();
+            //myIcon.Save("D:\\" + ids[0].ToString("000") + ".png", ImageFormat.Png);
+            IntPtr hIcon = FileIcon.GetJumboIcon(FileIcon.GetIconIndex(fileName));
+            //IntPtr hIcon = GetJumboIcon(GetIconIndex("*." + ext));
+
+            // from native to managed
+            Icon ico = Icon.FromHandle(hIcon);
+            string path = "D:\\test\\" + System.Guid.NewGuid().ToString() + ".png";
+            //using ( ico = (Icon)Icon.FromHandle(hIcon).Clone())
+            //{
+            //    // save to file (or show in a picture box)
+            //    ico.ToBitmap().Save(path, ImageFormat.Png);
+            //}
+            //FileIcon.Shell32.DestroyIcon(hIcon); // don't forget to cleanup
+
+            return ico;
+        }
+        /// <summary> 
+        /// 获取文件夹图标
+        /// </summary> 
+        /// <returns>图标</returns> 
+        public static Icon GetDirectoryIcon(bool isLargeIcon)
+        {
+            SHFILEINFO _SHFILEINFO = new SHFILEINFO();
+            IntPtr _IconIntPtr;
+            if (isLargeIcon)
+            {
+                _IconIntPtr = SHGetFileInfo(@"", 0, ref _SHFILEINFO, (uint)Marshal.SizeOf(_SHFILEINFO), ((uint)FileInfoFlags.SHGFI_ICON | (uint)FileInfoFlags.SHGFI_LARGEICON));
+            }
+            else
+            {
+                _IconIntPtr = SHGetFileInfo(@"", 0, ref _SHFILEINFO, (uint)Marshal.SizeOf(_SHFILEINFO), ((uint)FileInfoFlags.SHGFI_ICON | (uint)FileInfoFlags.SHGFI_SMALLICON));
+            }
+            if (_IconIntPtr.Equals(IntPtr.Zero)) return null;
+            Icon _Icon = System.Drawing.Icon.FromHandle(_SHFILEINFO.hIcon);
+            return _Icon;
+        }
+
+        [DllImport("User32.dll")]
+        public static extern int PrivateExtractIcons(
+             string lpszFile, //file name
+             int nIconIndex,  //The zero-based index of the first icon to extract.
+             int cxIcon,      //The horizontal icon size wanted.
+             int cyIcon,      //The vertical icon size wanted.
+             IntPtr[] phicon, //(out) A pointer to the returned array of icon handles.
+             int[] piconid,   //(out) A pointer to a returned resource identifier.
+             int nIcons,      //The number of icons to extract from the file. Only valid when *.exe and *.dll
+             int flags        //Specifies flags that control this function.
+         );
+
+        //[DllImport("User32.dll")]
+        //public static extern bool DestroyIcon(
+        //     IntPtr hIcon //A handle to the icon to be destroyed. The icon must not be in use.
+        // );
+    }
+}

+ 88 - 0
ViewModel/AppConfig.cs

@@ -0,0 +1,88 @@
+using GalaSoft.MvvmLight;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using GeekDesk.Constant;
+
+namespace GeekDesk.ViewModel
+{
+    [Serializable]
+    public class AppConfig  :  ViewModelBase
+    {
+        private int menuSortType = (int)SortType.CUSTOM; //菜单排序类型
+        private int iconSortType = (int)SortType.CUSTOM; //图表排序类型
+        private double windowWidth = (double)DefaultConstant.WINDOW_WIDTH;  //窗口宽度
+        private double windowHeight = (double)DefaultConstant.WINDOW_HEIGHT; //窗口高度
+        private double menuCardWidth = (double)DefaultConstant.MENU_CARD_WIDHT;//菜单栏宽度
+
+
+        #region GetSet
+        public int MenuSortType {
+            get
+            {
+                return menuSortType;
+            }
+            set
+            {
+                menuSortType = value;
+                RaisePropertyChanged();
+            }
+        }
+
+        public int IconSortType
+        {
+            get
+            {
+                return iconSortType;
+            }
+            set
+            {
+                iconSortType = value;
+                RaisePropertyChanged();
+            }
+        }
+
+        public double WindowWidth
+        {
+            get
+            {
+                return windowWidth;
+            }
+            set
+            {
+                windowWidth = value;
+                RaisePropertyChanged();
+            }
+        }
+
+        public double WindowHeight
+        {
+            get
+            {
+                return windowHeight;
+            }
+            set
+            {
+                windowHeight = value;
+                RaisePropertyChanged();
+            }
+        }
+
+        public double MenuCardWidth
+        {
+            get
+            {
+                return menuCardWidth;
+            }
+            set
+            {
+                menuCardWidth = value;
+                RaisePropertyChanged();
+            }
+        }
+        #endregion
+
+    }
+}

+ 70 - 0
ViewModel/DataInfos.cs

@@ -0,0 +1,70 @@
+using GalaSoft.MvvmLight;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media.Imaging;
+
+namespace GeekDesk.ViewModel
+{
+    public class DataInfos : ViewModelBase
+    {
+        private string path; //路径
+        private string name; //文件名
+        private int count = 0; //打开次数
+        private BitmapImage bitmapImage; //位图
+
+        public int Count
+        {
+            get
+            {
+                return count;
+            }
+            set
+            {
+                count = value;
+                RaisePropertyChanged();
+            }
+        }
+
+        public string Name
+        {
+            get
+            {
+                return name;
+            }
+            set
+            {
+                name = value;
+                RaisePropertyChanged();
+            }
+        }
+
+        public string Path
+        {
+            get
+            {
+                return path;
+            }
+            set
+            {
+                path = value;
+                RaisePropertyChanged();
+            }
+        }
+
+        public BitmapImage BitmapImage
+        {
+            get
+            {
+                return bitmapImage;
+            }
+            set
+            {
+                bitmapImage = value;
+                RaisePropertyChanged();
+            }
+        }
+    }
+}

+ 12 - 0
ViewModel/MainModel.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GeekDesk.ViewModel
+{
+    class MainModel
+    {
+    }
+}

+ 34 - 0
ViewModel/MainViewModel.cs

@@ -0,0 +1,34 @@
+using GalaSoft.MvvmLight;
+
+namespace GeekDesk.ViewModel
+{
+    /// <summary>
+    /// This class contains properties that the main View can data bind to.
+    /// <para>
+    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
+    /// </para>
+    /// <para>
+    /// You can also use Blend to data bind with the tool's support.
+    /// </para>
+    /// <para>
+    /// See http://www.galasoft.ch/mvvm
+    /// </para>
+    /// </summary>
+    public class MainViewModel : ViewModelBase
+    {
+        /// <summary>
+        /// Initializes a new instance of the MainViewModel class.
+        /// </summary>
+        public MainViewModel()
+        {
+            ////if (IsInDesignMode)
+            ////{
+            ////    // Code runs in Blend --> create design time data.
+            ////}
+            ////else
+            ////{
+            ////    // Code runs "for real"
+            ////}
+        }
+    }
+}

+ 36 - 0
ViewModel/MenuViewModel.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GeekDesk.ViewModel
+{
+    class MenuViewModel
+    {
+
+        public MenuViewModel()
+        {
+            
+        }
+
+        public ObservableCollection<Menu> GetMenus()
+        {
+            ObservableCollection<Menu> menus = new ObservableCollection<Menu>();
+            menus.Add(new Menu() { menu = "test1" });
+            menus.Add(new Menu() { menu = "test2" });
+            menus.Add(new Menu() { menu = "test3" });
+            return menus;
+        }
+        
+
+    }
+
+
+
+    public class Menu
+    {
+        public string menu { get; set; }
+    }
+}

+ 9 - 0
packages.config

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="CommonServiceLocator" version="2.0.6" targetFramework="net452" requireReinstallation="true" />
+  <package id="HandyControl" version="3.1.0" targetFramework="net452" requireReinstallation="true" />
+  <package id="MvvmLight" version="5.4.1.1" targetFramework="net452" />
+  <package id="MvvmLightLibs" version="5.4.1.1" targetFramework="net452" />
+  <package id="Newtonsoft.Json" version="13.0.1" targetFramework="net452" />
+  <package id="System.Drawing.Common" version="5.0.2" targetFramework="net472" />
+</packages>