在前面三篇文章中我们介绍了如何给图形设计器增加移动、选择、改变大小及面板、缩略图、框线选择和工具箱和连接等功能,本篇是这个图形设计器系列的最后一篇,将和大家一起来学习一下如何给图形设计器增加分组、对齐、排序、序列化等功能。

WPF Diagram Designer - Part 4

分组:Group, Ungroup

由于WPF不允许一个对象作为多个其他元素的子对象存在,而当移动父对象时,模板也会Unload导致一些问题,所以在这个系列中对分组的实现方式是:当分组一组元素时,内部生成一个Group,这个Group内部其实也是一个DesignerItem对象,只是IsGroup=true而已,在分组时,内部的对象的ParentID都置为这个Group对象的Id

public interface IGroupable
{
Guid ID { get; }
Guid ParentID { get; set; }
bool IsGroup { get; set; }
}

执行分组时的代码如下:

private void Group_Executed(object sender, ExecutedRoutedEventArgs e)
{
var items = from item in this.SelectionService.CurrentSelection.OfType<DesignerItem>()
where item.ParentID == Guid.Empty
select item;

Rect rect = GetBoundingRectangle(items);

DesignerItem groupItem = new DesignerItem();
groupItem.IsGroup = true;
groupItem.Width = rect.Width;
groupItem.Height = rect.Height;
Canvas.SetLeft(groupItem, rect.Left);
Canvas.SetTop(groupItem, rect.Top);
Canvas groupCanvas = new Canvas();
groupItem.Content = groupCanvas;
Canvas.SetZIndex(groupItem, this.Children.Count);
this.Children.Add(groupItem);

foreach (DesignerItem item in items)
item.ParentID = groupItem.ID;

this.SelectionService.SelectItem(groupItem);
}

当我们选择一个分组子对象时,设计器会选择这个分组以及分组的所有子对象

internal void SelectItem(ISelectable item)
{
this.ClearSelection();
this.AddToSelection(item);
}

internal void AddToSelection(ISelectable item)
{
if (item is IGroupable)
{
List<IGroupable> groupItems = GetGroupMembers(item as IGroupable);

foreach (ISelectable groupItem in groupItems)
{
groupItem.IsSelected = true;
CurrentSelection.Add(groupItem);
}
}
else
{
item.IsSelected = true;
CurrentSelection.Add(item);
}
}

对齐:Align (Left, Right, Top, Bottom, Centered horizontal, Centered vertical)、Distribute (horizontal, vertical)

private void AlignLeft_Executed(object sender, ExecutedRoutedEventArgs e)
{
var selectedItems = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
where item.ParentID == Guid.Empty
select item;

if (selectedItems.Count() > 1)
{
double left = Canvas.GetLeft(selectedItems.First());

foreach (DesignerItem item in selectedItems)
{
double delta = left - Canvas.GetLeft(item);
foreach (DesignerItem di in SelectionService.GetGroupMembers(item))
{
Canvas.SetLeft(di, Canvas.GetLeft(di) + delta);
}
}
}
}

private void AlignHorizontalCenters_Executed(object sender, ExecutedRoutedEventArgs e)
{
var selectedItems = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
where item.ParentID == Guid.Empty
select item;

if (selectedItems.Count() > 1)
{
double center = Canvas.GetLeft(selectedItems.First()) + selectedItems.First().Width / 2;

foreach (DesignerItem item in selectedItems)
{
double delta = center - (Canvas.GetLeft(item) + item.Width / 2);
foreach (DesignerItem di in SelectionService.GetGroupMembers(item))
{
Canvas.SetLeft(di, Canvas.GetLeft(di) + delta);
}
}
}
}

排序:Order (Bring forward, Bring to top, Send backward, Send to back)

