diff --git a/App.config b/App.config new file mode 100644 index 0000000..adfec6e --- /dev/null +++ b/App.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/App.xaml b/App.xaml new file mode 100644 index 0000000..53e826a --- /dev/null +++ b/App.xaml @@ -0,0 +1,17 @@ + + + + + + + + + + \ No newline at end of file diff --git a/App.xaml.cs b/App.xaml.cs new file mode 100644 index 0000000..26596fe --- /dev/null +++ b/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 +{ + /// + /// App.xaml 的交互逻辑 + /// + public partial class App : Application + { + } +} diff --git a/Command/DelegateCommand.cs b/Command/DelegateCommand.cs new file mode 100644 index 0000000..be531b4 --- /dev/null +++ b/Command/DelegateCommand.cs @@ -0,0 +1,127 @@ +// Developed by doiTTeam => devdoiTTeam@gmail.com +using System; +using System.Windows.Input; + +namespace DraggAnimatedPanelExample +{ + /// + /// An whose delegates can be attached for and . + /// It also implements the interface, which is useful when registering this command in a that monitors command's activity. + /// + /// Parameter type. + /// + /// 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. + /// + /// Instead, callers should support a value type by using a nullable value type and checking the HasValue property before using the Value property. + /// + /// + /// public MyClass() + /// { + /// this.submitCommand = new DelegateCommand<int?>(this.Submit, this.CanSubmit); + /// } + /// + /// private bool CanSubmit(int? customerId) + /// { + /// return (customerId.HasValue && customers.Contains(customerId.Value)); + /// } + /// + /// + /// + public class DelegateCommand : DelegateCommandBase + { + /// + /// Initializes a new instance of . + /// + /// Delegate to execute when Execute is called on the command. This can be null to just hook up a CanExecute delegate. + /// + /// will always return true. + /// + public DelegateCommand(Action executeMethod) + : this(executeMethod, (o) => true) + { + } + + /// + /// Initializes a new instance of . + /// + /// Delegate to execute when Execute is called on the command. This can be null to just hook up a CanExecute delegate. + /// Delegate to execute when CanExecute is called on the command. This can be null. + /// When both and ar . + public DelegateCommand(Action executeMethod, Func canExecuteMethod) + : base((o) => executeMethod((T) o), (o) => canExecuteMethod((T) o)) + { + if (executeMethod == null || canExecuteMethod == null) + throw new ArgumentNullException("executeMethod"); + } + + /// + /// Determines if the command can execute by invoked the provided during construction. + /// + ///Data used by the command to determine if it can execute. + /// + /// if this command can be executed; otherwise, . + /// + public bool CanExecute(T parameter) + { + return base.CanExecute(parameter); + } + + /// + /// Executes the command and invokes the provided during construction. + /// + ///Data used by the command. + public void Execute(T parameter) + { + base.Execute(parameter); + } + } + + /// + /// An whose delegates do not take any parameters for and . + /// + /// + /// + public class DelegateCommand : DelegateCommandBase + { + /// + /// Creates a new instance of with the to invoke on execution. + /// + /// The to invoke when is called. + public DelegateCommand(Action executeMethod) : this(executeMethod, () => true) + { + } + + /// + /// Creates a new instance of with the to invoke on execution + /// and a to query for determining if the command can execute. + /// + /// The to invoke when is called. + /// The to invoke when is called + public DelegateCommand(Action executeMethod, Func canExecuteMethod) + : base((o) => executeMethod(), (o) => canExecuteMethod()) + { + if (executeMethod == null || canExecuteMethod == null) + throw new ArgumentNullException("executeMethod"); + } + + + /// + /// Executes the command. + /// + public void Execute() + { + Execute(null); + } + + /// + /// Determines if the command can be executed. + /// + /// Returns if the command can execute,otherwise returns . + public bool CanExecute() + { + return CanExecute(null); + } + } +} \ No newline at end of file diff --git a/Command/DelegateCommandBase.cs b/Command/DelegateCommandBase.cs new file mode 100644 index 0000000..29a721f --- /dev/null +++ b/Command/DelegateCommandBase.cs @@ -0,0 +1,95 @@ +// Developed by doiTTeam => devdoiTTeam@gmail.com +using System; +using System.Diagnostics.CodeAnalysis; +using System.Windows.Input; + +namespace DraggAnimatedPanelExample +{ + /// + /// An whose delegates can be attached for and . + /// + public abstract class DelegateCommandBase : ICommand + { + private readonly Func canExecuteMethod; + private readonly Action executeMethod; + + /// + /// Createse a new instance of a , specifying both the execute action and the can execute function. + /// + /// The to execute when is invoked. + /// The to invoked when is invoked. + protected DelegateCommandBase(Action executeMethod, Func 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); + } + + /// + /// Occurs when changes occur that affect whether or not the command should execute. + /// + public event EventHandler CanExecuteChanged; + + #endregion + + /// + /// Raises on the UI thread so every + /// command invoker can requery to check if the + /// can execute. + /// + protected virtual void OnCanExecuteChanged() + { + var handlers = CanExecuteChanged; + if (handlers != null) + { + handlers(this, EventArgs.Empty); + } + } + + /// + /// Raises on the UI thread so every command invoker + /// can requery to check if the command can execute. + /// + /// Note that this will trigger the execution of once for each invoker. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")] + public void RaiseCanExecuteChanged() + { + OnCanExecuteChanged(); + } + + /// + /// Executes the command with the provided parameter by invoking the supplied during construction. + /// + /// + protected void Execute(object parameter) + { + executeMethod(parameter); + } + + /// + /// Determines if the command can execute with the provided parameter by invoing the supplied during construction. + /// + /// The parameter to use when determining if this command can execute. + /// Returns if the command can execute. otherwise. + protected bool CanExecute(object parameter) + { + return canExecuteMethod == null || canExecuteMethod(parameter); + } + } +} \ No newline at end of file diff --git a/Constant/DefaultConstant.cs b/Constant/DefaultConstant.cs new file mode 100644 index 0000000..a706f9b --- /dev/null +++ b/Constant/DefaultConstant.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +/// +/// 默认参数 +/// +namespace GeekDesk.Constant +{ + enum DefaultConstant + { + WINDOW_WIDTH = 650, //默认窗体宽度 + WINDOW_HEIGHT = 700, //默认窗体高度 + MENU_CARD_WIDHT = 150 //默认菜单栏宽度 + } +} diff --git a/Constant/SortType.cs b/Constant/SortType.cs new file mode 100644 index 0000000..b2238e7 --- /dev/null +++ b/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 //按使用次数排序 + } +} diff --git a/DraggAnimatedPanel/DraggAnimatedPanel.Drag.cs b/DraggAnimatedPanel/DraggAnimatedPanel.Drag.cs new file mode 100644 index 0000000..72877b9 --- /dev/null +++ b/DraggAnimatedPanel/DraggAnimatedPanel.Drag.cs @@ -0,0 +1,205 @@ +/*Developed by (doiTTeam)=>doiTTeam.mail = devdoiTTeam@gmail.com*/ +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 +{ + /// + /// Description of SafariPanel_Drag. + /// + 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(); + } + } + } + } +} diff --git a/DraggAnimatedPanel/DraggAnimatedPanel.cs b/DraggAnimatedPanel/DraggAnimatedPanel.cs new file mode 100644 index 0000000..81bd82d --- /dev/null +++ b/DraggAnimatedPanel/DraggAnimatedPanel.cs @@ -0,0 +1,237 @@ +/*Developed by (doiTTeam)=>doiTTeam.mail = devdoiTTeam@gmail.com*/ +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 +{ + /// + /// Description of DraggAnimatedPanel. + /// + 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 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 + } +} diff --git a/GeekDesk.csproj b/GeekDesk.csproj new file mode 100644 index 0000000..70cf898 --- /dev/null +++ b/GeekDesk.csproj @@ -0,0 +1,146 @@ + + + + + Debug + AnyCPU + {B4983CEC-2281-413C-8ECF-92EE0E40A713} + WinExe + GeekDesk + GeekDesk + v4.7.2 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + true + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + + packages\CommonServiceLocator.2.0.6\lib\net45\CommonServiceLocator.dll + + + packages\MvvmLightLibs.5.4.1.1\lib\net45\GalaSoft.MvvmLight.dll + + + packages\MvvmLightLibs.5.4.1.1\lib\net45\GalaSoft.MvvmLight.Extras.dll + + + packages\MvvmLightLibs.5.4.1.1\lib\net45\GalaSoft.MvvmLight.Platform.dll + + + packages\HandyControl.3.1.0\lib\net452\HandyControl.dll + + + packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + + + + + + packages\System.Drawing.Common.5.0.2\lib\net461\System.Drawing.Common.dll + + + packages\MvvmLightLibs.5.4.1.1\lib\net45\System.Windows.Interactivity.dll + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + + + + + + + + + + + + + + + + + + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + MainWindow.xaml + Code + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + \ No newline at end of file diff --git a/GeekDesk.sln b/GeekDesk.sln new file mode 100644 index 0000000..a05cf6d --- /dev/null +++ b/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 diff --git a/MainWindow.xaml b/MainWindow.xaml new file mode 100644 index 0000000..cae0118 --- /dev/null +++ b/MainWindow.xaml @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs new file mode 100644 index 0000000..2c3296d --- /dev/null +++ b/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 +{ + /// + /// MainWindow.xaml 的交互逻辑 + /// + /// + public partial class MainWindow : Window + { + private static MainModel mainModel; + + ListViewDragDropManager dragMgr; + ListViewDragDropManager 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 _swap; + public DelegateCommand SwapCommand + { + get + { + if (_swap == null) + _swap = new DelegateCommand( + (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 _swap2; + public DelegateCommand SwapCommand2 + { + get + { + if (_swap2 == null) + _swap2 = new DelegateCommand( + (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) + { + + } + + + + /// + /// 图标点击事件 + /// + /// + /// + private void dataClick(object sender, MouseButtonEventArgs e) + { + //string path = ((StackPanel)sender).Tag.ToString(); + //System.Diagnostics.Process.Start(path); + } + + /// + /// data选中事件 设置不可选中 + /// + /// + /// + 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 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).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).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 MenuList { get; set; } + + public List DataList { get; set; } + + + } + + +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c057420 --- /dev/null +++ b/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 文件中的 CultureYouAreCodingWith +//例如,如果您在源文件中使用的是美国英语, +//使用的是美国英语,请将 设置为 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")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs new file mode 100644 index 0000000..f906b01 --- /dev/null +++ b/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace GeekDesk.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 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() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [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; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/Properties/Resources.resx b/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs new file mode 100644 index 0000000..0e3a3af --- /dev/null +++ b/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +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; + } + } + } +} diff --git a/Properties/Settings.settings b/Properties/Settings.settings new file mode 100644 index 0000000..033d7a5 --- /dev/null +++ b/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Util/CommonCode.cs b/Util/CommonCode.cs new file mode 100644 index 0000000..d6b900f --- /dev/null +++ b/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; + +/// +/// 提取一些代码 +/// +namespace GeekDesk.Util +{ + class CommonCode + { + private static string appConfigFilePath = AppDomain.CurrentDomain.BaseDirectory.Trim() + "\\config"; + /// + /// 获取app配置 + /// + /// + 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(json); + } + } + return config; + } + + /// + /// 保存app配置 + /// + /// + 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); + } + } + } +} diff --git a/Util/ConsoleManager.cs b/Util/ConsoleManager.cs new file mode 100644 index 0000000..112968f --- /dev/null +++ b/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; } + } + + /// + /// Creates a new console instance if the process is not attached to a console already. + /// + public static void Show() + { + //#if DEBUG + if (!HasConsole) + { + AllocConsole(); + InvalidateOutAndError(); + } + //#endif + } + + /// + /// 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. + /// + 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); + } + } +} diff --git a/Util/DragAdorner.cs b/Util/DragAdorner.cs new file mode 100644 index 0000000..e7dc001 --- /dev/null +++ b/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 +{ + /// + /// Renders a visual which can follow the mouse cursor, + /// such as during a drag-and-drop operation. + /// + public class DragAdorner : Adorner + { + #region Data + + private Rectangle child = null; + private double offsetLeft = 0; + private double offsetTop = 0; + + #endregion // Data + + #region Constructor + + /// + /// Initializes a new instance of DragVisualAdorner. + /// + /// The element being adorned. + /// The size of the adorner. + /// A brush to with which to paint the adorner. + 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 + + /// + /// Override. + /// + /// + /// + 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 + + /// + /// Gets/sets the horizontal offset of the adorner. + /// + public double OffsetLeft + { + get { return this.offsetLeft; } + set + { + this.offsetLeft = value; + UpdateLocation(); + } + } + + #endregion // OffsetLeft + + #region SetOffsets + + /// + /// Updates the location of the adorner in one atomic operation. + /// + /// + /// + public void SetOffsets( double left, double top ) + { + this.offsetLeft = left; + this.offsetTop = top; + this.UpdateLocation(); + } + + #endregion // SetOffsets + + #region OffsetTop + + /// + /// Gets/sets the vertical offset of the adorner. + /// + public double OffsetTop + { + get { return this.offsetTop; } + set + { + this.offsetTop = value; + UpdateLocation(); + } + } + + #endregion // OffsetTop + + #endregion // Public Interface + + #region Protected Overrides + + /// + /// Override. + /// + /// + /// + protected override Size MeasureOverride( Size constraint ) + { + this.child.Measure( constraint ); + return this.child.DesiredSize; + } + + /// + /// Override. + /// + /// + /// + protected override Size ArrangeOverride( Size finalSize ) + { + this.child.Arrange( new Rect( finalSize ) ); + return finalSize; + } + + /// + /// Override. + /// + /// + /// + protected override Visual GetVisualChild( int index ) + { + return this.child; + } + + /// + /// Override. Always returns 1. + /// + 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 + } +} \ No newline at end of file diff --git a/Util/FileIcon.cs b/Util/FileIcon.cs new file mode 100644 index 0000000..7b26b17 --- /dev/null +++ b/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 + { + /// get icon + Icon = 0x000000100, + /// get display name + DisplayName = 0x000000200, + /// get type name + TypeName = 0x000000400, + /// get attributes + Attributes = 0x000000800, + /// get icon location + IconLocation = 0x000001000, + /// return exe type + ExeType = 0x000002000, + /// get system icon index + SysIconIndex = 0x000004000, + /// put a link overlay on icon + LinkOverlay = 0x000008000, + /// show icon in selected state + Selected = 0x000010000, + /// get only specified attributes + Attr_Specified = 0x000020000, + /// get large icon + LargeIcon = 0x000000000, + /// get small icon + SmallIcon = 0x000000001, + /// get open icon + OpenIcon = 0x000000002, + /// get shell size icon + ShellIconSize = 0x000000004, + /// pszPath is a pidl + PIDL = 0x000000008, + /// use passed dwFileAttribute + UseFileAttributes = 0x000000010, + /// apply the appropriate overlays + AddOverlays = 0x000000020, + /// Get the index of the overlay in the upper 8 bits of the iIcon + 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); + }; + + } + +} diff --git a/Util/ListViewDragDropManager.cs b/Util/ListViewDragDropManager.cs new file mode 100644 index 0000000..1a680d6 --- /dev/null +++ b/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 + + /// + /// 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. + /// + /// The type of the ListView's items. + public class ListViewDragDropManager 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 + + /// + /// Initializes a new instance of ListViewDragManager. + /// + public ListViewDragDropManager() + { + this.canInitiateDrag = false; + this.dragAdornerOpacity = 0.7; + this.indexToSelect = -1; + this.showDragAdorner = true; + } + + /// + /// Initializes a new instance of ListViewDragManager. + /// + /// + public ListViewDragDropManager( ListView listView ) + : this() + { + this.ListView = listView; + } + + /// + /// Initializes a new instance of ListViewDragManager. + /// + /// + /// + public ListViewDragDropManager( ListView listView, double dragAdornerOpacity ) + : this( listView ) + { + this.DragAdornerOpacity = dragAdornerOpacity; + } + + /// + /// Initializes a new instance of ListViewDragManager. + /// + /// + /// + public ListViewDragDropManager( ListView listView, bool showDragAdorner ) + : this( listView ) + { + this.ShowDragAdorner = showDragAdorner; + } + + #endregion // Constructors + + #region Public Interface + + #region DragAdornerOpacity + + /// + /// Gets/sets the opacity of the drag adorner. This property has no + /// effect if ShowDragAdorner is false. The default value is 0.7 + /// + 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 + + /// + /// Returns true if there is currently a drag operation being managed. + /// + public bool IsDragInProgress + { + get { return this.isDragInProgress; } + private set { this.isDragInProgress = value; } + } + + #endregion // IsDragInProgress + + #region ListView + + /// + /// 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. + /// + 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] + + /// + /// 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. + /// + public event EventHandler> ProcessDrop; + + #endregion // ProcessDrop [event] + + #region ShowDragAdorner + + /// + /// Gets/sets whether a visual representation of the ListViewItem being dragged + /// follows the mouse cursor during a drag operation. The default value is true. + /// + 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 which contains the dropped data object. + ObservableCollection itemsSource = this.listView.ItemsSource as ObservableCollection; + if( itemsSource == null ) + throw new Exception( + "A ListView managed by ListViewDragManager must have its ItemsSource set to an ObservableCollection." ); + + 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 args = new ProcessDropEventArgs( 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 + + /// + /// Returns the index of the ListViewItem underneath the + /// drag cursor, or -1 if the cursor is not over an item. + /// + 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 + + /// + /// Returns true if the mouse cursor is over a scrollbar in the ListView. + /// + 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 + + /// + /// 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. + /// + public static class ListViewItemDragState + { + #region IsBeingDragged + + /// + /// Identifies the ListViewItemDragState's IsBeingDragged attached property. + /// This field is read-only. + /// + public static readonly DependencyProperty IsBeingDraggedProperty = + DependencyProperty.RegisterAttached( + "IsBeingDragged", + typeof( bool ), + typeof( ListViewItemDragState ), + new UIPropertyMetadata( false ) ); + + /// + /// Returns true if the specified ListViewItem is being dragged, else false. + /// + /// The ListViewItem to check. + public static bool GetIsBeingDragged( ListViewItem item ) + { + return (bool)item.GetValue( IsBeingDraggedProperty ); + } + + /// + /// Sets the IsBeingDragged attached property for the specified ListViewItem. + /// + /// The ListViewItem to set the property on. + /// Pass true if the element is being dragged, else false. + internal static void SetIsBeingDragged( ListViewItem item, bool value ) + { + item.SetValue( IsBeingDraggedProperty, value ); + } + + #endregion // IsBeingDragged + + #region IsUnderDragCursor + + /// + /// Identifies the ListViewItemDragState's IsUnderDragCursor attached property. + /// This field is read-only. + /// + public static readonly DependencyProperty IsUnderDragCursorProperty = + DependencyProperty.RegisterAttached( + "IsUnderDragCursor", + typeof( bool ), + typeof( ListViewItemDragState ), + new UIPropertyMetadata( false ) ); + + /// + /// Returns true if the specified ListViewItem is currently underneath the cursor + /// during a drag-drop operation, else false. + /// + /// The ListViewItem to check. + public static bool GetIsUnderDragCursor( ListViewItem item ) + { + return (bool)item.GetValue( IsUnderDragCursorProperty ); + } + + /// + /// Sets the IsUnderDragCursor attached property for the specified ListViewItem. + /// + /// The ListViewItem to set the property on. + /// Pass true if the element is underneath the drag cursor, else false. + internal static void SetIsUnderDragCursor( ListViewItem item, bool value ) + { + item.SetValue( IsUnderDragCursorProperty, value ); + } + + #endregion // IsUnderDragCursor + } + + #endregion // ListViewItemDragState + + #region ProcessDropEventArgs + + /// + /// Event arguments used by the ListViewDragDropManager.ProcessDrop event. + /// + /// The type of data object being dropped. + public class ProcessDropEventArgs : EventArgs where ItemType : class + { + #region Data + + ObservableCollection itemsSource; + ItemType dataItem; + int oldIndex; + int newIndex; + DragDropEffects allowedEffects = DragDropEffects.None; + DragDropEffects effects = DragDropEffects.None; + + #endregion // Data + + #region Constructor + + internal ProcessDropEventArgs( + ObservableCollection 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 + + /// + /// The items source of the ListView where the drop occurred. + /// + public ObservableCollection ItemsSource + { + get { return this.itemsSource; } + } + + /// + /// The data object which was dropped. + /// + public ItemType DataItem + { + get { return this.dataItem; } + } + + /// + /// The current index of the data item being dropped, in the ItemsSource collection. + /// + public int OldIndex + { + get { return this.oldIndex; } + } + + /// + /// The target index of the data item being dropped, in the ItemsSource collection. + /// + public int NewIndex + { + get { return this.newIndex; } + } + + /// + /// The drag drop effects allowed to be performed. + /// + public DragDropEffects AllowedEffects + { + get { return allowedEffects; } + } + + /// + /// The drag drop effect(s) performed on the dropped item. + /// + public DragDropEffects Effects + { + get { return effects; } + set { effects = value; } + } + + #endregion // Public Properties + } + + #endregion // ProcessDropEventArgs +} \ No newline at end of file diff --git a/Util/MenuWidthConvert.cs b/Util/MenuWidthConvert.cs new file mode 100644 index 0000000..d83a00a --- /dev/null +++ b/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(); + } + } +} diff --git a/Util/MouseUtilities.cs b/Util/MouseUtilities.cs new file mode 100644 index 0000000..ef5aea4 --- /dev/null +++ b/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 +{ + /// + /// Provides access to the mouse location by calling unmanaged code. + /// + /// + /// 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 + /// + 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 ); + + /// + /// 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. + /// + /// The Visual to which the mouse coordinates will be relative. + 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 + } + } +} \ No newline at end of file diff --git a/Util/SystemIcon.cs b/Util/SystemIcon.cs new file mode 100644 index 0000000..337714b --- /dev/null +++ b/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 + /// + /// 获取文件类型的关联图标 + /// + /// 文件类型的扩展名或文件的绝对路径 + /// 是否返回大图标 + /// 获取到的图标 + 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; + } + /// + /// 获取文件夹图标 + /// + /// 图标 + 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. + // ); + } +} diff --git a/ViewModel/AppConfig.cs b/ViewModel/AppConfig.cs new file mode 100644 index 0000000..3b74363 --- /dev/null +++ b/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 + + } +} diff --git a/ViewModel/DataInfos.cs b/ViewModel/DataInfos.cs new file mode 100644 index 0000000..7b0bae1 --- /dev/null +++ b/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(); + } + } + } +} diff --git a/ViewModel/MainModel.cs b/ViewModel/MainModel.cs new file mode 100644 index 0000000..86f5601 --- /dev/null +++ b/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 + { + } +} diff --git a/ViewModel/MainViewModel.cs b/ViewModel/MainViewModel.cs new file mode 100644 index 0000000..e4bfb26 --- /dev/null +++ b/ViewModel/MainViewModel.cs @@ -0,0 +1,34 @@ +using GalaSoft.MvvmLight; + +namespace GeekDesk.ViewModel +{ + /// + /// This class contains properties that the main View can data bind to. + /// + /// Use the mvvminpc snippet to add bindable properties to this ViewModel. + /// + /// + /// You can also use Blend to data bind with the tool's support. + /// + /// + /// See http://www.galasoft.ch/mvvm + /// + /// + public class MainViewModel : ViewModelBase + { + /// + /// Initializes a new instance of the MainViewModel class. + /// + public MainViewModel() + { + ////if (IsInDesignMode) + ////{ + //// // Code runs in Blend --> create design time data. + ////} + ////else + ////{ + //// // Code runs "for real" + ////} + } + } +} \ No newline at end of file diff --git a/ViewModel/MenuViewModel.cs b/ViewModel/MenuViewModel.cs new file mode 100644 index 0000000..419c2cb --- /dev/null +++ b/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 GetMenus() + { + ObservableCollection menus = new ObservableCollection(); + 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; } + } +} diff --git a/packages.config b/packages.config new file mode 100644 index 0000000..a705136 --- /dev/null +++ b/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file