DiagramDesigner的学习心得一
2013-01-06 15:10 爱研究源码的javaer 阅读(907) 评论(1) 编辑 收藏 举报DiagramDesigner是CodeProject上关于WPF的控件模板,移动拖放,改变控件大小,旋转的很好的文章。
在博客园博主周金根的博客上也有相应的介绍。它总共分四部分,每部分都循序渐进。
首先我们来讲第一部分,关于控件的移动,和改变大小,MoveAndResize.
MoveResize项目运行起来的效果如下:
在MoveResize项目的window1.xaml中:
<ContentControl Width="130" MinWidth="50" Height="130" MinHeight="50" Canvas.Top="150" Canvas.Left="470" Template="{StaticResource DesignerItemTemplate}"> <Ellipse Fill="Red" IsHitTestVisible="False"/> </ContentControl> <ContentControl Width="130" MinWidth="50" Height="130" MinHeight="50" Canvas.Top="150" Canvas.Left="150" Template="{StaticResource DesignerItemTemplate}"> <Path Fill="Blue" Data="M 0,5 5,0 10,5 5,10 Z" Stretch="Fill" IsHitTestVisible="False"/> </ContentControl>
这两个ContentControl控件一个是左边的菱形,另一个则是一个圆形。样式都采用了DesignerItemTemplate.
<!-- Designer Item Template--> <ControlTemplate x:Key="DesignerItemTemplate" TargetType="ContentControl"> <Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"> <s:MoveThumb Template="{StaticResource MoveThumbTemplate}" Cursor="SizeAll"/> <Control Template="{StaticResource ResizeDecoratorTemplate}"/> <ContentPresenter Content="{TemplateBinding ContentControl.Content}"/> </Grid> </ControlTemplate>
对上面的两段xaml里的TemplateBinding和ContentPresenter我们可以引用周金根博主里面的解释:
- 限制目标类型
ControlTemplate和Style一样,也有一个TargetType属性来限制模板可以被应用到的类型上,如果没有一个显示的TargetType,则目标类型将被隐式的设置为Control。由于没有默认的控件模板,所以它与Style是不同的,当使用TargetType时不允许移除模板的x:Key。 - 模板绑定TemplateBinding
在控件模板中,从目标元素插入属性值的关键是数据绑定,我们可以通过一个简单、轻量级的模板绑定TemplateBinding来处理。TemplateBinding的数据源总是目标元素,而Path则是目标元素的任何一个依赖属性。使用方式如上例的{TemplateBinding ContentControl.Content},如果我们设置了TargetType,可以更简单的使用为{TemplateBinding Content}
TemplateBinding仅仅是一个便捷的设置模板绑定的机制,对于有些可冻结的属性(如Brush的Color属性)时绑定会失败,这时候我们可以使用常规的Binding来达到同样效果,通过使用一个RelativeSource,其值为{Relative Source TemplatedParent}以及一个Path。 - ContentPresenter
在控件模板中应该使用轻量级的内容显示元素ContentPresenter,而不是ContentControl。ContentPresenter显示的内容和ContentControl是一样的,但是ContentControl是一个带有控件模板的成熟控件,其内部包含了ContentPresenter。
如果我们在使用ContentPresenter时忘记了将它的Content设置为{TemplateBinding Content}时,它将隐式的假设{TemplateBinding Content}就是我们需要的内容 - 与触发器交互
在模板内部可以使用触发器,但是在进行绑定时需要注意只能使用Binding,因为触发器位于控件可视树模板外部
我们看到在DesignerItemTemplate里还有两个MoveThumbTemplate 和 ResizeDecoratorTemplate .
MoveThumbTemplate其实就是一个Rectangle搞定。我们把它的Fill改成Blue看下:
ResizeDecoratorTemplate就是四个角上的
我们来看看ResizeThumb这个类是怎么实现的:
public class ResizeThumb : Thumb { public ResizeThumb() { //当有逻辑焦点或鼠标捕获时,随着鼠标位置改变一次或多次 DragDelta += new DragDeltaEventHandler(this.ResizeThumb_DragDelta); } private void ResizeThumb_DragDelta(object sender, DragDeltaEventArgs e) { Control designerItem = this.DataContext as Control;//获取父控件,通过DataContext if (designerItem != null) { double deltaVertical, deltaHorizontal; switch (VerticalAlignment) { case VerticalAlignment.Bottom: deltaVertical = Math.Min(-e.VerticalChange, designerItem.ActualHeight - designerItem.MinHeight);//计算减小的高度 designerItem.Height -= deltaVertical;//减小高度 break; case VerticalAlignment.Top: deltaVertical = Math.Min(e.VerticalChange, designerItem.ActualHeight - designerItem.MinHeight); Canvas.SetTop(designerItem, Canvas.GetTop(designerItem) + deltaVertical);//重新设置相对于相对于Canvas的上边距 designerItem.Height -= deltaVertical; break; default: break; } switch (HorizontalAlignment) { case HorizontalAlignment.Left: deltaHorizontal = Math.Min(e.HorizontalChange, designerItem.ActualWidth - designerItem.MinWidth); Canvas.SetLeft(designerItem, Canvas.GetLeft(designerItem) + deltaHorizontal);//重新设置相对于Canvas的左边距 designerItem.Width -= deltaHorizontal;//减小宽度 break; case HorizontalAlignment.Right: deltaHorizontal = Math.Min(-e.HorizontalChange, designerItem.ActualWidth - designerItem.MinWidth); designerItem.Width -= deltaHorizontal; break; default: break; } } e.Handled = true; } }
MoveThumbTemplate是基于MoveThumb类的:
public class MoveThumb : Thumb { public MoveThumb() { DragDelta += new DragDeltaEventHandler(this.MoveThumb_DragDelta); } private void MoveThumb_DragDelta(object sender, DragDeltaEventArgs e) { Control designerItem = this.DataContext as Control;//获取父控件,通过DataContext if (designerItem != null) { double left = Canvas.GetLeft(designerItem);//获取现在相对于Canvas画布的左边距 double top = Canvas.GetTop(designerItem);//获取现在相对于Canvas画布的上边距 Canvas.SetLeft(designerItem, left + e.HorizontalChange);//e.HorizontalChange 水平改变大小,有可能为负 Canvas.SetTop(designerItem, top + e.VerticalChange);//e.VerticalChange 垂直改变大小,有可能为负 } } }
下篇我们来分析下旋转 还有 根据Adorner来怎么实现。