private void BringForward_Executed(object sender, ExecutedRoutedEventArgs e)
{
List<UIElement> ordered = (from item in SelectionService.CurrentSelection
orderby Canvas.GetZIndex(item as UIElement) descending
select item as UIElement).ToList();

int count = this.Children.Count;

for (int i = 0; i < ordered.Count; i++)
{
int currentIndex = Canvas.GetZIndex(ordered[i]);
int newIndex = Math.Min(count - 1 - i, currentIndex + 1);
if (currentIndex != newIndex)
{
Canvas.SetZIndex(ordered[i], newIndex);
IEnumerable<UIElement> it = this.Children.OfType<UIElement>().Where(item => Canvas.GetZIndex(item) == newIndex);

foreach (UIElement elm in it)
{
if (elm != ordered[i])
{
Canvas.SetZIndex(elm, currentIndex);
break;
}
}
}
}
}

序列化:Open, Save

使用XML保存,代码如下:

XElement serializedItems = new XElement("DesignerItems",
from item in designerItems
let contentXaml = XamlWriter.Save(((DesignerItem)item).Content)
select new XElement("DesignerItem",
new XElement("Left", Canvas.GetLeft(item)),
new XElement("Top", Canvas.GetTop(item)),
new XElement("Width", item.Width),
new XElement("Height", item.Height),
new XElement("ID", item.ID),
new XElement("zIndex", Canvas.GetZIndex(item)),
new XElement("IsGroup", item.IsGroup),
new XElement("ParentID", item.ParentID),
new XElement("Content", contentXaml)
)
);

读取的时候需要建立Connection

private void Open_Executed(object sender, ExecutedRoutedEventArgs e)
{
       ... 
foreach (XElement connectionXML in connectionsXML)
{
Guid sourceID = new Guid(connectionXML.Element("SourceID").Value);
Guid sinkID = new Guid(connectionXML.Element("SinkID").Value);

String sourceConnectorName = connectionXML.Element("SourceConnectorName").Value;
String sinkConnectorName = connectionXML.Element("SinkConnectorName").Value;

Connector sourceConnector = GetConnector(sourceID, sourceConnectorName);
Connector sinkConnector = GetConnector(sinkID, sinkConnectorName);

Connection connection = new Connection(sourceConnector, sinkConnector);
Canvas.SetZIndex(connection, Int32.Parse(connectionXML.Element("zIndex").Value));
this.Children.Add(connection);
}
}

常用功能:Cut, Copy, Paste, Delete,Print

private void Cut_Executed(object sender, ExecutedRoutedEventArgs e)
{
CopyCurrentSelection();
DeleteCurrentSelection();
}
private void Print_Executed(object sender, ExecutedRoutedEventArgs e)
{
SelectionService.ClearSelection();

PrintDialog printDialog = new PrintDialog();

if (true == printDialog.ShowDialog())
{
printDialog.PrintVisual(this, "WPF Diagram");
}
}

本文转自 jingen_zhou 51CTO博客,原文链接:http://blog.51cto.com/zhoujg/517448,如需转载请自行联系原作者

