运行结果图:
点击左上角图框的Here之后,跳转:
进来需要制作一个显示土地流转信息的部件,一开始利用华丽的Dev控件,GarreryControl,效果自认为不错,拿给领导看后遭到痛批。由于本来水平有限,加之美工图标的不给力,我认了。只好自己从头来做,换一种思路。
在网上找到一个Demo,认真研读后,找到了方向:XAML加C#的Canvas控件,接下来就开始动手吧:
首先,我设计了自己的思路,Dia做出UML图,即整体框架:
第一次做UML图,和最终的结构还是有很大差距,比如类与类之间的嵌套关系不清,点击事件由StatusCanvas传出到最外层,即调用LayoutElement的函数中的事件,一开始做就没有考虑到,等到快做完时才亡羊补牢,好歹是做完了。
好了,UML设计完成后,开始代码的编辑:
首先是保存信息的图框,我使用的是Rectangle和TextBlock相互配合的方式,传值只需要传入图框的中心位置点位坐标即可。到编码结束时,还考虑到了,如果做不出来动态效果,客户必然不好发现那一个是他之前点击选择的那个框,所以我设计了让他将当前选择的框体边缘加粗显示,代码如下:
//状态框
public partial class StatusCanvas : Canvas
{
RectangleGeometry rectangle;
TextBlock textBlock;
public TextBlock LinkBlock;
Point topLeft;
public double RectangleWidth;
public double RectangleHeight;
public define.fieldInfo fieldInfo;
public delegate void ClickDelegate(string strSomeWhenRegionCodeOut, int intAfterTurnStageOut);
public event ClickDelegate ClickEvent;
public int StrokeThickness;
public StatusCanvas(Point topCentrolIn, double RectangleWidthIn, double RectangleHeightIn, define.fieldInfo fieldInfoIn, FindPareOrChild PareOrChild)
{
topLeft.X = topCentrolIn.X - RectangleWidthIn / 2;
topLeft.Y = topCentrolIn.Y - RectangleHeightIn / 2;
RectangleWidth = RectangleWidthIn;
RectangleHeight = RectangleHeightIn;
fieldInfo = fieldInfoIn;
if (PareOrChild == FindPareOrChild.FindSelf)
{
StrokeThickness = 4;
}
else
{
StrokeThickness = 1;
}
//图形框
double x = topLeft.X;
double y = topLeft.Y;
rectangle = new RectangleGeometry(new Rect(x, y, RectangleWidth, RectangleHeight), 5, 5);
LinearGradientBrush b3 = new LinearGradientBrush(Colors.Yellow, Colors.Black,100);
System.Windows.Shapes.Path myPath = new System.Windows.Shapes.Path();
myPath.Fill = Brushes.LemonChiffon;
myPath.Stroke = b3;
myPath.StrokeThickness = StrokeThickness;
myPath.Data = rectangle;
this.Children.Add(myPath);
//文本框
textBlock = new TextBlock();
textBlock.TextWrapping = TextWrapping.WrapWithOverflow;
textBlock.MaxWidth = RectangleWidth - 15;
textBlock.MaxHeight = RectangleHeight - 10;
{
textBlock.Inlines.Add(new Bold(new Run("\r宗地名称:")));
textBlock.Inlines.Add(new Run(fieldInfo.strFieldName));
textBlock.Inlines.Add(new Bold(new Run("\r现承包方名称:")));
textBlock.Inlines.Add(new Run(fieldInfo.strNowCBFName));
textBlock.Inlines.Add(new Bold(new Run("\r现宗地号:")));
textBlock.Inlines.Add(new Run(fieldInfo.strOriginalCde));
textBlock.Inlines.Add(new Bold(new Run("\r宗地东至:")));
textBlock.Inlines.Add(new Run(fieldInfo.strToEast));
textBlock.Inlines.Add(new Bold(new Run("\r宗地西至:")));
textBlock.Inlines.Add(new Run(fieldInfo.strToSouth));
textBlock.Inlines.Add(new Bold(new Run("\r宗地南至:")));
textBlock.Inlines.Add(new Run(fieldInfo.strToWest));
textBlock.Inlines.Add(new Bold(new Run("\r宗地北至:")));
textBlock.Inlines.Add(new Run(fieldInfo.strToNorth));
}
Canvas.SetLeft(textBlock, x + 10);
Canvas.SetTop(textBlock, y + 5);
this.Children.Add(textBlock);
LinkBlock = new TextBlock();
LinkBlock.Foreground = Brushes.Blue;
LinkBlock.Inlines.Add(new Underline(new Run("Here")));
Canvas.SetLeft(LinkBlock, x + RectangleWidthIn - 30);
Canvas.SetTop(LinkBlock, y + RectangleHeightIn - 20);
LinkBlock.Focusable = true;
this.Children.Add(LinkBlock);
LinkBlock.MouseUp += new MouseButtonEventHandler(LinkBlock_MouseUp);
}
public RectangleGeometry _rectangle()
{
return rectangle;
}
public TextBlock _textBlock()
{
return textBlock;
}
private void LinkBlock_MouseUp(object sender, MouseButtonEventArgs e)
{
ClickEvent(fieldInfo.strOriginalCde, fieldInfo.strToStageNum);
}
}
其次我设计连接两个状态框之间的箭头,传入参数一样很简单,只需要起始的点位和终止点位的坐标信息即可:
//箭头
public partial class ProcessCanvas : Canvas
{
Point BeginPoint;
Point EndPoint;
Line myLineGeo;
Line myEndLine;
public ProcessCanvas(Point Begin,Point end)
{
BeginPoint = Begin;
EndPoint = end;
initLine();
}
public void initLine()
{
double slope = (BeginPoint.X - EndPoint.X) / (BeginPoint.Y - EndPoint.Y);
myLineGeo = new Line();
myLineGeo.Stroke = Brushes.Blue;
myLineGeo.StrokeThickness = 2;
myLineGeo.X1 = BeginPoint.X;
myLineGeo.Y1 = BeginPoint.Y;
myLineGeo.X2 = EndPoint.X - 3 * slope;
myLineGeo.Y2 = EndPoint.Y - 3;
myEndLine = new Line();
myEndLine.Stroke = Brushes.Blue;
myEndLine.StrokeThickness = 15;
myEndLine.X1 = EndPoint.X - 3 * slope;
myEndLine.Y1 = EndPoint.Y - 3;
myEndLine.X2 = EndPoint.X;
myEndLine.Y2 = EndPoint.Y;
myEndLine.StrokeEndLineCap = PenLineCap.Triangle;
this.Children.Add(myLineGeo);
this.Children.Add(myEndLine);
}
}
接下来是做主要的画布类,上面放置了该节点(也就是Focuse节点)的信息,还有parents节点和children节点的信息:
//画布
public partial class LayoutElement : Canvas
{
private LayoutElement ParentElement;
private LayoutElement ChildrenElement;
private string strRegCodeNow;
private string strRegCodeOrig;
private int intAfterTurnStage;
define.fieldInfo FieldInfo;
double m_horizantalGap;
double m_verticalGap;
double m_RectangleHeight;
double m_RectangleWidth;
StatusCanvas focusedStatusCanvas;//状态框
ProcessCanvas processCanvas;//流转箭头
public delegate void ClickDelegate(string strSomeWhenRegionCodeOut, int intAfterTurnStageOut);
public event ClickDelegate ClickEvent;
//构造函数。focuse节点
public LayoutElement(string RegCodeNowIn,string RegCodeOrigIn,int AfterTurnStageIn)
{
initLayoutElement( RegCodeNowIn, RegCodeOrigIn, AfterTurnStageIn);
}
//初始化
private void initLayoutElement(string RegCodeNowIn, string RegCodeOrigIn, int AfterTurnStageIn)
{
strRegCodeNow = RegCodeNowIn;
//strRegCodeOrig = RegCodeOrigIn;
intAfterTurnStage = AfterTurnStageIn;
ArrayList SomeWhenParentsStatus = getSomeWhenStatus(FindPareOrChild.FindParents);
ArrayList SomeWhenChildrenStatus = getSomeWhenStatus(FindPareOrChild.FindChildren);
Point centrol = setComposing( SomeWhenChildrenStatus.Count,SomeWhenParentsStatus.Count);
FieldInfo = getTurnInfoByLandCode(strRegCodeNow, intAfterTurnStage);
focusedStatusCanvas = new StatusCanvas(centrol, m_RectangleWidth, m_RectangleHeight, FieldInfo,FindPareOrChild.FindSelf);
focusedStatusCanvas.Focusable = true;
focusedStatusCanvas.ForceCursor = true;
focusedStatusCanvas.ClickEvent += new StatusCanvas.ClickDelegate(focusedStatusCanvas_ClickEvent);
this.Children.Add(focusedStatusCanvas);
InitParents(centrol, SomeWhenParentsStatus);
InitChildren(centrol, SomeWhenChildrenStatus);
}
//构造函数。父子节点
public LayoutElement(Point tCentrolPointIn, define.fieldInfo FieldInfoIn, double RectangleWidth, double RectangleHeight, Point ArrowEndPoint, FindPareOrChild findPareOrChild)
{
Point tCentrolPoint = tCentrolPointIn;
FieldInfo = FieldInfoIn;
m_RectangleWidth = RectangleWidth;
m_RectangleHeight = RectangleHeight;
focusedStatusCanvas = new StatusCanvas(tCentrolPoint, m_RectangleWidth, m_RectangleHeight, FieldInfo, findPareOrChild);
this.Children.Add(focusedStatusCanvas);
if (tCentrolPoint.Y > ArrowEndPoint.Y)
{
processCanvas = new ProcessCanvas(ArrowEndPoint, new Point((tCentrolPoint.X), (tCentrolPoint.Y - RectangleHeight / 2)));
}
else
{
processCanvas = new ProcessCanvas(new Point((tCentrolPoint.X), (tCentrolPoint.Y + RectangleHeight / 2)), ArrowEndPoint);
}
focusedStatusCanvas.ClickEvent += new StatusCanvas.ClickDelegate(focusedStatusCanvas_ClickEvent);
this.Children.Add(processCanvas);
}
//设置排版信息中心点
public Point setComposing(int ChildrenCount,int ParentsCount)
{
int maxNum;
Point centrol;
if (ChildrenCount > ParentsCount)
{
maxNum =ChildrenCount;
}
else
{
maxNum =ParentsCount;
}
m_horizantalGap = 50;
m_verticalGap = 80;
m_RectangleHeight = 190;
m_RectangleWidth = 180;
if (maxNum == 0)
{
this.Width = m_RectangleWidth;
}
else
{
this.Width = maxNum * (m_RectangleWidth + m_horizantalGap) - m_horizantalGap;
}
//无父节点,整体上移
if (ParentsCount <= 0)
{
this.Height = m_RectangleHeight * 2 + m_verticalGap;
centrol = new Point((this.Width / 2), (m_RectangleHeight/2));
}
else if (ChildrenCount <= 0)
{
this.Height = m_RectangleHeight * 2 + m_verticalGap;
centrol = new Point((this.Width / 2), (this.Height - m_RectangleHeight * 0.5));
}
else
{
this.Height = m_RectangleHeight * 3 + m_verticalGap * 2;
centrol = new Point((this.Width / 2), (this.Height / 2));
}
return centrol;
}
//初始化父节点树
private void InitParents(Point centrol, ArrayList SomeWhenParentsStatus)
{
double StatusCount = SomeWhenParentsStatus.Count;
double couLeftMax = ((StatusCount - 1) / 2) * (m_horizantalGap + m_RectangleWidth);
for (int ii = 0; ii < StatusCount; ii++)
{
double centrolPositionX = centrol.X-couLeftMax + ii * (m_horizantalGap + m_RectangleWidth);
double centrolPositionY = centrol.Y - (m_verticalGap + m_RectangleHeight);
ParentElement = new LayoutElement(new Point(centrolPositionX, centrolPositionY), (define.fieldInfo)SomeWhenParentsStatus[ii], m_RectangleWidth, m_RectangleHeight, new Point((centrol.X), (centrol.Y - m_RectangleHeight / 2)),FindPareOrChild.FindParents);
ParentElement.ClickEvent += new ClickDelegate(focusedStatusCanvas_ClickEvent);
this.Children.Add(ParentElement);
}
}
//初始化子节点树
private void InitChildren(Point centrol, ArrayList SomeWhenChildrenStatus)
{
double StatusCount = SomeWhenChildrenStatus.Count;
double couLeftMax = ((StatusCount - 1) / 2) * (m_horizantalGap + m_RectangleWidth);
for (int ii = 0; ii < StatusCount; ii++)
{
double centrolPositionX = centrol.X - couLeftMax + ii * (m_horizantalGap + m_RectangleWidth);
double centrolPositionY = centrol.Y + (m_verticalGap + m_RectangleHeight);
ChildrenElement = new LayoutElement(new Point(centrolPositionX, centrolPositionY), (define.fieldInfo)SomeWhenChildrenStatus[ii], m_RectangleWidth, m_RectangleHeight, new Point((centrol.X), (centrol.Y + m_RectangleHeight / 2)),FindPareOrChild.FindChildren);
ChildrenElement.ClickEvent += new ClickDelegate(focusedStatusCanvas_ClickEvent);
this.Children.Add(ChildrenElement);
}
}
//获取当前宗地的父节点或子节点的信息
private ArrayList getSomeWhenStatus(FindPareOrChild findPareOrChild)
{
ArrayList SomeWhenStatus=new ArrayList();
ArrayList SomeWhenStatusCode = new ArrayList();
SomeWhenStatusCode = getSomeWhenCode(findPareOrChild);
foreach (string strSomeWhenStatusCode in SomeWhenStatusCode)
{
if (findPareOrChild == FindPareOrChild.FindChildren)
{
int intAfterTurn = intAfterTurnStage + 1;
if (intAfterTurn == 100000001)
intAfterTurn = 1;
SomeWhenStatus.Add(getTurnInfoByLandCode(strSomeWhenStatusCode, intAfterTurn));
}
else
{
//通过to_stated来进行判断,当to_stated为0时,自动转成100000000
int intAfterTurn=intAfterTurnStage - 1;
if (intAfterTurn == 0)
intAfterTurn = 100000000;
SomeWhenStatus.Add(getTurnInfoByLandCode(strSomeWhenStatusCode, intAfterTurn));
}
}
return SomeWhenStatus;
}
//获取父节点或子节点的编码列表
private ArrayList getSomeWhenCode(FindPareOrChild findPareOrChild)
{
ArrayList SomeWhenStatusCode = new ArrayList();
ContractDataConnection contractDataConnection = ContractDataConnection.getInstance();
//select ZDBM from CB_BG_LAND where BGQZDBM ='42068311200501015J0018' AND YZDBM='42068311200501015J0018' AND BGID='1'
string strSQL = "";
if (findPareOrChild == FindPareOrChild.FindChildren)
{
//为初始节点时
int intAfterTurn = intAfterTurnStage + 1;
if (intAfterTurnStage == 100000000)
intAfterTurn = 1;
strSQL = "select ZDBM as result from CB_BG_LAND where BGQZDBM = \'" + strRegCodeNow.ToString() + "\' AND BGID=\'" + (intAfterTurn).ToString() + "\'";
}
else
{
strSQL = "select BGQZDBM as result from CB_BG_LAND where ZDBM = \'" + strRegCodeNow.ToString() + "\' AND BGID=\'" + intAfterTurnStage.ToString() + "\'";
}
OleDbCommand oleDbCommand = new OleDbCommand(strSQL, contractDataConnection.DbConnection);
OleDbDataAdapter oleDbDataAdapter = new OleDbDataAdapter(oleDbCommand);
DataTable landMaxTable = new DataTable();
try
{
oleDbDataAdapter.Fill(landMaxTable);
}
catch (System.Exception ex)
{
XtraMessageBox.Show(ex.ToString());
contractDataConnection.Close();
}
if (landMaxTable.Rows.Count <= 0)
{
contractDataConnection.Close();
}
foreach (DataRow dr in landMaxTable.Rows)
{
SomeWhenStatusCode.Add(dr["result"].ToString());
}
return SomeWhenStatusCode;
}
//获取Land表节点信息,通过to_stated来进行判断
private define.fieldInfo getTurnInfoByLandCode(string strFieldCode, int turnStage)
{
define.fieldInfo fieldInfo = new Contract.Archive.define.fieldInfo();
fieldInfo.strOriginalCde = "";
ContractDataConnection contractDataConnection = ContractDataConnection.getInstance();
//SELECT * FROM CB_LAND where ZDBM ="42068300401402009J0001" and FROM_STATEID=0
string strSQL;
//if (turnStage == 0)
//{
// strSQL = "select * from " + Contract.Data.ContractDefine.TABLE_CB_LAND + " where ZDBM = '" + strFieldCode + "' and FROM_STATEID=" + turnStage;
//}
//else
//{
strSQL="select * from " + Contract.Data.ContractDefine.TABLE_CB_LAND + " where ZDBM = '" + strFieldCode + "' and TO_STATEID=" + turnStage;
//}
OleDbCommand oleDbCommand = new OleDbCommand(strSQL, contractDataConnection.DbConnection);
OleDbDataAdapter oleDbDataAdapter = new OleDbDataAdapter(oleDbCommand);
DataTable landMaxTable = new DataTable();
try
{
oleDbDataAdapter.Fill(landMaxTable);
}
catch (System.Exception ex)
{
XtraMessageBox.Show(ex.ToString());
contractDataConnection.Close();
return fieldInfo;
}
if (landMaxTable.Rows.Count <= 0)
{
contractDataConnection.Close();
return fieldInfo;
}
fieldInfo.strNowCBFName = CBFCodeToName(landMaxTable.Rows[0]["BFDBZJHM"].ToString());
fieldInfo.strOriginalCde = landMaxTable.Rows[0]["ZDBM"].ToString();
fieldInfo.strFieldName = landMaxTable.Rows[0]["ZDMC"].ToString();//宗地名称ZDMC
fieldInfo.strRegionCode = landMaxTable.Rows[0]["XZQDM"].ToString();//行政区代码XZQDM
fieldInfo.strContractType = landMaxTable.Rows[0]["CBFS"].ToString();//承包方式CBFS
fieldInfo.strToEast = landMaxTable.Rows[0]["ZDDZ"].ToString();//东至ZDDZ
fieldInfo.strToWest = landMaxTable.Rows[0]["ZDXZ"].ToString();//西至ZDXZ
fieldInfo.strToNorth = landMaxTable.Rows[0]["ZDBZ"].ToString();//北至ZDBZ
fieldInfo.strToSouth = landMaxTable.Rows[0]["ZDNZ"].ToString();//南至ZDNZ
fieldInfo.strToStageNum = turnStage;
return fieldInfo;
}
//获取承包人姓名
private string CBFCodeToName(string cbfCode)
{
Contract.Data.CBaseOleDb OleDb = new CBaseOleDb(Contract.Data.ContractDefine.TABLE_CB_CBF);
string strSQL = " BFDBZJHM =\"" + cbfCode + "\"";
DataRow[] dtRow = new DataRow[1];
try
{
dtRow = OleDb.GetDataRow(strSQL, true);
}
catch (Exception ex)
{
XtraMessageBox.Show(ex.ToString());
OleDb.Close();
return null;
}
OleDb.Close();
string regionName;
if (dtRow == null || dtRow.Count() <= 0)
return "";
else
{
regionName = dtRow[0]["CBFDBXM"].ToString();
return regionName;
}
}
//单击节点时
public void focusedStatusCanvas_ClickEvent(string strSomeWhenRegionCodeOut, int intAfterTurnStageOut)
{
ClickEvent( strSomeWhenRegionCodeOut, intAfterTurnStageOut);
}
}
设计一个Design类来使用这个画布:
public class Designer : Canvas
{
public Designer()
{
}
public void DesignerSetUp(string strSomeWhenRegionCodeOut, int intAfterTurnStageOut)
{
lye = null;
lye = new LayoutElement(strSomeWhenRegionCodeOut, "", intAfterTurnStageOut);
lye.ClickEvent += new LayoutElement.ClickDelegate(lye_ClickEvent);
this.Children.Add(lye);
this.Width = lye.Width;
this.Height = lye.Height;
}
LayoutElement lye;
public string strSomeWhenRegionCodeOut;
public int intAfterTurnStageOut;
private void lye_ClickEvent(string strSomeWhenRegionCodeOut, int intAfterTurnStageOut)
{
this.Children.Clear();
lye = new LayoutElement(strSomeWhenRegionCodeOut, "", intAfterTurnStageOut);
lye.ClickEvent += new LayoutElement.ClickDelegate(lye_ClickEvent);
this.Children.Add(lye);
this.Width = lye.Width;
this.Height = lye.Height;
}
}
接下来是XAML语言,做一个WPF的UserControl控件,就可以将它拖放到你想要的窗体上使用了:
xaml语言:
<UserControl x:Class="Contract.Archive.UCCanvas"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="clr-namespace:Contract.Archive"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="295*" />
<RowDefinition Height="16*" />
</Grid.RowDefinitions>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto" Grid.RowSpan="2" CanContentScroll="False">
<s:Designer Focusable="true" x:Name="MyDesigner"
Margin="10" FocusVisualStyle="{x:Null}" ></s:Designer>
</ScrollViewer>
</Grid>
</UserControl>
还有和它一起的XAML。CS的代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Contract.Archive
{
/// <summary>
/// UCCanvas.xaml 的交互逻辑
/// </summary>
public partial class UCCanvas : UserControl
{
public UCCanvas(string strSomeWhenRegionCodeOut, int intAfterTurnStageOut)
{
InitializeComponent();
this.MyDesigner.Children.Clear();
this.MyDesigner.DesignerSetUp(strSomeWhenRegionCodeOut, intAfterTurnStageOut);
}
public UCCanvas()
{
InitializeComponent();
}
}
}
最后是外部调用它的一小段代码:
//双击gridControl
private void gridView1_DoubleClick(object sender, EventArgs e)
{
Point pt;
Point ptClient;
pt = MousePosition;
ptClient = gridControl1.PointToClient(pt);
DevExpress.XtraGrid.Views.Grid.ViewInfo.GridHitInfo hi = this.gridView1.CalcHitInfo(ptClient);
string strZDBM = "";
Int32 intTurnStage=0;
if (hi != null && hi.RowHandle >= 0)
{
strZDBM = this.gridView1.GetDataRow(hi.RowHandle)["初始宗地编号"].ToString();
intTurnStage = Convert.ToInt32(this.gridView1.GetDataRow(hi.RowHandle)["变更次数"]);
groupControl2.Visible = true;
//新方法
this.ucCanvas1 = new UCCanvas(strZDBM, intTurnStage);
this.elementHost1.Child = this.ucCanvas1;
this.elementHost1.BackColor = System.Drawing.Color.White;
}
}