既然Silverlight号称是AJAX杀手,而且相比JavaScript更接近桌面应用,那么这种拖拽的效果自然是手到擒来。
布局容器选择:
Silverlight提供了Canvas、GrID、StackPanel三种布局容器,基本能满足各种需要。个人习惯是用GrID做框架布局,具体内容利用StackPanel自组织,而Canvas则在某些特定场合,比如@R_502_6062@的界面中使用。
一方面在自适应浏览器大小的角度上,GrID提供了两方面的自由度,StackPanel提供了一个方向的自由度,而Canvas没有自由度,也就不能随这浏览器大小改变而自动适应大小;另一方面,GrID和StackPanel都可以不用直接指定坐标进行布局,而Canvas则需要指定确定的坐标,维护起来肯定麻烦;第三点,则是主要针对于可拖拽效果的,StackPanel是唯一可以通过添加、删除内部控件而自动调整内部其他控件位置的布局容器。
所以最后决定:用GrID做外部布局,每一列都设定ColumnDeFinition,再在每一列添加一个StackPanel做为单列的容器:
@H_404_31@
@H_404_31@
@H_404_31@这里的ContainerGrID和ContainerPanel分别继承自GrID和Panel,主要是添加了几个方便以后查询以及扩展的属性。
可拖拽控件:
可拖拽控件可以继承自任何控件,这里用的是UserControl。
public partial class DragableGrID : UserControl那么对于DragableGrID,最主要的事件是三个:MouseleftbuttonDown(左键按下),MouseleftbuttonUp(左键抬起),MouseMove(左键移动)。MouseleftbuttonDown标识拖拽开始,MouseleftbuttonUp说明拖拽结束,而MouseMove则是拖拽过程触发。此外,考虑到当鼠标移动到控件可移动范围之外的情况,还有MouseLeave事件,和MouseleftbuttonUp的意义一样。所以需要在构造函数中添加它们的处理逻辑:
this.MouseleftbuttonDown += new MousebuttonEventHandler(DragableGrID_MouseleftbuttonDown);
this.MouseleftbuttonUp += new MousebuttonEventHandler(DragableGrID_MouseleftbuttonUp);
this.MouseMove += new MouseEventHandler(DragableGrID_MouseMove);
this.MouseLeave += new MouseEventHandler(DragableGrID_MouseLeave);左键按下(拖拽开始):
voID DragableGrID_MouseleftbuttonDown(object sender,MousebuttonEventArgs e)
{
//标示开始拖拽
IsDraging = true;
//鼠标的起始位置(控件内部)
_beginPoint = e.Getposition(this);
//鼠标相对于GrID的位置
Point marginPoint = e.Getposition(_parentGrID);
ContainerPanel parentPanel = this.Parent as ContainerPanel;
shadowGrID = new ShadowGrID(this,parentPanel);
//设置初始margin。
Thickness beginmargin = new Thickness(marginPoint.X - _beginPoint.X,marginPoint.Y - _beginPoint.Y,_parentGrID.ActualWIDth - this.ActualWIDth - marginPoint.X + _beginPoint.X,_parentGrID.ActualHeight - this.ActualHeight - marginPoint.Y + _beginPoint.Y);
(this.Parent as Panel).Children.Remove(this);
if (this.Parent == null)
{
_parentGrID.Children.Add(this);
}
this.SetValue(GrID.ColumnSpanProperty,_parentGrID.PanelCount);
this.margin = beginmargin;
}
首先设置IsDraging标识位,然后分别获取鼠标点击点相对于控件左上角以及相对于ContainerGrID左上角的位移,从而获得可拖拽控件相对于ContainerGrID的margin值。注意鼠标相对控件左上角的位移在拖拽过程中是不变的,所以作为可拖拽控件的一个私有变量存储起来。然后将DragableGrID从父Panel中移除,添加到ContainerGrID中,使它能在整个GrID的范围内移动。
这里仿照iGoogle和其他类似的可拖拽框架的模式,当拖拽一个可拖拽控件时,会在可拖拽控件的原处位置添加一个背影控件(ShadowGrID),用来占位:
public ShadowGrID(DragableGrID originGrID,ContainerPanel parentPanel)
{
InitializeComponent();
this._orginGrID = originGrID;
//设置影子GrID和原始GrID的样式一致。
this.WIDth = _orginGrID.ActualWIDth;
this.Height = _orginGrID.ActualHeight;
this.Text = _orginGrID.Text;
this.margin = _orginGrID.margin;
//设置影子GrID的父Panel并插入。
_vIndex = parentPanel.Children.IndexOf(_orginGrID);
_hIndex = parentPanel.Index;
parentPanel.Children.Insert(_vIndex + 1,this);
}而且这里影子控件和可拖拽控件互相引用,方便以后的 *** 作。
鼠标移动:
先判断IsDraging 标识位,之后根据鼠标的新位置设置控件的margin:
if (IsDraging)
{
Point newposition = e.Getposition(_parentGrID);
this.margin = new Thickness(newposition.X - _beginPoint.X,newposition.Y - _beginPoint.Y,_parentGrID.ActualWIDth - this.ActualWIDth - newposition.X + _beginPoint.X,_parentGrID.ActualHeight - this.ActualHeight - newposition.Y + _beginPoint.Y);
这里利用了鼠标相对控件的位移,也就是_beginPoint是使用不变的。
接下来就是最麻烦的部分:根据新位置,判断控件的拖拽状况,这里有左右移动和上下移动两种情况。
首先是比较简单的左右移动:
//向左移
if (this.margin.left < _parentGrID.PanelWIDth * (shadowGrID.HIndex - 0.5))
{
if (shadowGrID.HIndex > 0)
{
ContainerPanel panel = this._parentGrID.ChildPanels[shadowGrID.HIndex - 1];
shadowGrID.Moveto(panel);
}
}
//向右移
else if (this.margin.left > _parentGrID.PanelWIDth * (shadowGrID.HIndex + 0.5))
{
if (shadowGrID.HIndex < _parentGrID.PanelCount - 1)
{
ContainerPanel panel = this._parentGrID.ChildPanels[shadowGrID.HIndex + 1];
shadowGrID.Moveto(panel);
}
}_parentGrID.PanelWIDth代表每一列的宽度,而shadowGrID.HIndex则是shadowGrID所在列的序号,shadowGrID的Moveto方法实现了将shadowGrID水平插入另一个Panel中:
public voID Moveto(ContainerPanel panel)
{
ParentPanel.Children.Remove(this);
if (panel.Children.Count > _vIndex)
{
panel.Children.Insert(_vIndex,this);
}
else
{
_vIndex = panel.Children.Count;
panel.Children.Add(this);
}
this._hIndex = panel.Index;
}其中_vIndex代表shadowGrID在所在列中的位置。
之后则是比较复杂的上下移动:
//向上移
bool hasMove = false;
if (shadowGrID.VIndex > 0)
{
Control upControl = shadowGrID.ParentPanel.Children[shadowGrID.VIndex - 1] as Control;
Point upPoint = e.Getposition(upControl);
if ((upPoint.Y - _beginPoint.Y) < upControl.ActualHeight / 2)
{
shadowGrID.Moveto(shadowGrID.VIndex - 1);
hasMove = true;
}
}
//向下移
if (!hasMove && shadowGrID.VIndex < shadowGrID.ParentPanel.Children.Count - 1)
{
Control downControl = shadowGrID.ParentPanel.Children[shadowGrID.VIndex + 1] as Control;
Point downPoint = e.Getposition(downControl);
if ((_beginPoint.Y - downPoint.Y) < this.ActualHeight / 2)
{
shadowGrID.Moveto(shadowGrID.VIndex + 1);
}
}由于Silverlight中并没有像WPF那样提供直接获取两个控件相对位移的方法,而由于控件的大小未必固定,所以不能像左右移动那样根据Panel的宽度来计算是否移动。幸好我们可以利用鼠标的MouseEventArgs e来获取鼠标当前位置和其他控件的位移,从而间接算出两个控件的位置。shadowGrID的另一个重载的Moveto方法实现了将shadowGrID在一个Panel中从一个位置移到另一个位置:
public voID Moveto(int newVIndex)
{
ContainerPanel panel = ParentPanel;
panel.Children.Remove(this);
_vIndex = newVIndex;
panel.Children.Insert(_vIndex,this);
}鼠标左键抬起或离开(拖拽结束)
抬起和离开的逻辑是一样的:
voID DragableGrID_MouseLeave(object sender,MouseEventArgs e)
{
_parentGrID.Children.Remove(this);
RealeaseShadow();
IsDraging = false;
}public voID RealeaseShadow()
{
if (this.shadowGrID != null)
{
this.shadowGrID.Release();
}
this.shadowGrID = null;
}ShadowGrID的Release方法,将自己从父容器中移出,并将原始的可拖拽控件放入ShadowGrID所在的位置。
public voID Release()
{
this._orginGrID.margin = this.margin;
ContainerPanel panel = ParentPanel;
panel.Children.Remove(this);
panel.Children.Insert(_vIndex,_orginGrID);
}注意事项:
1.由于鼠标移动是一个很快的过程,MouseMove事件可能触发多次,如果移动过快,就可能出现运算不及,导致鼠标脱离可拖拽控件的情况,之后鼠标再返回控件,可能会出现问题。所以在一些地方,需要做一些看似多余的检测,比如RealeaseShadow方法中,检测shadowGrID 是否为空。
2.由于使用了StackPanel和GrID,里面的控件默认会采用Strech样式,即充满整个容器,对于Panel中的控件,就没有指定它的WIDth,所以这里用到的都是控件的ActualWIDth属性,也就是实际显示的宽度,这个不可设的属性会在控件Rend之后获得。
3.如果外层的GrID的大小会改变,那么就不能利用分别设置上下左右的margin来设置控件的位置,否则控件的大小也会随着GrID的大小而改变,那么就需要先确保控件的WIDth和Height有确定的值,然后设置控件的左对齐和上对齐,那么此时控件的margin-Right和Maring-Bottom无效,则只要通过指定margin-top,和margin-left,就可以对控件在GrID里做定位。
this.WIDth = this.ActualWIDth;
this.Height = this.ActualHeight;
this.VerticalAlignment = VerticalAlignment.top;
this.HorizontalAlignment = HorizontalAlignment.left;
//设置初始margin。
Thickness beginmargin = new Thickness(marginPoint.X - _beginPoint.X,0);
源代码下载:可拖拽Silverlight控件源码
REF:http://blog.csdn.net/tuoxie5431/archive/2009/03/01/3946278.aspx
总结以上是内存溢出为你收集整理的利用Silverlight实现类似iGoogle的浮动层拖拽效果全部内容,希望文章能够帮你解决利用Silverlight实现类似iGoogle的浮动层拖拽效果所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)