首先。如果放到以前这应该这里应该叫做自定义控件,但是问题是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小,椭圆就会被裁剪。

 

wpf meunitem元素 wpf frameworkelement_System

wpf meunitem元素 wpf frameworkelement_wpf meunitem元素_02

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需要一定长宽比.

wpf meunitem元素 wpf frameworkelement_System

wpf meunitem元素 wpf frameworkelement_wpf meunitem元素_02

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。

wpf meunitem元素 wpf frameworkelement_System

wpf meunitem元素 wpf frameworkelement_wpf meunitem元素_02

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);
        }

}