先來看看iPhone的短信界面,就當是麻豆了 ^-^。
剛看到這個界面,口水就止不住啊。
擦干口水,趕緊模仿。
最初做法:
基於UserControl,自己做一個用戶控件,內含TextBolck可以實現多行顯示文本,
使用Path和Geometry將帶有小尾巴的圖形畫出來,然後再畫上光照效果,就大功
告成了。
雖然效果能做出來,但是細細想來,還是有不妥。
問題就在於,自己作的這個控件,只能顯示文字,為了要顯示圖片,還得寫一堆代碼,
太過死板,為什麼就不能利用WPF的框架或特點,讓這個控件變的靈活一些,只要些
繪圖效果和控制布局的效果就OK了,至於內部包含的是文字還是圖片,就交給WPF去
完成好了。
基於此,立馬想到為TextBlock控件寫個Template,但稍稍一定神發現,用Template
完成復雜圖形的繪制有些虛無缥缈,結果放棄。
思路:
在老大的點化之下,采用Decorater。乍看之下,有點摸不著北,細細看來,發現,
Border就是從此類繼承而來,說白了就是包含一個子元素的容器,自定義的Decorater
負責繪制圖形背景和管理子元素的顯示,至於顯示的是圖片還是文字就交給子元素去
完成。
為什麼不用Border而用它,道理就是用它就足夠了,干淨。
(當然了,容器還有Panel,ContentControl,在這裡用Decorater就夠了)。
先看一下最終效果:
主要就是用到基類的方法:
MeasureOverride:通過測量子元素來計算需要的空間的大小。
ArrangeOverride:排列子元素的顯示位置。
OnRender:自繪函數,帶小尾巴的圖形繪制就在這裡了,等價於OnPaint。
自定義IDecorator類:
public class iDecorator : Decorator {
public iDecorator() {
this.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
}
public bool Direction {
get { return (bool)GetValue(DirectionProperty); }
set { SetValue(DirectionProperty, value); }
}
protected override Size MeasureOverride(Size constraint) {
Size result = new Size();
if (Child != null) {
Child.Measure(constraint);
result.Width = Child.DesiredSize.Width + padding.Left + padding.Right;
result.Height = Child.DesiredSize.Height + padding.Top + padding.Bottom;
if (result.Height < 35) {
result.Height = 35;
padding.Top = padding.Bottom = (result.Height - Child.DesiredSize.Height) / 2;
}
}
return result;
}
protected override Size ArrangeOverride(Size arrangeSize) {
if (Child != null) {
Child.Arrange(new Rect(new Point(padding.Left, padding.Top),
Child.DesiredSize));
}
return arrangeSize;
}
protected override void OnRender(DrawingContext dc) {
if (Child != null) {
Geometry cg = null;
Brush brush = null;
Pen pen = new Pen();
pen.Brush = new SolidColorBrush(Colors.Black);
pen.Thickness = 1;
if (Direction) {
//生成小尾巴在右側的圖形和底色
cg = CreateGeometryTailAtRight();
brush = CreateBrushTailAtRight();
}
else {
//生成小尾巴在左側的圖形和底色
cg = CreateGeometryTailAtLeft();
brush = CreateBrushTailAtLeft();
}
dc.DrawGeometry(brush, pen, cg);
//繪制光照效果
GradientStopCollection gscLight = new GradientStopCollection();
gscLight.Add(new GradientStop(Color.FromArgb(0xDA, 0xFF, 0xFF, 0xFF), 0));
gscLight.Add(new GradientStop(Color.FromArgb(0x68, 0xFF, 0xEF, 0xFF), 1));
Brush lightBrush = new LinearGradientBrush(gscLight, new Point(0, 0), new Point(0, 1));
dc.DrawRoundedRectangle(lightBrush, null, new Rect(22, 1, this.ActualWidth - 45, 20), 10, 10);
}
//省略部分代碼。。。
public static void OnDirectionPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var self = d as iDecorator;
self.HorizontalAlignment = (bool)e.NewValue ?
HorizontalAlignment.Right : HorizontalAlignment.Left;
}
private Thickness padding = new Thickness(25, 6, 25, 6);
public static readonly DependencyProperty DirectionProperty =
DependencyProperty.Register("Direction", typeof(bool), typeof(iDecorator),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender, OnDirectionPropertyChangedCallback));
}
使用處:
短信框的小尾巴在右側:
<local:iDecorator HorizontalAlignment="Right" Direction="True">
<TextBlock MaxWidth="200" Foreground="Black" TextWrapping="Wrap">前兩天去哪兒了,聽說去北京了???</TextBlock>
</local:iDecorator>
短信框的小尾巴在左側:
就是把Direction(方向)設成False即可。
<local:iDecorator HorizontalAlignment="Right" Direction="False">
<TextBlock MaxWidth="200" Foreground="Black" TextWrapping="Wrap">前兩天去哪兒了,聽說去北京了???</TextBlock>
</local:iDecorator>
大功告成!
雖然還有些細節的效果還差一點,不過,It works。
源代碼:http://files.cnblogs.com/kongxianghai/iMessageDemoSln.rar
開發環境:VS2010,.NetFramWork4.0.
摘自 白色的海