WPF中元素拖拽的两个实例

WPF中元素拖拽的两个实例
原文:WPF中元素拖拽的两个实例

  今天结合之前做过的一些拖拽的例子来对这个方面进行一些总结,这里主要用两个例子来说明在WPF中如何使用拖拽进行操作,元素拖拽是一个常见的操作,第一个拖拽的例子是将ListBox中的子元素拖拽到ListView的某一个节点,从而将该子元素作为当前节点的子节点。第二个例子就是将ListView的某一项拖拽到另外一项上从而使两个子项位置互换,这两个例子的原理类似,实现细节上有所差别,下面就具体分析一下这些细节。

  DEMO1

  一 示例截图

图一 示例一截图

  二 重点原理分析

            2.1 前台代码分析

      这一部分主要是主界面的分析,主要包括两个部分一个是左侧的ListBox另外一个就是右侧的TreeView,在Treeview上设置了两个事件,一个是DragDrop

           .DragOver事件,另外一个是DragDrop.Drop事件,同时设置TreeView的AllowDrop属性为true,关于这两个事件后面再做重点分析。首先看看前台代码:

<Window x:Class="DragDrop.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:DragDrop"mc:Ignorable="d"Title="MainWindow" Height="350" Width="525"><Window.Resources><DataTemplate x:Key="listBoxTemplate" DataType="{x:Type local:DataItem}"><TextBlock Text="{Binding Header}"/></DataTemplate><HierarchicalDataTemplate x:Key="treeViewTemplate" DataType="{x:Type local:DataItem}" ItemsSource="{Binding Items}"><TextBlock Text="{Binding Header}"/></HierarchicalDataTemplate></Window.Resources><Grid x:Name="mTopLevelGrid"><Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="10"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions><ListBox x:Name="mListBox" Grid.Column="0" ItemsSource="{Binding Source={x:Static local:Data.Instance}, Path=ListBoxItems}" ItemTemplate="{StaticResource listBoxTemplate}"/><TreeView x:Name="mTreeView" Grid.Column="2" ItemsSource="{Binding Source={x:Static local:Data.Instance}, Path=TreeViewItems}" ItemTemplate="{StaticResource treeViewTemplate}"AllowDrop="True" DragDrop.DragOver="OnDragOver" DragDrop.Drop="OnDrop"/></Grid>
</Window>

  2.2 后台代码分析

  下面重点分析后台代码,在构造函数中我们首先为ListBox订阅了两个事件,一个是:PreviewMouseMove,另外一个是QueryContinueDrag,关于第一个事件就不做过多的说明,第二个事件是QueryContinueDrag:QueryContinueDrag在MSDN上的解释是在拖放操作期间键盘或鼠标按钮的状态改变时发生。这里在拖拽过程中由于鼠标的位置一直在移动,所以该函数会一直执行,那么我们来分析一下,这两个函数中到底做了些什么事情。  