WPF:从WPF Diagram Designer Part 4学习分组、对齐、排序、序列化和常用功能相关推荐

  1. WPF:从WPF Diagram Designer Part 1学习控件模板、移动、改变大小和旋转

    欢迎转载,转载请注明:转载自周金根 [ http://zhoujg.cnblogs.com/ ] 由于上周主要做了项目组产品架构.给公司新员工培训以及其他会议等事情,在OpenExpressApp对建 ...

  2. 菜鸟生信学习第三节笔记:plink常用功能

    plink由哈佛大学的Shaun Purcell开发的一个免费,开源的全基因组关联分析软件. 官网:PLINK 1.9 主要功能: 1.数据提取,合并.提取特定SNP.样本.基因组某段区域的基因型3. ...

  3. 太极计算机ehr系统,(数据科学学习手札21)sklearn.datasets常用功能详解

    作为Python中经典的机器学习模块,sklearn围绕着机器学习提供了很多可直接调用的机器学习算法以及很多经典的数据集,本文就对sklearn中专门用来得到已有或自定义数据集的datasets模块进 ...

  4. VUE学习(六) 高德地图常用功能总结

    项目中使用地图的场景比较多,将常用的功能整理一下,方便后期使用. 目录 1.地图的引入 2.地图在页面显示 3.点标记 3.1快速点标记 3.2点标记详细配置 4.圆形.方形.多边形标记 4.1创建圆 ...

  5. FineReport学习-【02 帆软报表常用功能】

    概览 添加汇总标签.添加公式 这样出来的合计是总合计 设置合计以地区为标准 父子格 纵向扩展 地区-销售员默认为分组格式 设置销售员左父格为无之后 横向扩展 设置地区.销售扩展方向为横向 设置销售的上 ...

  6. WPF - 图形设计器(Diagram Designer)

    WPF Diagram Designer: Part 1 Drag resize rotate WPF Diagram Designer - Part 2 设计面板(Designer Canvas : ...

  7. web前端学习526-534(变量概述,变量的使用,变量语法扩展,变量命名规范,推荐Diagram Designer)

    文章目录 1 变量概述 1.1 什么是变量 2 变量的使用 1 声明变量 2 赋值 3 变量的初始化 案例:变量的使用 3 变量语法扩展 1 更新变量 2 同时声明多个变量 3 声明变量特殊情况 4 ...

  8. DispatcherCore ,一个WPF异步操作常用功能库

    在WPF开发中,经常遇到跨线程的问题,以及频繁使用跨线程操作UI线程中的界面元素,一些COM组件操作也是必须在UI主线程中使用,否则就会抛出各种无法访问的错误.是否有遇到过呢?为了解决各种跨线程访问的 ...

  9. 绘图软件推荐——Diagram Designer

    目录 Diagram Designer安装 软件下载 软件图标 Diagram Designer应用 新建页面 工具栏简介 绘制多边形 创建并添加图形模板 图像导出 Diagram Designer安 ...

最新文章

  1. 在实施OKR之前,你必须先了解这7点
  2. 如何在JAVA代码中执行 exec master..xp_cmdshell @cmd // 当作SQL语句调用就成了 或者调用 Runtime.getRuntime().exec
  3. Android相关面试题---初识
  4. 解决使用个推后背景音乐音量变小问题
  5. 非凸函数上,随机梯度下降能否收敛?能,但有条件,且比凸函数收敛更难
  6. [Abp 源码分析]多租户体系与权限验证
  7. 3481. 阶乘的和
  8. 如果编程语言是超级英雄……
  9. 百度Java三面:现场面试39题目实拍含答案!
  10. java 打包后 文件资源文件 jar,JAVA打包成JAR无法找到资源文件
  11. mysql文件结构_MySQL文件结构
  12. python3入门代码-python3爬虫入门程序
  13. Emacs之快捷键大全
  14. Python返回Json格式定义的例子
  15. 瀑布模型、快速原型模型、增量模型、螺旋模型、喷泉模型
  16. 古体字与简体字对照表_简体字与繁体字对照表大全.pdf
  17. 【周总结】博客第一周小结SSL暑假训练第二周小结
  18. 创建选区快捷键是什么_PS如何移动和取消选区?快捷键是什么? - PS自学网
  19. 【独家】一文读懂文字识别(OCR)
  20. 华为 MA5800设备防盗

热门文章

  1. java怎么获取当前日期_JAVA中获取当前系统时间
  2. 2013_changchun_online
  3. 数据库原理及应用【三】DBMS+SQL
  4. C——通过调用函数分配内存
  5. 1080 MOOC期终成绩 (25 分)
  6. Leetcode | 107. Binary Tree Level Order Traversal II
  7. oppoJava面试题,java声明全局变量的关键字
  8. 面试加分项!程序员工作2年月薪12K,附架构师必备技术详解
  9. c语言 大雨 班上多个同学准备,2015年计算机二级考试《C语言》提高练习题(7)
  10. hdfs的特性、命令、安全模式、基准测试