首先。如果放到以前这应该这里应该叫做自定义控件,但是问题是wpf中element和控件还有一定的区别。
面对设计一个新的element的时候,我们首先第一个问题就是这个类需要继承于FrameworkElement还是contorl.
Control类有FrameworkElement所不具有的很多的属性,比如backgroud,foreground等。
class SimpleEllips:FrameworkElement
{
protected override void OnRender(DrawingContext drawingContext)
{
//base.OnRender(drawingContext);
drawingContext.DrawEllipse(Brushes.Blue, new Pen(Brushes.Red, 24),
new Point(RenderSize.Width / 2, RenderSize.Height / 2),
RenderSize.Width / 2, RenderSize.Height / 2);
}
}
这是一个自定义element的例子。
OnRender是用来绘制这个element的,在这个函数中,我们可以使用DrawingContext(DC)来绘制我们想要的图形。Height
在绘制的时候,一般需要用到RenderSize,他表示可以绘制图像的区域大小。RenderSize的确定一般是根据容器的大小,他是根据UIelement和一些因子计算出来的。当你设定了element的Width和Height的话,在计算RenderSize的时候,element的本身的Width和Height属性具有优先权。如果你设定了Width,那么RenderSize的Width就等于你设定的Width,但是要是容器的宽度比这个Width小,椭圆就会被裁剪。
View Code
class Program:Window
{
[STAThread]
static void Main(string[] args)
{
Application app = new Application();
app.Run(new Program());
}
public Program()
{
SimpleEllips elps = new SimpleEllips();
elps.HorizontalAlignment = HorizontalAlignment.Center;
elps.VerticalAlignment = VerticalAlignment.Center;
elps.Width = 36;
elps.Height = 36;
Content = elps;
}
}
class SimpleEllips:FrameworkElement
{
protected override void OnRender(DrawingContext drawingContext)
{
//base.OnRender(drawingContext);
drawingContext.DrawEllipse(Brushes.Blue, new Pen(Brushes.Red, 24),
new Point(RenderSize.Width / 2, RenderSize.Height / 2),
RenderSize.Width / 2, RenderSize.Height / 2);
}
}
对于我们自定义的element通常需要定义一个自己喜好的尺寸大小。比如说一个控件总有自己需要的最小大小限制。但是自定义一个element不应该随便指定 这几个属性,这些留给实例化的对象去指定。
而自定义的element需要指定的是想要的尺寸,什么叫想要的尺寸呢,表示可以显示的范围。一般情况下,所有他这个可能大小可以是从0到无穷大的。
比如说,一个窗口的content是这个element。那么这个element就是这个窗口的大小。
自定义element可以重载FrameworkElement的Measureoverride方法。
protected override Size MeasureOverride(Size availableSize)
{
Size sizedesird = base.MeasureOverride(availableSize);
//在这里可以设置它的评估大小,也就是想要的大小。
return sizedesird;
}
对于MeasureOverride的调用总是发生在第一次OnRender之前,其实就是在渲染之前首先进行一次大小评估,最大的尺寸是多大,然后才能确定如何去渲染。在这之后,即使OnRender被再次调用,MeasureOverride也不会再次被调用。除非父窗体的大小发生了变化。
接上文,如果上面这个element的窗口设置了SizetoContent(获取或设置一个值,该值指示窗口是否自动调整自身大小以适应其内容大小。这是一个依赖项属性。),那么如果设置了SizeToContent,
当 SizeToContent 设置为 WidthAndHeight 时,设置 Height 或 Width 不起作用;这两个属性都可以进行设置,但是为它们设置的值不会应用于窗口,那么element的availableSize会将Height 或 Width设置为Double..::.PositiveInfinity,表示正无穷。但是我们输出的情况下窗口处于最小化。
当 SizeToContent 设置为 Height 时,设置 Height 会更改窗口的高度。那么element的availableSize会将Height设置为Double..::.PositiveInfinity,表示正无穷。但是vailableSize。 Width会等于父窗口的Width。
当 SizeToContent 设置为 Width 时,设置 Width
????经过观察:WidthAndHeight对于窗口大小均为0.
Height:高度为0,Width为默认窗口的Width。
Width:Height为默认的窗口Height高度,但是宽度为窗口的最小宽度。
一般情况下,如果这个element没有孩子,你不需要太过于关注。但是如果这个element需要一定长宽比.
View Code
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Media;
using System.Globalization;
namespace wpfdemo_customelement
{
class Program:Window
{
[STAThread]
static void Main(string[] args)
{
Application app = new Application();
app.Run(new Program());
}
public Program()
{
SimpleEllips elps = new SimpleEllips();
//Canvas canvas = new Canvas();
//canvas.HorizontalAlignment = HorizontalAlignment.Stretch;
//canvas.VerticalAlignment = VerticalAlignment.Stretch;
// Ellipse elps = new Ellipse();
//canvas.Children.Add(elps);
//elps.Stroke = new Pen(Brushes.SeaGreen, 6);
//elps.Stroke = Brushes.Red;
//elps.Fill = Brushes.Red;
//elps.Width = 100;
//elps.Height = 100;
elps.HorizontalAlignment = HorizontalAlignment.Stretch;
elps.VerticalAlignment = VerticalAlignment.Stretch;
elps.Width = 36;//当设置为如此大小的时候可能会导致文字显示不全
elps.Height = 36;
Content = elps;
// SizeToContent = SizeToContent.Width;
this.WindowState = WindowState.Normal;
}
}
/// <summary>
/// mytesy
/// </summary>
class SimpleEllips:FrameworkElement
{
public static readonly DependencyProperty Fillproperty;
public static readonly DependencyProperty StrokeProprety;
//public static readonly DependencyProperty WidthProperty;
public Brush Fill
{
get { return (Brush)GetValue(Fillproperty); }
set { SetValue(Fillproperty, value); }
}
/// <summary>
///
/// </summary>
public Pen Stroke
{
get { return (Pen)GetValue(StrokeProprety); }
set { SetValue(StrokeProprety, value); }
}
static SimpleEllips()
{
//呈现或布局组合的某一方面(不包括测量或排列)受到此依赖项属性的值更改的影响。此依赖属性变化时候会调用OnRender
Fillproperty = DependencyProperty.Register("Fill", typeof(Brush), typeof(SimpleEllips),
new FrameworkPropertyMetadata(Brushes.Red, FrameworkPropertyMetadataOptions.AffectsRender));
//布局组合的测量处理过程受到此依赖项属性的值更改的影响。此依赖属性的变化时候会调用MeasureOverride
StrokeProprety = DependencyProperty.Register("Stroke", typeof(Pen), typeof(SimpleEllips),
new FrameworkPropertyMetadata(new Pen(Brushes.Black, 5), FrameworkPropertyMetadataOptions.AffectsMeasure));
}
protected override void OnRender(DrawingContext drawingContext)
{
//base.OnRender(drawingContext);
Size size = RenderSize;
if (Stroke!=null)
{
size.Width = Math.Max(0, size.Width - Stroke.Thickness);
size.Height = Math.Max(0, size.Height = Stroke.Thickness);
}
drawingContext.DrawEllipse(Fill,Stroke, new Point(RenderSize.Width / 2, RenderSize.Height / 2),
RenderSize.Width / 2, RenderSize.Height / 2);
//drawingContext.DrawText()
FormattedText temp = new FormattedText("Test", CultureInfo.CurrentCulture, FlowDirection, new Typeface("Times New Roman italic"), 24, Brushes.DarkBlue);
Point pt = new Point((RenderSize.Width - temp.Width) / 2, (RenderSize.Height - temp.Height) / 2);
drawingContext.DrawText(temp, pt);
}
protected override Size MeasureOverride(Size availableSize)
{
Size sizedesird = base.MeasureOverride(availableSize);
//在这里可以设置它的评估大小,也就是想要的大小。
if (Stroke!=null)
{//当Stroke发生变化时候,这个方法会被调用
sizedesird = new Size(Stroke.Thickness, Stroke.Thickness);
}
return sizedesird;
}
}
}
上面是一个自定义element的例子。可以在onRender函数中绘制图像,在MeasureOverride中评估大小。 可以看出,文字部分在椭圆比较小的时候还是出现在了椭圆区域之外,我们想要他显示在椭圆之内,该怎么办呢
OnRender并不会裁剪FormattedText。
View Code
class Mybutton :Control
{
FormattedText formtext;
Boolean IsMouseRealyover;
public static readonly DependencyProperty TextProperty;
public static readonly RoutedEvent KnockEvent;
public static readonly RoutedEvent PreviewKnockEvent;
static Mybutton()
{
TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(Mybutton),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsMeasure));
KnockEvent = EventManager.RegisterRoutedEvent("Knock",
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Mybutton));
PreviewKnockEvent = EventManager.RegisterRoutedEvent("PreViewKnock",
RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(Mybutton));
}
public string Text
{
set { this.SetValue(TextProperty, value==null?"":value); }
get { return (string)this.GetValue(TextProperty); }
}
public event RoutedEventHandler Knock
{
add { AddHandler(KnockEvent, value); }
remove { RemoveHandler(KnockEvent, value); }
}
public event RoutedEventHandler PreviewKnock
{
add { AddHandler(PreviewKnockEvent, value); }
remove { RemoveHandler(PreviewKnockEvent, value); }
}
protected override Size MeasureOverride(Size constraint)
{
formtext = new FormattedText(
Text, CultureInfo.CurrentCulture, FlowDirection,
new Typeface(FontFamily, FontStyle, FontWeight, FontStretch), FontSize, Foreground);
Size sizeDesired = new Size(Math.Max(48, formtext.Width) + 4, formtext.Height + 4);
sizeDesired.Width += Padding.Left + Padding.Right;
sizeDesired.Height += Padding.Top + Padding.Bottom;
return sizeDesired;
// return base.MeasureOverride(constraint);
}
protected override void OnRender(DrawingContext drawingContext)
{
Brush brushbackgroud = SystemColors.ControlBrush;
if (IsMouseCaptured&&IsMouseRealyover)
{
brushbackgroud = SystemColors.ControlDarkBrush;
}
Pen pen = new Pen(Foreground, IsMouseOver ? 2 : 1);
drawingContext.DrawRoundedRectangle(brushbackgroud, pen, new Rect(new Point(0, 0), RenderSize), 4, 4);
formtext.SetForegroundBrush(IsEnabled ? Foreground : SystemColors.ControlDarkBrush);
Point ptText = new Point(2, 2);
switch (HorizontalAlignment)
{
case HorizontalAlignment.Left:
ptText.X += Padding.Left;
break;
case HorizontalAlignment.Right:
ptText.X += RenderSize.Width-formtext.Width-Padding.Right;
break;
case HorizontalAlignment.Center:
case HorizontalAlignment.Stretch:
ptText.X +=( RenderSize.Width - formtext.Width - Padding.Right-Padding.Left)/2;
break;
}
switch (VerticalAlignment)
{
case VerticalAlignment.Top:
ptText.Y += Padding.Top;
break;
case VerticalAlignment.Bottom:
ptText.Y += RenderSize.Height-formtext.Height-Padding.Bottom;
break;
case VerticalAlignment.Center:
case VerticalAlignment.Stretch:
ptText.Y = (RenderSize.Height - formtext.Height - Padding.Top - Padding.Bottom) / 2;
break;
}
drawingContext.DrawText(formtext, ptText);
//base.OnRender(drawingContext);
}
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
InvalidateVisual();
}
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
InvalidateVisual();
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
Point pt = e.GetPosition(this);
Boolean isovernow = (pt.X >= 0 && pt.X < ActualWidth && pt.Y >= 0 && pt.Y < ActualHeight);
if (isovernow != IsMouseRealyover)
{
IsMouseRealyover = isovernow;
InvalidateVisual();
}
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
CaptureMouse();
InvalidateVisual();
e.Handled = true;
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
if (IsMouseCaptured)
{
if (IsMouseRealyover)
{
OnPreviewKnock();
OnKnock();
}
e.Handled = true;
Mouse.Capture(null);
}
}
protected override void OnLostMouseCapture(MouseEventArgs e)
{
base.OnLostMouseCapture(e);
InvalidateVisual();
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
}
protected virtual void OnKnock()
{
RoutedEventArgs args = new RoutedEventArgs();
args.RoutedEvent = Mybutton.KnockEvent;
args.Source = this;
RaiseEvent(args);
}
protected virtual void OnPreviewKnock()
{
RoutedEventArgs args = new RoutedEventArgs();
args.RoutedEvent = Mybutton.PreviewKnockEvent;
args.Source = this;
RaiseEvent(args);
}
}