private void OnPreviewListBoxMouseMove(object sender, MouseEventArgs e){if (Mouse.LeftButton != MouseButtonState.Pressed)return;Point pos = e.GetPosition(mListBox);HitTestResult result = VisualTreeHelper.HitTest(mListBox, pos);if (result == null)return;ListBoxItem listBoxItem = Utils.FindVisualParent<ListBoxItem>(result.VisualHit); // Find your actual visual you want to dragif (listBoxItem == null || listBoxItem.Content != mListBox.SelectedItem || !(mListBox.SelectedItem is DataItem))return;DragDropAdorner adorner = new DragDropAdorner(listBoxItem);mAdornerLayer = AdornerLayer.GetAdornerLayer(mTopLevelGrid); // Window class do not have AdornerLayermAdornerLayer.Add(adorner);DataItem dataItem = listBoxItem.Content as DataItem;DataObject dataObject = new DataObject(dataItem.Clone());System.Windows.DragDrop.DoDragDrop(mListBox, dataObject, DragDropEffects.Copy);          mStartHoverTime = DateTime.MinValue;mHoveredItem = null;mAdornerLayer.Remove(adorner);mAdornerLayer = null;}

  这段代码是为ListBox订阅的PreviewMouseMove事件,首先要获取到将要拖拽的ListBoxItem对象,获取到这个对象之后我们需要为mTopLevelGrid的AdornerLayer添加一个Adorner对象从而在拖拽的时候显示当前对象。然后我们便启动拖拽操作了 System.Windows.DragDrop.DoDragDrop(mListBox, dataObject, DragDropEffects.Copy); 只有启动了拖拽操作,后面的TreeView才能够执行相应的DragOver和Drop事件,在完成整个拖拽操作后才能够执行这句代码后面的释放当前对象的一些操作,这个就是整个大体的流程。

  在DragOver事件中我们只做了一件事就是当拖拽的对象移动到TreeViewItem的上面时,当前的TreeViewItem处于选中状态,而在Drop事件中我们需要将拖拽的对象作为子元素添加到当前的TreeViewItem的下一级,这两个过程最重要的都是通过VisualTreeHelper的HitTest(命中操作)来获取到当前的TreeViewItem对象。这个也是非常重要的一个部分,下面贴出具体的代码。

 private void OnDragOver(object sender, DragEventArgs e){e.Effects = DragDropEffects.None;Point pos = e.GetPosition(mTreeView);HitTestResult result = VisualTreeHelper.HitTest(mTreeView, pos);if (result == null)return;TreeViewItem selectedItem = Utils.FindVisualParent<TreeViewItem>(result.VisualHit);if (selectedItem != null)selectedItem.IsSelected = true;e.Effects = DragDropEffects.Copy;}private void OnDrop(object sender, DragEventArgs e){Point pos = e.GetPosition(mTreeView);HitTestResult result = VisualTreeHelper.HitTest(mTreeView, pos);if (result == null)return;TreeViewItem selectedItem = Utils.FindVisualParent<TreeViewItem>(result.VisualHit);if (selectedItem == null)return;DataItem parent = selectedItem.Header as DataItem;DataItem dataItem = e.Data.GetData(typeof(DataItem)) as DataItem;if (parent != null && dataItem != null)parent.Items.Add(dataItem);}

  另外一个重要的部分就是在拖拽的过程中我们需要不断去更新当前Adorner更新位置,这里我们通过重写OnRender函数来实现这一个目标。

public class DragDropAdorner : Adorner{public DragDropAdorner(UIElement parent): base(parent){IsHitTestVisible = false; // Seems Adorner is hit test visible?mDraggedElement = parent as FrameworkElement;}protected override void OnRender(DrawingContext drawingContext){base.OnRender(drawingContext);if (mDraggedElement != null){Win32.POINT screenPos = new Win32.POINT();if (Win32.GetCursorPos(ref screenPos)){Point pos =this.PointFromScreen(new Point(screenPos.X, screenPos.Y));                    Rect rect = new Rect(pos.X, pos.Y, mDraggedElement.ActualWidth, mDraggedElement.ActualHeight);drawingContext.PushOpacity(1.0);Brush highlight = mDraggedElement.TryFindResource(SystemColors.HighlightBrushKey) as Brush;if (highlight != null)drawingContext.DrawRectangle(highlight, new Pen(Brushes.Transparent, 0), rect);drawingContext.DrawRectangle(new VisualBrush(mDraggedElement),new Pen(Brushes.Transparent, 0), rect);}}}FrameworkElement mDraggedElement = null;}

  另外一点需要注意的是在ListBox订阅的OnQueryContinueDrag事件中必须不停执行刷新的操作,否则当前的拖拽对象是不能够实时进行更新操作的,这一点非常重要。

private void OnQueryContinueDrag(object sender, QueryContinueDragEventArgs e){mAdornerLayer.Update();UpdateTreeViewExpandingState();           }

  这个示例就介绍到这里,关键是对整体的拖拽有一个概念性的理解,接着我会介绍下面的示例,介绍完毕之后会对两者之间进行一个对比,对比之后会进一步对整个拖拽过程进行总结。

   DEMO2

        这一部分是一个ListView里面的Item的拖拽操作,通过拖拽操作能够改变元素的位置,从而实现自定义排序的结果。

  一 示例截图  

图二 示例二截图

  二 重点原理分析

    2.1  前台代码分析

    这一部分的前台代码比较简单就是在ListView中嵌套GridView对象,然后为当前对象绑定数据源,这里比较简单不再赘述。

<Window x:Class="ListViewDragDemo.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:ListViewDragDemo"xmlns:util="clr-namespace:ListViewDragDemo.Utils"mc:Ignorable="d"Title="MainWindow" Height="350" Width="525"><Window.Resources><Style x:Key="ItemContStyle" TargetType="ListViewItem"><Style.Resources><LinearGradientBrush x:Key="MouseOverBrush" StartPoint="0.5, 0" EndPoint="0.5, 1"><GradientStop Color="#22000000" Offset="0" /><GradientStop Color="#44000000" Offset="0.4" /><GradientStop Color="#55000000" Offset="0.6" /><GradientStop Color="#33000000" Offset="0.9" /><GradientStop Color="#22000000" Offset="1" /></LinearGradientBrush></Style.Resources>       <Setter Property="Padding" Value="0,8" /><Setter Property="HorizontalContentAlignment" Value="Stretch" /><Setter Property="Border.BorderThickness" Value="0,0,0,0.5" /><Setter Property="Border.BorderBrush" Value="LightGray" /><Style.Triggers><Trigger Property="util:ListViewItemDragState.IsBeingDragged" Value="True"><Setter Property="FontWeight" Value="DemiBold" /></Trigger><Trigger Property="util:ListViewItemDragState.IsUnderDragCursor" Value="True"><Setter Property="Background" Value="{StaticResource MouseOverBrush}" /></Trigger></Style.Triggers></Style></Window.Resources><Grid><Border Background="#eee" Padding="2" Margin="0 5 5 5"><DockPanel><ListView x:Name="ListViewCtl" ItemsSource="{Binding Students}" ItemContainerStyle="{StaticResource ItemContStyle}" SelectionMode="Single"><ListView.View><GridView><GridViewColumn Header="姓名" DisplayMemberBinding="{Binding Name}" Width="100"/><GridViewColumn Header="性别" DisplayMemberBinding="{Binding Sex}" Width="100"/><GridViewColumn Header="年级" DisplayMemberBinding="{Binding Grade}" Width="100" /><GridViewColumn Header="分数" DisplayMemberBinding="{Binding Score}" Width="100"/></GridView></ListView.View></ListView></DockPanel></Border></Grid>
</Window>

  2.1  后台代码分析

  这一部分涉及到的内容比较多,所采用的方法也有所不同,需要进行比较,然后深化对知识的理解。

  这一个示例与之前的示例的不同之处主要在于:1 不再使用QueryContinueDrag事件来更新当前的Adorner对象的位置,而在DragOver和DragEnter事件中去更新Adorner的位置。 2 获取当前ListViewItem的方式不再使用VisualTreeHelper.HitTest方法来进行获取。 3 Adorner对象中采用不同的重载方法来实现位置的更新操作。下面就这些区别来一一进行说明。

  2.1.1 为当前的ListView订阅事件

     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 Eventsthis.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 Eventsthis.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}}}

  注意在订阅这些方法之前先取消订阅,避免重复进行事件的订阅,这个是非常好的一个习惯。

  2.1.2  具体事件分析

  首先我们看一看PreviewMouseLeftButtonDown这个事件,这个主要是进行一些初始化的操作。首先要获取到当前鼠标位置处的ListViewItem的序号Index,采取的方法时查看当前鼠标的位置是否在ListView的某一个Item的范围之内,这里使用了VisualTreeHelper.GetDescendantBounds这个方法来获取元素的边界位置然后再判断当前鼠标位置是否位于此边界之内,通过这种方式来获取当前拖动的Index值。

