WPF标注装饰器
标注
在许多地方我们都会用到标注,比如在画图中:

在Office中:

在Foxit Reader中:

在Blend中:

等等。
简介
以前,因项目上需要做标注,简单找了一下,没发现适合要求的控件(包括Blend中的标注,标注的两个点距离是固定的)。所以自己简单的写了一个。后来又私下修改了几次,基本完成了圆角矩形的标注。
效果图如下:

对应的XAML代码如下:
<local:CalloutDecorator Margin="5" AnchorOffsetX="150" AnchorOffsetY="50"
Background="Purple" BorderBrush="Red" BorderThickness="10,20,30,40"
CornerRadius="10,20,30,40" Dock="Left" FirstOffset="110"
Padding="40" SecondOffset="130">
<Border Background="Yellow" />
</local:CalloutDecorator>
支持设置锚点(AnchorOffsetX和AnchorOffsetY)、与锚点相对应的两个点的坐标(FirstOffset
和SecondOffset)、朝向(Dock)、圆角信息(CornerRadius)、边框信息(BorderThickness、BorderBrush)、保留空间(Padding)、背景(Background)。
设置各项参数时需要注意,不能让与锚点相对应的两个点的坐标都边框以内,否则会产生奇怪的效果。

但是好在我们一般情况下都不会将边框设的过大,而将两个点设置的较小。
代码
代码中重载了WPF三个重要过程,测量(MeasureOverride)、布局(ArrangeOverride)、绘制(OnRender)。为了提高绘制效率,使用了缓存。代码较简单,也有注释,就不再多说了。
namespace YiYan127.WPF.Decorator
{
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media; /// <summary>
/// 标注式装饰器
/// </summary>
public class CalloutDecorator : Border
{
#region Fields #region DependencyProperty public static readonly DependencyProperty DockProperty = DependencyProperty.Register(
"Dock",
typeof(Dock),
typeof(CalloutDecorator),
new FrameworkPropertyMetadata(Dock.Bottom, Refresh)); public static readonly DependencyProperty AnchorOffsetXProperty = DependencyProperty.Register(
"AnchorOffsetX",
typeof(double),
typeof(CalloutDecorator),
new FrameworkPropertyMetadata(20.0, Refresh),
DoubleGreatterThanZero); public static readonly DependencyProperty AnchorOffsetYProperty = DependencyProperty.Register(
"AnchorOffsetY",
typeof(double),
typeof(CalloutDecorator),
new FrameworkPropertyMetadata(20.0, Refresh),
DoubleGreatterThanZero); public static readonly DependencyProperty FirstOffsetProperty = DependencyProperty.Register(
"FirstOffset",
typeof(double),
typeof(CalloutDecorator),
new FrameworkPropertyMetadata(10.0, FrameworkPropertyMetadataOptions.AffectsArrange),
DoubleGreatterThanZero); public static readonly DependencyProperty SecondOffsetProperty = DependencyProperty.Register(
"SecondOffset",
typeof(double),
typeof(CalloutDecorator),
new FrameworkPropertyMetadata(20.0, FrameworkPropertyMetadataOptions.AffectsArrange),
DoubleGreatterThanZero); #endregion DependencyProperty /// <summary>
/// 刷新选项
/// </summary>
private const FrameworkPropertyMetadataOptions Refresh =
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender
| FrameworkPropertyMetadataOptions.AffectsArrange; /// <summary>
/// 是否为Callout模式,为false的话,表示border模式
/// </summary>
private bool isCalloutMode; /// <summary>
/// 背景的缓存
/// </summary>
private StreamGeometry backgroundGeometryCache; /// <summary>
/// 标注的缓存
/// </summary>
private StreamGeometry calloutGeometryCache; #endregion Fields #region Properties /// <summary>
/// 引线朝向(左、上、右、下)
/// </summary>
public Dock Dock
{
get { return (Dock)GetValue(DockProperty); }
set { this.SetValue(DockProperty, value); }
} /// <summary>
/// X方向的锚点偏移(针对子控件)
/// </summary>
public double AnchorOffsetX
{
get { return (double)GetValue(AnchorOffsetXProperty); }
set { this.SetValue(AnchorOffsetXProperty, value); }
} /// <summary>
/// Y方向的锚点偏移(针对子控件)
/// </summary>
public double AnchorOffsetY
{
get { return (double)GetValue(AnchorOffsetYProperty); }
set { this.SetValue(AnchorOffsetYProperty, value); }
} /// <summary>
/// 在对应的轴上第一个偏移位置
/// </summary>
public double FirstOffset
{
get { return (double)GetValue(FirstOffsetProperty); }
set { this.SetValue(FirstOffsetProperty, value); }
} /// <summary>
/// 在对应的轴上的第二个偏移位置
/// </summary>
public double SecondOffset
{
get { return (double)GetValue(SecondOffsetProperty); }
set { this.SetValue(SecondOffsetProperty, value); }
} #endregion Properties #region Overrides /// <summary>
/// 重载测量过程
/// </summary>
/// <param name="constraint">约束</param>
/// <returns>需要的大小</returns>
protected override Size MeasureOverride(Size constraint)
{
this.isCalloutMode = (this.Child != null) && (!IsZero(this.AnchorOffsetX) && (!IsZero(this.AnchorOffsetY))); if (!this.isCalloutMode)
{
return base.MeasureOverride(constraint);
} Size borderSize = GetDesiredSize(this.BorderThickness);
Size paddingSize = GetDesiredSize(this.Padding); // 最少需要的大小
var basicSize = new Size(borderSize.Width + paddingSize.Width, borderSize.Height + paddingSize.Height); // 计算需要的实际大小
switch (Dock)
{
case Dock.Left:
case Dock.Right:
{
// 宽度不能小于0
double availableWidth = Math.Max(0, constraint.Width - basicSize.Width - this.AnchorOffsetX);
var availableSize = new Size(availableWidth, Math.Max(0.0, constraint.Height - basicSize.Height)); this.Child.Measure(availableSize);
Size desiredSize = this.Child.DesiredSize; return new Size(
desiredSize.Width + basicSize.Width + this.AnchorOffsetX,
desiredSize.Height + basicSize.Height);
} case Dock.Top:
case Dock.Bottom:
{
double availableHeight = Math.Max(0, constraint.Height - basicSize.Height - this.AnchorOffsetY);
var availableSize = new Size(Math.Max(0.0, constraint.Width - basicSize.Width), availableHeight); this.Child.Measure(availableSize);
Size desiredSize = this.Child.DesiredSize; return new Size(
desiredSize.Width + basicSize.Width,
desiredSize.Height + basicSize.Height + this.AnchorOffsetY);
}
} return basicSize;
} /// <summary>
/// 重载布局过程
/// </summary>
/// <param name="finalSize">可用的布局大小</param>
/// <returns>布局大小</returns>
protected override Size ArrangeOverride(Size finalSize)
{
if (!this.isCalloutMode)
{
return base.ArrangeOverride(finalSize);
} var boundaryRect = new Rect(finalSize);
var outterRect = new Rect(); switch (Dock)
{
#region 根据不同的Dock进行处理 case Dock.Left:
{
outterRect = DeflateRect(boundaryRect, new Thickness(this.AnchorOffsetX, 0, 0, 0));
break;
} case Dock.Right:
{
outterRect = DeflateRect(boundaryRect, new Thickness(0, 0, this.AnchorOffsetX, 0));
break;
} case Dock.Top:
{
outterRect = DeflateRect(boundaryRect, new Thickness(0, this.AnchorOffsetY, 0, 0));
break;
} case Dock.Bottom:
{
outterRect = DeflateRect(boundaryRect, new Thickness(0, 0, 0, this.AnchorOffsetY));
break;
} #endregion 根据不同的Dock进行处理
} Rect innerRect = DeflateRect(outterRect, this.BorderThickness);
Rect finalRect = DeflateRect(innerRect, this.Padding);
this.Child.Arrange(finalRect); var innerPoints = new BorderPoints(this.CornerRadius, this.BorderThickness, false);
if (!IsZero(innerRect.Width) && !IsZero(innerRect.Height))
{
var streamGeometry = new StreamGeometry();
using (StreamGeometryContext streamGeometryContext = streamGeometry.Open())
{
this.GenerateGeometry(streamGeometryContext, innerRect, innerPoints, boundaryRect);
} streamGeometry.Freeze();
this.backgroundGeometryCache = streamGeometry;
}
else
{
this.backgroundGeometryCache = null;
} if (!IsZero(outterRect.Width) && !IsZero(outterRect.Height))
{
var outterPoints = new BorderPoints(this.CornerRadius, this.BorderThickness, true);
var streamGeometry = new StreamGeometry();
using (StreamGeometryContext streamGeometryContext = streamGeometry.Open())
{
this.GenerateGeometry(streamGeometryContext, outterRect, outterPoints, boundaryRect);
if (this.backgroundGeometryCache != null)
{
this.GenerateGeometry(streamGeometryContext, innerRect, innerPoints, boundaryRect);
}
} streamGeometry.Freeze();
this.calloutGeometryCache = streamGeometry;
}
else
{
this.calloutGeometryCache = null;
} return finalSize;
} /// <summary>
/// 重载绘制
/// </summary>
/// <param name="dc"></param>
protected override void OnRender(DrawingContext dc)
{
if (!this.isCalloutMode)
{
base.OnRender(dc);
return;
} if (this.calloutGeometryCache != null && this.BorderBrush != null)
{
dc.DrawGeometry(this.BorderBrush, null, this.calloutGeometryCache);
} if (this.backgroundGeometryCache != null && this.Background != null)
{
dc.DrawGeometry(this.Background, null, this.backgroundGeometryCache);
}
} #endregion Overrides #region Private Methods /// <summary>
/// 验证类型为double且大于0
/// </summary>
/// <param name="value">值</param>
/// <returns>数据为double类型且大于0</returns>
private static bool DoubleGreatterThanZero(object value)
{
return (value is double) && ((double)value) > 0;
} /// <summary>
/// 获取期望的大小
/// </summary>
/// <param name="thickness">边框信息</param>
/// <returns>期望的大小</returns>
private static Size GetDesiredSize(Thickness thickness)
{
return new Size(thickness.Left + thickness.Right, thickness.Top + thickness.Bottom);
} /// <summary>
/// 返回在矩形中留出边框后的矩形
/// </summary>
/// <param name="rt">矩形</param>
/// <param name="thick">边框</param>
/// <returns>留出边框后的矩形</returns>
private static Rect DeflateRect(Rect rt, Thickness thick)
{
return new Rect(rt.Left + thick.Left, rt.Top + thick.Top, Math.Max(0.0, rt.Width - thick.Left - thick.Right), Math.Max(0.0, rt.Height - thick.Top - thick.Bottom));
} /// <summary>
/// 判断一个数是否为0
/// </summary>
/// <param name="value">数</param>
/// <returns>为0返回true,否则返回false</returns>
private static bool IsZero(double value)
{
return Math.Abs(value) < 2.22044604925031E-15;
} /// <summary>
/// 返回过两点的直线在Y坐标上的X坐标
/// </summary>
/// <param name="point1">第一个点</param>
/// <param name="point2">第二个点</param>
/// <param name="y">Y坐标</param>
/// <returns>对应的X坐标</returns>
private static double CalculateLineX(Point point1, Point point2, double y)
{
return point1.X - ((point1.X - point2.X) * (point1.Y - y) / (point1.Y - point2.Y));
} /// <summary>
/// 返回过两点的直线在X坐标上的Y坐标
/// </summary>
/// <param name="point1">第一个点</param>
/// <param name="point2">第二个点</param>
/// <param name="x">X坐标</param>
/// <returns>对应的Y坐标</returns>
private static double CalculateLineY(Point point1, Point point2, double x)
{
return point1.Y - ((point1.X - x) * (point1.Y - point2.Y) / (point1.X - point2.X));
} /// <summary>
/// 生成形状
/// </summary>
/// <param name="ctx">绘制上下文</param>
/// <param name="rect">绘制所在的矩形</param>
/// <param name="points">边框绘制点</param>
/// <param name="boundaryRect">绘制的外边界</param>
private void GenerateGeometry(StreamGeometryContext ctx, Rect rect, BorderPoints points, Rect boundaryRect)
{
var leftTopPt = new Point(points.LeftTop, 0.0);
var rightTopPt = new Point(rect.Width - points.RightTop, 0.0);
var topRightPt = new Point(rect.Width, points.TopRight);
var bottomRightPt = new Point(rect.Width, rect.Height - points.BottomRight);
var rightBottomPt = new Point(rect.Width - points.RightBottom, rect.Height);
var leftBottomPt = new Point(points.LeftBottom, rect.Height);
var bottomLeftPt = new Point(0.0, rect.Height - points.BottomLeft);
var topLeftPt = new Point(0.0, points.TopLeft); if (leftTopPt.X > rightTopPt.X)
{
double x = points.LeftTop / (points.LeftTop + points.RightTop) * rect.Width;
leftTopPt.X = x;
rightTopPt.X = x;
} if (topRightPt.Y > bottomRightPt.Y)
{
double y = points.TopRight / (points.TopRight + points.BottomRight) * rect.Height;
topRightPt.Y = y;
bottomRightPt.Y = y;
} if (rightBottomPt.X < leftBottomPt.X)
{
double x2 = points.LeftBottom / (points.LeftBottom + points.RightBottom) * rect.Width;
rightBottomPt.X = x2;
leftBottomPt.X = x2;
} if (bottomLeftPt.Y < topLeftPt.Y)
{
double y2 = points.TopLeft / (points.TopLeft + points.BottomLeft) * rect.Height;
bottomLeftPt.Y = y2;
topLeftPt.Y = y2;
} var vector = new Vector(rect.TopLeft.X, rect.TopLeft.Y);
leftTopPt += vector;
rightTopPt += vector;
topRightPt += vector;
bottomRightPt += vector;
rightBottomPt += vector;
leftBottomPt += vector;
bottomLeftPt += vector;
topLeftPt += vector; ctx.BeginFigure(leftTopPt, true, true); if (this.Dock == Dock.Top)
{
var secondOutPoint = new Point(this.SecondOffset, this.AnchorOffsetY);
var firstOutPoint = new Point(this.FirstOffset, this.AnchorOffsetY);
var calloutPoint = new Point(this.AnchorOffsetX, 0); ctx.LineTo(new Point(CalculateLineX(calloutPoint, firstOutPoint, rect.Top), rect.Top), true, false);
ctx.LineTo(calloutPoint, true, false);
ctx.LineTo(new Point(CalculateLineX(calloutPoint, secondOutPoint, rect.Top), rect.Top), true, false);
} ctx.LineTo(rightTopPt, true, false);
double sizeX = rect.TopRight.X - rightTopPt.X;
double sizeY = topRightPt.Y - rect.TopRight.Y;
if (!IsZero(sizeX) || !IsZero(sizeY))
{
ctx.ArcTo(topRightPt, new Size(sizeX, sizeY), 0.0, false, SweepDirection.Clockwise, true, false);
} if (this.Dock == Dock.Right)
{
var secondOutPoint = new Point(boundaryRect.Width - this.AnchorOffsetX, this.SecondOffset);
var firstOutPoint = new Point(boundaryRect.Width - this.AnchorOffsetX, this.FirstOffset);
var calloutPoint = new Point(boundaryRect.Width, this.AnchorOffsetY); ctx.LineTo(new Point(rect.Right, CalculateLineY(calloutPoint, firstOutPoint, rect.Right)), true, false);
ctx.LineTo(calloutPoint, true, false);
ctx.LineTo(new Point(rect.Right, CalculateLineY(calloutPoint, secondOutPoint, rect.Right)), true, false);
} ctx.LineTo(bottomRightPt, true, false);
sizeX = rect.BottomRight.X - rightBottomPt.X;
sizeY = rect.BottomRight.Y - bottomRightPt.Y;
if (!IsZero(sizeX) || !IsZero(sizeY))
{
ctx.ArcTo(rightBottomPt, new Size(sizeX, sizeY), 0.0, false, SweepDirection.Clockwise, true, false);
} if (this.Dock == Dock.Bottom)
{
var secondOutPoint = new Point(this.SecondOffset, boundaryRect.Height - this.AnchorOffsetY);
var firstOutPoint = new Point(this.FirstOffset, boundaryRect.Height - this.AnchorOffsetY);
var calloutPoint = new Point(this.AnchorOffsetX, boundaryRect.Height); ctx.LineTo(new Point(CalculateLineX(calloutPoint, secondOutPoint, rect.Bottom), rect.Bottom), true, false);
ctx.LineTo(calloutPoint, true, false);
ctx.LineTo(new Point(CalculateLineX(calloutPoint, firstOutPoint, rect.Bottom), rect.Bottom), true, false);
} ctx.LineTo(leftBottomPt, true, false);
sizeX = leftBottomPt.X - rect.BottomLeft.X;
sizeY = rect.BottomLeft.Y - bottomLeftPt.Y;
if (!IsZero(sizeX) || !IsZero(sizeY))
{
ctx.ArcTo(bottomLeftPt, new Size(sizeX, sizeY), 0.0, false, SweepDirection.Clockwise, true, false);
} if (this.Dock == Dock.Left)
{
var secondOutPoint = new Point(this.AnchorOffsetX, this.SecondOffset);
var firstOutPoint = new Point(this.AnchorOffsetX, this.FirstOffset);
var calloutPoint = new Point(0, this.AnchorOffsetY); ctx.LineTo(new Point(rect.Left, CalculateLineY(calloutPoint, firstOutPoint, rect.Left)), true, false);
ctx.LineTo(calloutPoint, true, false);
ctx.LineTo(new Point(rect.Left, CalculateLineY(calloutPoint, secondOutPoint, rect.Left)), true, false);
} ctx.LineTo(topLeftPt, true, false);
sizeX = leftTopPt.X - rect.TopLeft.X;
sizeY = topLeftPt.Y - rect.TopLeft.Y;
if (!IsZero(sizeX) || !IsZero(sizeY))
{
ctx.ArcTo(leftTopPt, new Size(sizeX, sizeY), 0.0, false, SweepDirection.Clockwise, true, false);
}
} #endregion Private Methods /// <summary>
/// 边框绘制点
/// </summary>
private struct BorderPoints
{
internal readonly double LeftTop;
internal readonly double TopLeft;
internal readonly double TopRight;
internal readonly double RightTop;
internal readonly double RightBottom;
internal readonly double BottomRight;
internal readonly double BottomLeft;
internal readonly double LeftBottom; /// <summary>
/// 构造函数
/// </summary>
/// <param name="borderCornerRadius">圆角信息</param>
/// <param name="boderThickness">边框信息</param>
/// <param name="outer">是否为外部</param>
internal BorderPoints(CornerRadius borderCornerRadius, Thickness boderThickness, bool outer)
{
double halfLeft = 0.5 * boderThickness.Left;
double halfTop = 0.5 * boderThickness.Top;
double halfRight = 0.5 * boderThickness.Right;
double halfBottom = 0.5 * boderThickness.Bottom;
if (outer)
{
if (IsZero(borderCornerRadius.TopLeft))
{
this.LeftTop = this.TopLeft = 0.0;
}
else
{
this.LeftTop = borderCornerRadius.TopLeft + halfLeft;
this.TopLeft = borderCornerRadius.TopLeft + halfTop;
} if (IsZero(borderCornerRadius.TopRight))
{
this.TopRight = this.RightTop = 0.0;
}
else
{
this.TopRight = borderCornerRadius.TopRight + halfTop;
this.RightTop = borderCornerRadius.TopRight + halfRight;
} if (IsZero(borderCornerRadius.BottomRight))
{
this.RightBottom = this.BottomRight = 0.0;
}
else
{
this.RightBottom = borderCornerRadius.BottomRight + halfRight;
this.BottomRight = borderCornerRadius.BottomRight + halfBottom;
} if (IsZero(borderCornerRadius.BottomLeft))
{
this.BottomLeft = this.LeftBottom = 0.0;
}
else
{
this.BottomLeft = borderCornerRadius.BottomLeft + halfBottom;
this.LeftBottom = borderCornerRadius.BottomLeft + halfLeft;
}
}
else
{
this.LeftTop = Math.Max(0.0, borderCornerRadius.TopLeft - halfLeft);
this.TopLeft = Math.Max(0.0, borderCornerRadius.TopLeft - halfTop);
this.TopRight = Math.Max(0.0, borderCornerRadius.TopRight - halfTop);
this.RightTop = Math.Max(0.0, borderCornerRadius.TopRight - halfRight);
this.RightBottom = Math.Max(0.0, borderCornerRadius.BottomRight - halfRight);
this.BottomRight = Math.Max(0.0, borderCornerRadius.BottomRight - halfBottom);
this.BottomLeft = Math.Max(0.0, borderCornerRadius.BottomLeft - halfBottom);
this.LeftBottom = Math.Max(0.0, borderCornerRadius.BottomLeft - halfLeft);
}
}
}
}
}
WPF标注装饰器的更多相关文章
- WPF装饰器
装饰器定义: 装饰器是一种特殊类型的 FrameworkElement,用于向用户提供可视化提示. 对于其他用户,装饰器可用于将功能控点添加到元素中或提供有关控件的状态信息. 装饰器可以在不改变原有的 ...
- WPF和Expression Blend开发实例:Adorner(装饰器)应用实例
装饰器-- 表示用于修饰 UIElement 的 FrameworkElement 的抽象类 简单来说就是,在不改变一个UIElement结构的情况下,将一个Visual对象加到它上面. 应用举例: ...
- (十)装饰器模式详解(与IO不解的情缘)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. LZ到目前已经写了九个设计模 ...
- Python学习笔记——基础篇2【第三周】——计数器、有序字典、元组、单(双)向队列、深浅拷贝、函数、装饰器
目录 1.Python计数器Counter 2.Python有序字典OrderredDict 3.Python默认字典default 4.python可命名元组namedtuple 5.Python双 ...
- Python系列之文件操作、冒泡算法、装饰器、及递归
文件处理 python对文件进行读写操作的方法与具体步骤,包括打开文件.读取内容.写入文件.文件中的内容定位.及关闭文件释放资源等 open().file(),这个两函数提供了初始化输入\输出(I\O ...
- python装饰器 & flask 通过装饰器 实现 单点登录验证
首先介绍装饰器,以下是一段标注了特殊输出的代码.用于帮助理解装饰器的调用过程. import time def Decorator_one(arg1): info = "\033[1;31; ...
- 基于TypeScript装饰器定义Express RESTful 服务
前言 本文主要讲解如何使用TypeScript装饰器定义Express路由.文中出现的代码经过简化不能直接运行,完整代码的请戳:https://github.com/WinfredWang/expre ...
- 涉及模式之 装饰器模式详解(与IO不解的情缘)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. LZ到目前已经写了九个设计模 ...
- Java设计模式系列-装饰器模式
原创文章,转载请标注出处:<Java设计模式系列-装饰器模式> 一.概述 装饰器模式作用是针对目标方法进行增强,提供新的功能或者额外的功能. 不同于适配器模式和桥接模式,装饰器模式涉及的是 ...
随机推荐
- 优秀程序设计的Kiss原则(keep it simple,stupid)
优秀程序设计的Kiss原则(keep it simple,stupid) 良好的编程原则与良好的设计工程原则密切相关.本文总结的这些设计原则,帮助开发者更有效率的编写代码,并帮助成为一名优秀的程序员. ...
- Egret Engine(白鹭引擎)介绍及windows下安装
Egret Engine简要介绍----- Egret Engine(白鹭引擎)[Egret Engine官网:http://www.egret-labs.org/]是一款使用TypeScript语言 ...
- 复杂领域的Cynefin模型和Stacey模型
最近好奇“复杂系统”,收集了点资料,本文关于Cynefin模型和Stacey模型.图文转自互联网后稍做修改. Cynefin模型提供一个从因果关系复杂情度来分析当前情况而作决定的框架,提出有五个领域: ...
- vsphere vcenter server下安装ubuntu的vmwaretools
0.参考文献 百度经验:这里面是以redhat桌面版为实例进行介绍的,我的环境是ubuntu-server,虽然不一样,也可以参考 http://jingyan.baidu.com/article/2 ...
- 《android传感器高级编程》译者序
翻看手机中的应用,就能发现大多数应用都已经使用了传感器.让微信彻底火起来的“附近的人”和“摇一摇”.碰一碰交换信息的Bump.各种运动记录app.神奇的“磁力探测仪”.火爆的游戏Temple Run… ...
- C++语言-01-简介
简介 C++语言是C语言的超集,它扩充和完善了C语言:C++语言是一种静态类型的.编译时的.跨平台的.不规则的中级编程语言,综合了高级语言和低级语言的特点 C++支持的编程类型 面向对象编程 过程化编 ...
- Stronger (What Doesn't Kill You)
今天听一个歌曲,挺不错的.以前一直不知道意思.这次把歌词摘抄下来. 试听音乐: 原版MV: You know the bed feels warmer 你知道被窝里的温暖 Sleeping here ...
- Linux写时拷贝技术(copy-on-write)
COW技术初窥: 在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,linux中引入了“写时复制“技术,也就是只有进程空间的各段的内 ...
- 奇怪的cab_xxxx_x文件
最近一段时间发现C盘老是提示空间紧张(显示为红色),之前清理了一次系统盘的\Windows\Temp文件夹,发现了很多文件名类似cab_xxxx_x的cab文件,大概占用了五六个G的空间,当时没太在意 ...
- win10中文简体繁体切换快捷键
win10中文简体繁体切换快捷键Ctrl+Shift+F