void listView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e){if (this.IsMouseOverScrollbar){//Set the flag to false when cursor is over scrollbar.this.canInitiateDrag = false;return;}int index = this.IndexUnderDragCursor;Debug.WriteLine(string.Format("this.IndexUnderDragCursor:{0}", index.ToString()));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;}}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;}}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);}

  下一步就是在PreviewMouseMove事件中获取ListViewItem,添加Adorner,然后再启动拖放操作,这个过程和上面的示例中一样,初始化AdornerLayer的过程在InitializeAdornerLayer这个函数中进行,主要是在当前的ListView的的AdornerLayer层添加拖拽的Adorner对象。

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;}

  这里面的重点是DragAdorner这个类,这个当前的拖拽的对象,我们也就此分析一下。

public class DragAdorner : Adorner{#region Dataprivate 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 Helpersprivate void UpdateLocation(){AdornerLayer adornerLayer = this.Parent as AdornerLayer;if (adornerLayer != null)adornerLayer.Update(this.AdornedElement);}#endregion // Private Helpers}

  在这里面我们不再是重写基类的OnRender这个函数而是采用重写GetDesiredTransform来实现当前的DragAdorner位置随着鼠标的变化而变化的,这里需要注意的是要重写基类的GetVisualChild(int index)这个方法和VisualChildrenCount这个属性,否则程序会无法执行。在解释了这一部分之后就重点来讲述DragEnter、DragOver、DragLeave和Drop这几个函数了,这几个是整个拖拽过程的重点。

  首先是DragEnter,这个当鼠标移入进去的时候会触发此事件,主要是来更新当前DragAdorner的显示状态,DragLeave事件是当当前拖拽的对象移出整个ListView的时候会使DragAdorner不显示。DragOver是一个比较重要的事件,主要是进行两方面的工作,一个是在拖拽时更新当前Adorner的位置,另外就是获取新的鼠标位置处的ListViewItem对象,这个是最后执行Drop事件的最重要的准备工作,有了这些操作,那么最后一步就是调换拖拽的源和目标处的ListViewItem,从而最终完成拖拽操作。

       其实对比两个示例的过程,很多地方都有相似的地方,关键是把整个过程弄清楚,这样无论是从一个控件拖拽到另外一个控件上还是从一个控件的一个项拖到这个控件的另外一个项上都可以实现。

  最后请点击此处获得整个工程的源码!

  

posted on 2018-09-20 09:03 NET未来之路 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/9678820.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/365654.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用vert.x 2.0,RxJava和mongoDB创建simpe RESTful服务

中断了将近半年后发表了一篇新文章。 在本文中&#xff0c;我们将快速了解如何开始使用vert.x&#xff0c;更有趣的是&#xff0c;如何使用RxJava简化异步系统的编程。 我们将涵盖以下主题&#xff1a; 使用Maven创建一个空的vert.x项目 导入IntelliJ并创建一个简单的HTTP服务…

搭建react native所遇到的坑

一、所遇问题 在搭建react native环境中,遇到执行react native run-android命令出现如下问题 1 Could not resolve all dependencies for configuration :classpath. 2 > Could not resolve com.github.dcendents:android-maven-gradle-plugin:3.1.4 3 Required b…

sqlalchemy mysql_使用SQLAlchemy操作MySQL

SQLAlchemy是Python编程语言下的一款开源软件&#xff0c;提供了SQL工具包及对象关系映射(ORM)工具&#xff0c;使用MIT许可证发行。SQLAlchemy首次发行于2006年2月&#xff0c;并迅速地在Python社区中最广泛使用的ORM工具之一&#xff0c;不亚于Django的ORM框架。本文将介绍如…

使用phpstorm+wamp实现php代码实时调试审计

转载自&#xff1a;https://www.bugbank.cn/q/article/5853afaffc0bf4f010ee6ac3.html php调试有N多好用的工具&#xff0c;最近研究到phpstorm配合wamp实现php实时断点调试相当不错&#xff0c;推荐给大家&#xff0c;网上有各种配置版本&#xff0c;但没有一个能好使的。本文…

如何使用Play框架为https配置SSL证书

我花了数小时试图使它起作用&#xff0c;最后&#xff0c;问题是我自己没有使用keytool生成CSR&#xff08;证书请求&#xff09;。 尝试通过https访问Play时&#xff0c;我一直收到此错误&#xff1a; javax.net.ssl.SSLPeerUnverifiedException&#xff1a;对等方未通过身份…

js页面加载前执行_做一名合格的前端开发工程师:Javascript加载执行问题探索

做前端开发少不了各种利器。比如我习惯用的还是Google浏览器和重型武器Fiddller。一&#xff1a;原始情况 首先大家看看如下的代码&#xff1a;估计90%的程序员都会把js文件放在head中&#xff0c;但是大家有没有深究过呢&#xff1f;很多浏览器都会使用单一的线程来做“界面UI…

【线性代数】3-5:独立性,基和维度(Independence,Basis and Dimension)

title: 【线性代数】3-5:独立性&#xff0c;基和维度(Independence,Basis and Dimension) categories: MathematicLinear Algebra keywords:IndependenceBasisDimensionSpan toc: true date: 2017-09-25 15:20:46Abstract: 本文是本章最重要的知识点&#xff0c;也是整个线性代…

APP网络测试要点和弱网模拟

当前APP网络环境比较复杂&#xff0c;网络制式有2G、3G、4G网络&#xff0c;还有越来越多的公共Wi-Fi。不同的网络环境和网络制式的差异&#xff0c;都会对用户使用app造成一定影响。另外&#xff0c;当前app使用场景多变&#xff0c;如进地铁、上公交、进电梯等&#xff0c;使…

word公式插件_再也不用担心我的公式写不出来了:一款公式输入神器实测

→ → https://mp.weixin.qq.com/s/gNRWdN-W4_Eb2MyyTpWzjw关注微信公众号&#xff1a;人工智能前沿讲习&#xff0c;重磅干货&#xff0c;第一时间送达本文推荐一个公式输入神器&#xff0c;只要截图就能识别公式&#xff0c;手写的公式都能识别。经过实测&#xff0c;几乎没有…

使用Camel在Amazon上构建分布式工作流应用程序

带有SNS-SQS的管道 工作流由按动态条件确定的特定顺序执行的独立任务组成。 工作流通常代表业务流程&#xff0c;例如电子商务商店中的订单处理步骤。 Amazon Web Services提供了各种工具来构建分布式和可扩展的工作流应用程序。 构建此类应用程序的一种方法是使用主题和队列来…

mysql 高版本检索外键_第05期:外键到底能不能用?

外键的设计初衷是为了在数据库端保证对逻辑上相关联的表数据在操作上的一致性与完整性。外键在大部分企业写的开发规范里会直接规避掉&#xff01;外键有优缺点&#xff0c;也并不是说每种场景都不适用&#xff0c;完全没有必要一刀切。外键到底能不能用&#xff1f;下面会针对…

从Ant Build演进Gradle Build:导入Ant Build文件

在大型项目上更改构建系统可能很困难并且需要大量工作。 幸运的是&#xff0c;对于那些将Ant版本迁移到Gradle版本的人&#xff0c;Gradle提供了特别方便的机制来促进这种迁移 。 由于Gradle基于Groovy构建&#xff0c;并且Groovy通过AntBuilder包含内置的Ant支持&#xff0c;因…

HTML5 之 简单汇总

参考&#xff1a; HTML5的十大新特性 前端面试必备之html5的新特性 HTML5 主要内容&#xff1a;语义化、增强型表单、多媒体标签、Canvas、SVG、地理定位、拖放API、Web Worker、Web Storage、WebSocket、HTML 5 应用程序缓存 1.语义化元素 1.1结构元素 标签描述article表示与上…

个人信息管理系统代码_Thymeleaf+SpringBoot+Mybatis实现的易游网旅游信息管理系统...

项目简介项目源码&#xff1a;麻烦转发后关注JAVA梦想口服液私信回复【源码】即可获取&#xff01;本系统是基于ThymeleafSpringBootMybatis。是非常标准的SSM三大框架(SpringBoot就是一个大框架&#xff0c;里面包含了许多的东西&#xff0c;其中Spring就是最核心的内容&#…

被问到有没有内核开发经验_一个人就是一个开发团队!成电硬核毕业生自制迷你电脑走红!...

△小视频近日&#xff0c;一段长3分多钟的小视频在B站突然爆红&#xff0c;不仅登上首页&#xff0c;获得超过200万的播放量&#xff0c;还被众多观众“膜拜”。有网友在评论区说&#xff1a;“其实这些东西吧&#xff0c;外行看起来很牛&#xff0c;但我这种内行看起来&#x…

matlab2010a连接mysql_MATLAB2010a+OpenCV2.3.1+VS2010运行TLD

出现matlab不显示C编译器的原因主要还是当前Matlab版本相对于VS来说不够新&#xff0c;比如14版的肯定支持10的VS。 本文引用地址&#xff1a; http://blog.csdn.net/shanpohe/article/details/7596401 http://blog.sina.com.cn/s/blog_adfd55190101ejvr.html TLD(Tracking Lea…

更改span标签样式_CSS 内嵌样式

前面一节我们讲了行内样式&#xff0c;但是行内样式的缺点就是样式不能重用。例如当有好多个 标签&#xff0c;我们希望所有的 标签的样式都一致&#xff0c;那么需要在每个标签中都写一遍&#xff0c;这么会很麻烦&#xff0c;也会增加很多代码。那么为了解决这个问题&#…

mysql 6安装当前密码_MySQL8.0 安装踩坑指南

就在昨天上午&#xff0c;刚为云服务器安装好Apache2.4.33和PHP7.2.4环境&#xff0c;准备再来一个最新的MySQL5.7.22。寻找5.7版本的rpm包时下到mysql80xxx.rpm&#xff0c;看人家的教程是mysql57&#xff0c;难道80是MySQL出出…出了8版&#xff0c;一搜新闻2个小时前MySQL发…

Elasticsearch环境搭建和介绍(Windows)

一、Elasticsearch介绍和安装 1.1 介绍 Elastic Elastic官网&#xff1a;https://www.elastic.co/cn/ Elastic有一条完整的产品线&#xff1a;Elasticsearch、Kibana、Logstash等&#xff0c;前面说的三个就是大家常说的ELK技术栈。 Elasticsearch Elasticsearch官网&#xff1…

Jar Hell变得轻松–用jHades揭秘classpath

Java开发人员将不得不面对的最困难的问题是类路径错误&#xff1a; ClassNotFoundException &#xff0c; NoClassDefFoundError &#xff0c;Jar Hell&#xff0c; Xerces Hell和company。 在本文中&#xff0c;我们将探究这些问题的根本原因&#xff0c;并了解最小的工具&am…