C#用戶自定義控件(含源代碼)-透明文本框

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

using System.Drawing.Imaging;

namespace ZBobb
{
    /// <summary>
    /// AlphaBlendTextBox: A .Net textbox that can be translucent to the background.
    /// (C) 2003 Bob Bradley / ZBobb@hotmail.com
    /// </summary>
 

    public class AlphaBlendTextBox : System.Windows.Forms.TextBox
    {
        #region private variables

        private uPictureBox myPictureBox;
        private bool myUpToDate = false;
        private bool myCaretUpToDate = false;
        private Bitmap myBitmap;
        private Bitmap myAlphaBitmap;

        private int myFontHeight = 10;

        private System.Windows.Forms.Timer myTimer1;

        private bool myCaretState = true;

        private bool myPaintedFirstTime = false;

        private Color myBackColor = Color.White;
        private int myBackAlpha = 10;

        /// <summary> 
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components = null;

        #endregion // end private variables


        #region public methods and overrides

        public AlphaBlendTextBox()
        {
            // This call is required by the Windows.Forms Form Designer.
            InitializeComponent();
            // TODO: Add any initialization after the InitializeComponent call

            this.BackColor = myBackColor;

            this.SetStyle(ControlStyles.UserPaint, false);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.DoubleBuffer, true);


            myPictureBox = new uPictureBox();
            this.Controls.Add(myPictureBox);
            myPictureBox.Dock = DockStyle.Fill;
        }


        protected override void OnResize(EventArgs e)
        {

            base.OnResize(e);
            this.myBitmap = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);//(this.Width,this.Height);
            this.myAlphaBitmap = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);//(this.Width,this.Height);
            myUpToDate = false;
            this.Invalidate();
        }


        //Some of these should be moved to the WndProc later

        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);
            myUpToDate = false;
            this.Invalidate();
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyUp(e);
            myUpToDate = false;
            this.Invalidate();

        }

        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            base.OnKeyPress(e);
            myUpToDate = false;
            this.Invalidate();
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            this.Invalidate();
        }

        protected override void OnGiveFeedback(GiveFeedbackEventArgs gfbevent)
        {
            base.OnGiveFeedback(gfbevent);
            myUpToDate = false;
            this.Invalidate();
        }


        protected override void OnMouseLeave(EventArgs e)
        {
            //found this code to find the current cursor location
            //at http://www.syncfusion.com/FAQ/WinForms/FAQ_c50c.asp#q597q

            Point ptCursor = Cursor.Position;

            Form f = this.FindForm();
            ptCursor = f.PointToClient(ptCursor);
            if (!this.Bounds.Contains(ptCursor))
                base.OnMouseLeave(e);
        }


        protected override void OnChangeUICues(UICuesEventArgs e)
        {
            base.OnChangeUICues(e);
            myUpToDate = false;
            this.Invalidate();
        }


        //--
        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            myCaretUpToDate = false;
            myUpToDate = false;
            this.Invalidate();


            myTimer1 = new System.Windows.Forms.Timer(this.components);
            myTimer1.Interval = (int)win32.GetCaretBlinkTime(); //  usually around 500;

            myTimer1.Tick += new EventHandler(myTimer1_Tick);
            myTimer1.Enabled = true;

        }

        protected override void OnLostFocus(EventArgs e)
        {
            base.OnLostFocus(e);
            myCaretUpToDate = false;
            myUpToDate = false;
            this.Invalidate();

            myTimer1.Dispose();
        }

        //--  

        protected override void OnFontChanged(EventArgs e)
        {
            if (this.myPaintedFirstTime)
                this.SetStyle(ControlStyles.UserPaint, false);

            base.OnFontChanged(e);

            if (this.myPaintedFirstTime)
                this.SetStyle(ControlStyles.UserPaint, true);


            myFontHeight = GetFontHeight();


            myUpToDate = false;
            this.Invalidate();
        }

        protected override void OnTextChanged(EventArgs e)
        {
            base.OnTextChanged(e);
            myUpToDate = false;
            this.Invalidate();
        }


        protected override void WndProc(ref Message m)
        {

            base.WndProc(ref m);

            // need to rewrite as a big switch

            if (m.Msg == win32.WM_PAINT)
            {
                myPaintedFirstTime = true;

                if (!myUpToDate || !myCaretUpToDate)
                    GetBitmaps();
                myUpToDate = true;
                myCaretUpToDate = true;

                if (myPictureBox.Image != null) myPictureBox.Image.Dispose();
                myPictureBox.Image = (Image)myAlphaBitmap.Clone();

            }

            else if (m.Msg == win32.WM_HSCROLL || m.Msg == win32.WM_VSCROLL)
            {
                myUpToDate = false;
                this.Invalidate();
            }

            else if (m.Msg == win32.WM_LBUTTONDOWN
             || m.Msg == win32.WM_RBUTTONDOWN
             || m.Msg == win32.WM_LBUTTONDBLCLK
                //  || m.Msg == win32.WM_MOUSELEAVE  ///****
             )
            {
                myUpToDate = false;
                this.Invalidate();
            }

            else if (m.Msg == win32.WM_MOUSEMOVE)
            {
                if (m.WParam.ToInt32() != 0)  //shift key or other buttons
                {
                    myUpToDate = false;
                    this.Invalidate();
                }
            }



            //System.Diagnostics.Debug.WriteLine("Pro: " + m.Msg.ToString("X"));

        }


        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                //this.BackColor = Color.Pink;
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose(disposing);
        }

        #endregion  //end public method and overrides


        #region public property overrides

        public new BorderStyle BorderStyle
        {
            get { return base.BorderStyle; }
            set
            {
                if (this.myPaintedFirstTime)
                    this.SetStyle(ControlStyles.UserPaint, false);

                base.BorderStyle = value;

                if (this.myPaintedFirstTime)
                    this.SetStyle(ControlStyles.UserPaint, true);

                this.myBitmap = null;
                this.myAlphaBitmap = null;
                myUpToDate = false;
                this.Invalidate();
            }
        }

        public new Color BackColor
        {
            get
            {
                return Color.FromArgb(base.BackColor.R, base.BackColor.G, base.BackColor.B);
            }
            set
            {
                myBackColor = value;
                base.BackColor = value;
                myUpToDate = false;
            }
        }
        public override bool Multiline
        {
            get { return base.Multiline; }
            set
            {
                if (this.myPaintedFirstTime)
                    this.SetStyle(ControlStyles.UserPaint, false);

                base.Multiline = value;

                if (this.myPaintedFirstTime)
                    this.SetStyle(ControlStyles.UserPaint, true);

                this.myBitmap = null;
                this.myAlphaBitmap = null;
                myUpToDate = false;
                this.Invalidate();
            }
        }


        #endregion    //end public property overrides


        #region private functions and classes

        private int GetFontHeight()
        {
            Graphics g = this.CreateGraphics();
            SizeF sf_font = g.MeasureString("X", this.Font);
            g.Dispose();
            return (int)sf_font.Height;
        }


        private void GetBitmaps()
        {

            if (myBitmap == null
             || myAlphaBitmap == null
             || myBitmap.Width != Width
             || myBitmap.Height != Height
             || myAlphaBitmap.Width != Width
             || myAlphaBitmap.Height != Height)
            {
                myBitmap = null;
                myAlphaBitmap = null;
            }



            if (myBitmap == null)
            {
                myBitmap = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);//(Width,Height);
                myUpToDate = false;
            }


            if (!myUpToDate)
            {
                //Capture the TextBox control window

                this.SetStyle(ControlStyles.UserPaint, false);

                win32.CaptureWindow(this, ref myBitmap);

                this.SetStyle(ControlStyles.UserPaint, true);
                this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
                this.BackColor = Color.FromArgb(myBackAlpha, myBackColor);

            }
            //--



            Rectangle r2 = new Rectangle(0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height);
            ImageAttributes tempImageAttr = new ImageAttributes();


            //Found the color map code in the MS Help

            ColorMap[] tempColorMap = new ColorMap[1];
            tempColorMap[0] = new ColorMap();
            tempColorMap[0].OldColor = Color.FromArgb(255, myBackColor);
            tempColorMap[0].NewColor = Color.FromArgb(myBackAlpha, myBackColor);

            tempImageAttr.SetRemapTable(tempColorMap);

            if (myAlphaBitmap != null)
                myAlphaBitmap.Dispose();


            myAlphaBitmap = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);//(Width,Height);

            Graphics tempGraphics1 = Graphics.FromImage(myAlphaBitmap);

            tempGraphics1.DrawImage(myBitmap, r2, 0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height, GraphicsUnit.Pixel, tempImageAttr);

            tempGraphics1.Dispose();

            //----

            if (this.Focused && (this.SelectionLength == 0))
            {
                Graphics tempGraphics2 = Graphics.FromImage(myAlphaBitmap);
                if (myCaretState)
                {
                    //Draw the caret
                    Point caret = this.findCaret();
                    Pen p = new Pen(this.ForeColor, 3);
                    tempGraphics2.DrawLine(p, caret.X, caret.Y + 0, caret.X, caret.Y + myFontHeight);
                    tempGraphics2.Dispose();
                }

            }



        }



        private Point findCaret()
        {
            /*  Find the caret translated from code at 
             * http://www.vb-helper.com/howto_track_textbox_caret.html
             * 
             * and 
             * 
             * http://www.microbion.co.uk/developers/csharp/textpos2.htm
             * 
             * Changed to EM_POSFROMCHAR
             * 
             * This code still needs to be cleaned up and debugged
             * */

            Point pointCaret = new Point(0);
            int i_char_loc = this.SelectionStart;
            IntPtr pi_char_loc = new IntPtr(i_char_loc);

            int i_point = win32.SendMessage(this.Handle, win32.EM_POSFROMCHAR, pi_char_loc, IntPtr.Zero);
            pointCaret = new Point(i_point);

            if (i_char_loc == 0)
            {
                pointCaret = new Point(0);
            }
            else if (i_char_loc >= this.Text.Length)
            {
                pi_char_loc = new IntPtr(i_char_loc - 1);
                i_point = win32.SendMessage(this.Handle, win32.EM_POSFROMCHAR, pi_char_loc, IntPtr.Zero);
                pointCaret = new Point(i_point);

                Graphics g = this.CreateGraphics();
                String t1 = this.Text.Substring(this.Text.Length - 1, 1) + "X";
                SizeF sizet1 = g.MeasureString(t1, this.Font);
                SizeF sizex = g.MeasureString("X", this.Font);
                g.Dispose();
                int xoffset = (int)(sizet1.Width - sizex.Width);
                pointCaret.X = pointCaret.X + xoffset;

                if (i_char_loc == this.Text.Length)
                {
                    String slast = this.Text.Substring(Text.Length - 1, 1);
                    if (slast == "\n")
                    {
                        pointCaret.X = 1;
                        pointCaret.Y = pointCaret.Y + myFontHeight;
                    }
                }

            }



            return pointCaret;
        }


        private void myTimer1_Tick(object sender, EventArgs e)
        {
            //Timer used to turn caret on and off for focused control

            myCaretState = !myCaretState;
            myCaretUpToDate = false;
            this.Invalidate();
        }


        private class uPictureBox : PictureBox
        {
            public uPictureBox()
            {
                this.SetStyle(ControlStyles.Selectable, false);
                this.SetStyle(ControlStyles.UserPaint, true);
                this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
                this.SetStyle(ControlStyles.DoubleBuffer, true);

                this.Cursor = null;
                this.Enabled = true;
                this.SizeMode = PictureBoxSizeMode.Normal;

            }




            //uPictureBox
            protected override void WndProc(ref Message m)
            {
                if (m.Msg == win32.WM_LBUTTONDOWN
                 || m.Msg == win32.WM_RBUTTONDOWN
                 || m.Msg == win32.WM_LBUTTONDBLCLK
                 || m.Msg == win32.WM_MOUSELEAVE
                 || m.Msg == win32.WM_MOUSEMOVE)
                {
                    //Send the above messages back to the parent control
                    win32.PostMessage(this.Parent.Handle, (uint)m.Msg, m.WParam, m.LParam);
                }

                else if (m.Msg == win32.WM_LBUTTONUP)
                {
                    //??  for selects and such
                    this.Parent.Invalidate();
                }


                base.WndProc(ref m);
            }


        }   // End uPictureBox Class


        #endregion  // end private functions and classes


        #region Component Designer generated code
        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
        }
        #endregion


        #region New Public Properties

        [
        Category("Appearance"),
        Description("The alpha value used to blend the control's background. Valid values are 0 through 255."),
        Browsable(true),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)

        ]
        public int BackAlpha
        {
            get { return myBackAlpha; }
            set
            {
                int v = value;
                if (v > 255)
                    v = 255;
                myBackAlpha = v;
                myUpToDate = false;
                Invalidate();
            }
        }

        #endregion



    }  // End AlphaTextBox Class


}  // End namespace ZBobb


//----
View Code

用戶自定義控件(含源代碼)-圓角Panelhtml

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace myControlLibrary
{
    public partial class RoundPanel : System.Windows.Forms.Panel
    {
        public RoundPanel()
        {
            InitializeComponent();

            this.Padding = new System.Windows.Forms.Padding(0, 0, 0, 0);
            this.Margin = new System.Windows.Forms.Padding(0, 0, 0, 0);
            this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
        }


        // 圓角
        // ===============================================================================================
        private int _Radius;  // 圓角弧度

        /// <summary>圓角弧度(0爲不要圓角)</summary>
        [Browsable(true)]
        [Description("圓角弧度(0爲不要圓角)")]
        public int _setRoundRadius
        {
            get
            {
                return _Radius;
            }
            set
            {
                if (value < 0) { _Radius = 0; }
                else { _Radius = value; }
                base.Refresh();
            }
        }


        // 圓角代碼
        public void Round(System.Drawing.Region region)
        {
            // -----------------------------------------------------------------------------------------------
            // 已是.net提供給咱們的最容易的改窗體的屬性了(之前要本身調API)
            System.Drawing.Drawing2D.GraphicsPath oPath = new System.Drawing.Drawing2D.GraphicsPath();
            int x = 0;
            int y = 0;
            int thisWidth = this.Width;
            int thisHeight = this.Height;
            int angle = _Radius;
            if (angle > 0)
            {
                System.Drawing.Graphics g = CreateGraphics();
                oPath.AddArc(x, y, angle, angle, 180, 90);                                 // 左上角
                oPath.AddArc(thisWidth - angle, y, angle, angle, 270, 90);                 // 右上角
                oPath.AddArc(thisWidth - angle, thisHeight - angle, angle, angle, 0, 90);  // 右下角
                oPath.AddArc(x, thisHeight - angle, angle, angle, 90, 90);                 // 左下角
                oPath.CloseAllFigures();
                Region = new System.Drawing.Region(oPath);
            }
            // -----------------------------------------------------------------------------------------------
            else
            {
                oPath.AddLine(x + angle, y, thisWidth - angle, y);                         // 頂端
                oPath.AddLine(thisWidth, y + angle, thisWidth, thisHeight - angle);        // 右邊
                oPath.AddLine(thisWidth - angle, thisHeight, x + angle, thisHeight);       // 底邊
                oPath.AddLine(x, y + angle, x, thisHeight - angle);                        // 左邊
                oPath.CloseAllFigures();
                Region = new System.Drawing.Region(oPath);
            }
        }
        // ===============================================================================================


        public RoundPanel(IContainer container)
        {
            container.Add(this);

            InitializeComponent();
        }

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs pe)
        {
            base.OnPaint(pe);
            Round(this.Region);  // 圓角
        }

        protected override void OnResize(EventArgs eventargs)
        {
            base.OnResize(eventargs);
            base.Refresh();
        }
    }
}
View Code

[WPF]WPF4.0中的字體呈現改進程序員

[WPF]WPF4.0中的字體呈現改進
http://www.21tx.com 2009年11月01日 博客園 紫色永恆
  注意TextOptions.TextFormattingMode這個附加屬性,它提供了兩種設置:
  · Ideal – 就是WPF出現之後一直使用的方式。
  · Display – Display模式,今天的主角。
  他們都是依賴屬性,因此其設定的值能夠被子元素繼承。
  一個小例子 :
    <StackPanel> 
      <TextBlock> 
        Hello World ... Ideal text formatting 
      </TextBlock> 
      <TextBlock TextOptions.TextFormattingMode="Display"> 
        Hello World ... Display text formatting 
      </TextBlock> 
    </StackPanel>

  爽了吧。
  再作個對比:

  更重要的是,WPF4.0中同時針對東亞字體的渲染進行了優化。是的,如今它支持點陣字體了。
  對比下:


  而開啓這個新特性十分簡單,只需爲FontFamily設置一種點陣字體就能夠了。好比FontFamily="SimSun"就是使用宋體。
  文章來源:http://024hi.cnblogs.com/
 
View Code

Socket實現多人同時聊天算法

 

          

     項目結構圖                                         服務端程序
 

      

                                                            客戶端效果
 
 
 
下面這個實例是一個完整的使用Socket實現的聊天(只限於局域網,若是能提供一個高權限的IP就能夠實現相似QQ聊天),其中的原理是:首先開啓服務端,打開偵聽(任何端口爲6600的IP),下面實現的代碼:服務端+客戶端【VS2005 C#.NET 2.0】
 
【服務端】三個窗體:About.cs,ServerMain.cs,Set.cs
ServerMain.cs窗體代碼
 

using System;
using System.Text;
using System.Windows.Forms;

using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Xml;

namespace Server
{
publicpartialclass ServerMain : Form
{
public ServerMain()
{
InitializeComponent();
}

privatevoid ServerMain_Load(object sender, EventArgs e)
{
this.CmdStar.Enabled =true;
this.CmdStop.Enabled =false;
}

privatevoid 配置參數ToolStripMenuItem_Click(object sender, EventArgs e)
{
Set TSet =new Set();
TSet.ShowDialog();
}

privatevoid 關於ToolStripMenuItem_Click(object sender, EventArgs e)
{
About TAbout =new About();
TAbout.Show();
}
///<summary>
/// 得到XML文件中的端口號
///</summary>
///<returns></returns>
privateint GetPort()
{
try
{
XmlDocument TDoc =new XmlDocument();
TDoc.Load("Settings.xml");
string TPort = TDoc.GetElementsByTagName("ServerPort")[0].InnerXml;
return Convert.ToInt32(TPort);

}
catch { return6600; }//默認是6600
}

//聲明將要用到的類
private IPEndPoint ServerInfo;//存放服務器的IP和端口信息
private Socket ServerSocket;//服務端運行的SOCKET
private Thread ServerThread;//服務端運行的線程
private Socket[] ClientSocket;//爲客戶端創建的SOCKET鏈接
privateint ClientNumb;//存放客戶端數量
privatebyte[] MsgBuffer;//存放消息數據

privatevoid CmdStar_Click(object sender, EventArgs e)
{
ServerSocket =new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//提供一個 IP 地址,指示服務器應偵聽全部網絡接口上的客戶端活動
IPAddress ip = IPAddress.Any;
ServerInfo=new IPEndPoint(ip,this.GetPort());
ServerSocket.Bind(ServerInfo);//將SOCKET接口和IP端口綁定
ServerSocket.Listen(10);//開始監聽,而且掛起數爲10

ClientSocket =new Socket[65535];//爲客戶端提供鏈接個數
MsgBuffer =newbyte[65535];//消息數據大小
ClientNumb =0;//數量從0開始統計

ServerThread =new Thread(new ThreadStart(RecieveAccept));//將接受客戶端鏈接的方法委託給線程
ServerThread.Start();//線程開始運行

CheckForIllegalCrossThreadCalls =false;//不捕獲對錯誤線程的調用

this.CmdStar.Enabled =false;
this.CmdStop.Enabled =true;
this.StateMsg.Text ="服務正在運行..."+" 運行端口:"+this.GetPort().ToString();
this.ClientList.Items.Add("服務於 "+ DateTime.Now.ToString() +" 開始運行.");
}

//接受客戶端鏈接的方法
privatevoid RecieveAccept()
{
while (true)
{
//Accept 以同步方式從偵聽套接字的鏈接請求隊列中提取第一個掛起的鏈接請求,而後建立並返回新的 Socket。
//在阻止模式中,Accept 將一直處於阻止狀態,直到傳入的鏈接嘗試排入隊列。鏈接被接受後,原來的 Socket 繼續將傳入的鏈接請求排入隊列,直到您關閉它。
ClientSocket[ClientNumb] = ServerSocket.Accept();
ClientSocket[ClientNumb].BeginReceive(MsgBuffer, 0, MsgBuffer.Length, SocketFlags.None,
new AsyncCallback(RecieveCallBack),ClientSocket[ClientNumb]);
lock (this.ClientList)
{
this.ClientList.Items.Add(ClientSocket[ClientNumb].RemoteEndPoint.ToString() +" 成功鏈接服務器.");
}
ClientNumb++;
}
}

//回發數據給客戶端
privatevoid RecieveCallBack(IAsyncResult AR)
{
try
{
Socket RSocket = (Socket)AR.AsyncState;
int REnd = RSocket.EndReceive(AR);
//對每個偵聽的客戶端端口信息進行接收和回發
for (int i =0; i < ClientNumb; i++)
{
if (ClientSocket[i].Connected)
{
//回發數據到客戶端
ClientSocket[i].Send(MsgBuffer, 0, REnd,SocketFlags.None);
}
//同時接收客戶端回發的數據,用於回發
RSocket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0, new AsyncCallback(RecieveCallBack), RSocket); 
}
}
catch { }

}

privatevoid CmdStop_Click(object sender, EventArgs e)
{
ServerThread.Abort();//線程終止
ServerSocket.Close();//關閉socket

this.CmdStar.Enabled =true;
this.CmdStop.Enabled =false;
this.StateMsg.Text ="等待運行...";
this.ClientList.Items.Add("服務於 "+ DateTime.Now.ToString() +" 中止運行.");
}

privatevoid ServerMain_FormClosed(object sender, FormClosedEventArgs e)
{
ServerThread.Abort();//線程終止
ServerSocket.Close();//關閉SOCKET
Application.Exit();
}
}
}

 
 
 
Set.cs代碼
 

using System;
using System.Text;
using System.Windows.Forms;

using System.Xml;

namespace Server
{
publicpartialclass Set : Form
{
public Set()
{
InitializeComponent();
}

privatevoid Set_Load(object sender, EventArgs e)
{
this.GetPort();
}

privatevoid GetPort()
{
try
{
XmlDocument TDoc =new XmlDocument();
TDoc.Load("Settings.xml");
string TPort = TDoc.GetElementsByTagName("ServerPort")[0].InnerXml;
this.Port.Text = TPort;
}
catch { }
}

privatevoid CmdSave_Click(object sender, EventArgs e)
{
try
{
XmlDocument TDoc =new XmlDocument();
TDoc.Load("Settings.xml");

XmlElement Root = TDoc.DocumentElement;
XmlElement newElem = TDoc.CreateElement("ServerPort");
newElem.InnerXml =this.Port.Text;

Root.ReplaceChild(newElem, Root.LastChild);

TDoc.Save("Settings.xml");

MessageBox.Show("參數保存成功!");
this.Close();
}
catch
{
MessageBox.Show("參數寫入XML文件不成功!");
}
}
}
}

 
 
附:
Settings.xml
 
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<ServerPort>6600</ServerPort>
 
 
 
 
 
******************************************************************************************************
 
 
【客戶端代碼】ClientMain.cs窗體
 

using System;
using System.Text;
using System.Windows.Forms;

using System.Net;
using System.Net.Sockets;

namespace Client
{
publicpartialclass ClientMain : Form
{
public ClientMain()
{
InitializeComponent();
}

private IPEndPoint ServerInfo;
private Socket ClientSocket;
//信息接收緩存
private Byte[] MsgBuffer;
//信息發送存儲
private Byte[] MsgSend;

privatevoid ClientMain_Load(object sender, EventArgs e)
{
this.CmdSend.Enabled =false;
this.CmdExit.Enabled =false;
//定義一個IPV4,TCP模式的Socket
ClientSocket =new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
MsgBuffer =new Byte[65535];
MsgSend =new Byte[65535];
//容許子線程刷新數據
CheckForIllegalCrossThreadCalls =false;
this.UserName.Text =Environment.MachineName;
}

privatevoid CmdEnter_Click(object sender, EventArgs e)
{
//服務端IP和端口信息設定,這裏的IP能夠是127.0.0.1,能夠是本機局域網IP,也能夠是本機網絡IP
ServerInfo =new IPEndPoint(IPAddress.Parse(this.ServerIP.Text), Convert.ToInt32(this.ServerPort.Text));

try
{
//客戶端鏈接服務端指定IP端口,Sockket
ClientSocket.Connect(ServerInfo);
//將用戶登陸信息發送至服務器,由此可讓其餘客戶端獲知
ClientSocket.Send(Encoding.Unicode.GetBytes("用戶: "+this.UserName.Text +" 進入系統!\n"));
//開始從鏈接的Socket異步讀取數據。接收來自服務器,其餘客戶端轉發來的信息
//AsyncCallback引用在異步操做完成時調用的回調方法
ClientSocket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), null);

this.SysMsg.Text +="登陸服務器成功!\n";
this.CmdSend.Enabled =true;
this.CmdEnter.Enabled =false;
this.CmdExit.Enabled =true;
}
catch
{
MessageBox.Show("登陸服務器失敗,請確認服務器是否正常工做!");
}
}

privatevoid ReceiveCallBack(IAsyncResult AR)
{
try
{
//結束掛起的異步讀取,返回接收到的字節數。 AR,它存儲此異步操做的狀態信息以及全部用戶定義數據
int REnd = ClientSocket.EndReceive(AR);

lock (this.RecieveMsg)
{
this.RecieveMsg.AppendText(Encoding.Unicode.GetString(MsgBuffer, 0, REnd));
}
ClientSocket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0, new AsyncCallback(ReceiveCallBack), null);

}
catch
{
MessageBox.Show("已經與服務器斷開鏈接!");
this.Close();
}

}
//點擊發送以後沒有直接添加到信息列表中,而是傳到服務器,由服務器轉發給每一個客戶端
privatevoid CmdSend_Click(object sender, EventArgs e)
{

MsgSend = Encoding.Unicode.GetBytes(this.UserName.Text +"說:\n"+this.SendMsg.Text +"\n");
if (ClientSocket.Connected)
{
//將數據發送到鏈接的 System.Net.Sockets.Socket。
ClientSocket.Send(MsgSend);
this.SendMsg.Text ="";

}
else
{
MessageBox.Show("當前與服務器斷開鏈接,沒法發送信息!");
}
}

privatevoid CmdExit_Click(object sender, EventArgs e)
{
if (ClientSocket.Connected)
{
ClientSocket.Send(Encoding.Unicode.GetBytes(this.UserName.Text +"離開了房間!\n"));
//禁用發送和接受
ClientSocket.Shutdown(SocketShutdown.Both);
//關閉套接字,不容許重用
ClientSocket.Disconnect(false);
}
ClientSocket.Close();

this.CmdSend.Enabled =false;
this.CmdEnter.Enabled =true;
this.CmdExit.Enabled =false;
}

privatevoid RecieveMsg_TextChanged(object sender, EventArgs e)
{
this.RecieveMsg.ScrollToCaret();
}

privatevoid SendMsg_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyValue ==13)
{
e.Handled =true;
this.CmdSend_Click(this, null); 
}
}
}
}

個性簽名:作要作好,作到不倫不類不如不作。
 
View Code

WPF入門指南教程數據庫

http://tech.it168.com/zt/wpf/index.html
WPF入門指南一:快速入門
【IT168技術文檔】
  摘要:WPF是微軟推出的基於Windows Vista的用戶界面框架,屬於.NET Framework 3.0的一部分。本文旨在經過一些簡單的示例,讓你對WPF有一個概要的認識。

主要內容
1.概述
2.WPF的組成
3.WPF的編程架構
4.XAML
5.WPF中的控件集

一.概述
WPF (Windows Presentation Foundation)是微軟推出的基於Windows Vista的用戶界面框架,屬於.NET Framework 3.0的一部分。它提供了統一的編程模型、語言和框架,真正作到了分離界面設計人員與開發人員的工做;同時它提供了全新的多媒體交互用戶圖形界面。

二.WPF的組成
WPF的構成組件以下所示,其中PresentationFramework、PresentationCore、milcore三部分是WPF的核心組件: 
 

三.WPF的編程架構 
   


  WPF總體的編程架構如圖2所示,能夠看到,它其中包含了文檔服務、用戶界面服務、多媒體服務以及一些其它的基本服務。在用戶界面服務中,提供了應用程序 服務、部署服務、控件集、佈局以及數據綁定等;在多媒體服務中能夠看到WPF幾乎能夠處理全部的媒體類型,包括圖片、音頻、視頻、動畫、2D、3D、文本 等。


四.XAML
在WPF中提供了兩種API,一種是用於普通編程的API,好比咱們能夠用C#、VB.NET等 語言進行編程,另外一種是基於XML的API,稱爲XAML(Extensible Application Markup Language),引入XAML使得UI代碼和應用程序邏輯代碼徹底分離,它是一種標記語言,支持聲明式編程,因爲XAML是基於XML的,因此它擁有 XML的全部規則和定義,很是容易進行擴展。看下面兩段簡單的代碼:
XAML代碼:

<Button Name="bt1" Background="Blue" Foreground="Red">

  This is a button

</Button>

C#代碼:

Button btn1 = new Button();

btn1.Content = "This is a button";

btn1.Background = Brushes.Blue;

btn1.Foreground = Brushes.Red;

 
五.WPF控件集
  在WPF中,提供了很是豐富的控件集,如表一所示:
分類                                          控件
Editing                                     CheckBox, ComboBox, PasswordBox, RadioButton, RichTextBox, Slider, TextBox
List                                         Selection ListBox, ListView, TreeView
User Information                 Label, ProgressBar, Popup, ToolTip
Action                        Button, ContextMenu, Menu, Separator, StatusBar, Thumb, ToolBar
Appearance             Border, BulletDecorator, Decorator, Image, Viewbox
Dialog boxes           OpenFileDialog, PrintDialog, SaveFileDialog
Containers               Expander, GroupBox, RepeatButton, ScrollBar, ScrollViewer, TabControl
Layout                       Canvas, DockPanel, Grid, GridSplitter, Panel, StackPanel, VirtualizingStackPanel, WrapPanel
                                  Navigation Frame, Hyperlink
Documents       DocumentViewer, FlowDocumentPageViewer, FlowDocumentReader, FlowDocumentScrollViewer

以上只是對WPF做一個概要的介紹,在後面的文章中,我會詳細的去講WPF中一些控件的用法\數據綁定、資源處理、樣式與模版、文檔編程、多媒體編程等內容。
WPF開發簡介
【IT168技術文檔】
    要贏得世界,必須在恰當的時間作出恰當的事。這一點,微軟作到了。歷經微軟DOS,Win 31, Win95, Win 98, Win 2k, WinXP, 一路走來,一次比一次熱鬧,一個比一個精彩、炫麗。Vista的宣傳更是轟轟烈烈,喧囂日上。Microsoft老是有能力在恰當的時間製造出意料中的轟 動,Vista一經推出便讓IT業界趨之若鶩。

    1、掀起你的蓋頭來,讓我看看你的臉——.NET Framework 3.0

    與微軟對Vista鑼鼓宣天的推廣相比,對與之相伴的.NET Framework 3.0卻顯得很是低調。微軟沒將它做爲宣傳重點。全部的幕後英雄老是默默無聞。對普通用戶而言,他們不關心舞臺背後的一切,它們更關心的是前臺的表現,是 那種炫麗奢華的感受。真正熱捧幕後英雄的,也是咱們這羣在幕後默默耕耘的程序員們。咱們對.NET Framework 3.0有種天然而然的親切感,由於咱們將在此架構上爲Vista編寫各類程序。

    做爲微軟精心打造的新一代.NET架構,.NET Framework 3.0在Vista時代擔當最主要的應用程序接口。.NET曾被微軟寄予厚望和重託,比爾·蓋茨先生欲用之來實現一統江湖之志。事實證實,微軟對.Net 的推出是成功。愈來愈多的應用程序開發者已從Win32和JAVA架構轉向.NET開發,愈來愈多的網站開發人員愛上了asp.net。這讓微軟看到了希 望,他們躊躇滿志,他們加緊了對.NET的研發,升級速度愈來愈快,.NET Framework 3.0就是微軟在.NET Framework 2.0後最重要的升級。甚至有人驚呼,咱們象劉翔同樣地飛奔也跟不上微軟的速度!

    到了.NET Framework 3.0,.NET令程序員更趁心、並逐漸「傾心」。在衆多的開發環境,微軟的開發系列產品,讓它在與其它架構的較量中逐漸搶得上風而獨在鰲頭。

    Vista集成.NET Framework 3.0,可謂珠聯璧合。能夠這麼講,沒有.Net Framework 3.0這個幕後英雄,Vista會黯淡不少。

    .NET Framework 3.0(原代號「WinFX」),它構建於.NET Framework 2.0之上,同時新增了四個大組件:Windows Presentation Foundation(WPF),Windows Workflow Foundation(WWF)、Windows Communication Foundation(WCF)和Windows CardSpace,而這些恰是NET Framework 3.0最具價值的部分。

    其中,最使人矚目的是WPF(原來代號爲「Avalon」),它是微軟全新的圖形界面引擎,爲各類應用提供統一的界面技術。程序員在WPF的幫助下,要開 發出媲美Mac程序的酷炫界面已再也不是高不可攀的奢望。Windows Workflow Foundation用於開發基於工做流的應用程序;Windows Communication Foundation則用於開發面向服務的應用程序,它是新一代通信和網絡開發框架,爲不一樣類型的網絡應用程序提供了一個通用的編程模型,尤爲是包含其中 的PeerChannel模塊,以難以置信的方式消除了開發P2P應用程序的複雜性和困難度;而Windows CardSpace提供數字標誌用戶控件。若是把這些林林總總小巧精美的產品比作是珍珠的話,那麼,須要一根金線將這些珍珠串起來,珍珠才更有價值,珍珠 才熠熠生輝。而串起這顆珍珠的金線,就是.NET Framework 3.0。

    2、搭建Vista程序的開發環境

    1. 若是你使用的是Vista操做系統,那麼,因爲Vista已集成了.NET Framework 3.0,因此沒必要再單獨安裝.NET Framework 3.0。可是,若是你是Windows XP或Windows 2003下開發,則必須安裝.NET Framework 3.02. 要開發Vista應用程序,則必須有Vista SDK開發包的支持。Windows SDK for Windows Vista and .NET Framework 3.0包含了大量庫文件、頭文件、文檔、示例和工具。因爲Vista操做系統的推出時間比Visual Studio 2005開發工具要晚,所以,針對Windows Vista開發的Windows SDK和.NET Framework 3.0並未集成到Visual Studio 2005中。所以,必須下載此SDK,個頭不小(當前最新版本是1.15GB),下載和安裝時須要你足夠的耐心和毅力,相信這個對於咱們這些一貫好脾氣的 程序員產不在話下。

    3. 安裝Visual Studio 2005(中/英文版都可,建議你習慣使用英文版,由於全部版本都是英文版的首先投放市場,而英文版到中文版,現實有三個月以上的時間差,而對於咱們這些 母語非英語的開發人員,英語關是一個必須跨越的檻,緣引魯迅老先生的名言改編一下:世界並無本身熟悉的路,走得多了,也就熟了路!英文看得多了,天然也 就熟悉了)。

    4. 下載並安裝Visual Studio 2005 SP1,Visual Studio 2005 SP1 Update for Windows Vista。Visual Studio 2005 SP1修補Visual Studio 2005許多錯誤,並加強了一些性能,因爲該補丁包要對Visual Studio 2005和Vista進行詳細檢測,安裝比較耗時。

    5.爲了方便程序員開發.NET Framework 3.0下的新型應用,微軟還推出了VS 2005 Extensions for .NET Framework 3.0(Workflow)和VS 2005 Extensions for .NET Framework 3.0(WCF&WPF)擴展組件,爲Visual Studio 2005提供開發Windows Workflow Foundation、Windows Communication Foundation和Windows Presentation Foundation類型應用程序的模版。

    6. 若是要學WPF,特別建議你安裝Expression Blend(目前版本是Expression Blend 2 Agust Preview版)。這樣,你能夠在vs2005新建一個WPF項目,而後用Blend打開此項目,在Blend中對它進行界面設計和美化等工做,而後再 轉到vs2005下進行程序編碼(兩個程序之間支持來回切換)。
3、WPF簡介

    Windows Presentation Foundation(WPF)是微軟新一代圖形系統,運行在.NET Framework 3.0架構下,爲用戶界面、2D/3D 圖形、文檔和媒體提供了統一的描述和操做方法。基於DirectX 9/10技術的WPF不只帶來了史無前例的3D界面,並且其圖形向量渲染引擎也大大改進了傳統的2D界面,好比Vista中的半透明效果的窗體等都得益於 WPF。微軟還提供了專門的界面開發語言XAML(eXtensible Application Markup Language,可擴展應用程序標記語言),使得界面描述代碼和程序代碼得以分開,從而提升了開發效率並有利於團隊開發。

    XAML是微軟爲構建下一代應用程序界面而建立的一種新的基於XML的描述性語言,它可對WPF程序的全部界面元素進行定製,從而構成具備WPF風格的界 面,並最終造成一個組織良好的XML文檔。因爲它最新會被編譯成.Net後臺代碼,所以它可以同後臺進行邏輯處理的.NET語言如C#、J#、C++、 VB等協同工做,其工做性質相似於ASP.NET中的HTML。同HTML同樣,XAML既能夠直接編碼,也能夠由專門的工具生成。目前最好的XAML編 輯器就是上面我建議你安裝的Expression Blend了,但你也能夠使用Windows SDK for Windows Vista中的XAMLPad,但功能卻顯得很是簡單。

    實際上,大多數WPF程序將同時包含XAML代碼和程序代碼,首先使用XAML定義程序界面,而後再用.NET語言編寫相應的邏輯代碼。跟ASP.NET 相似,邏輯代碼既能夠直接嵌入XAML文件中,也能夠將它保存爲獨立的代碼文件。儘管XAML並不是設計WPF程序所必須,按照傳統方式使用程序代碼來實現 界面依然有效,可是若是使用XAML,界面設計和邏輯設計能夠徹底分離,不但使程序的開發和維護更加方便,並且在團隊開發中,能夠使程序員專一於業務邏輯 的實現,而將界面設計交由專業人員來完成,從而使各種人員在項目中各盡其能各展其長,開發出功能強大、界面一流的WPF程序。
 
WPF指南之XAML概述
 
【IT168 技術文檔】
    在我開始看WPF文檔開始的幾天裏,腦子裏造成了一種錯誤的想法:WPF不就是XAML碼?當時的感受就是鬱悶啦,我學習WPF還得弄這個東西。給人的第一感受就是WPF很複雜。雖然對WPF的熟悉和了解還不是特別多,但如今已經知道這確實是一種錯誤的想法。

    Charles Petzold先生曾有一篇文章介紹了WPF、XAML的一些關係(The Two APIs)。文章中說明了WPF爲何很複雜:由於WPF有兩套API,一套用於普通的編碼訪問(好比C#、VB.NET等其中.NET支持的語言。而另 外一套就是基於XML的API,被稱爲XAML(Extensible Application Markup Language)。

    XAML實現UI代碼和應用程序邏輯代碼的分離。在.NET 3.0和Windows Vista中,XAML與WPF一塊兒創建整個的UI。因爲XAML是基於XML的,因此每一個XAML代碼都確定是一個完整的XML文件。XAML繼承了 XML全部的定義和規則。XAML與其餘XML擴展不一樣之處就是他所表示的意義。每一個XAML元素是一個.NET CLR類。基於XML使得咱們很是容易擴展和操做XAML。利用XAML的WPF這種關係,開發人員能夠單獨的設計漂亮的UI,也許真正的美工會更多的出 現。咱們能夠把程序邏輯寫在單獨的文件或者是內聯嵌入到XML文件。

    在XAML中使用得最多的XML功能應該有三個:命名空間、屬性和子元素。

    先看一個簡單的XAML的例子:

<Window x:Class="FirstXAML.Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="FirstXAML" Height="200" Width="300"

>

<Canvas>

</Canvas>

</Window> 

 
    其中的xmlns就是XML中的名字空間,在W3C中xmlns是以下定義的:

    XML namespaces provide a simple method for qualifying element and attribute names used in Extensible Markup Language documents by associating them with namespaces identified by URI references.

    簡單地說就是xmlns提供了一種方法把URI引用的名字空間定義爲當前XML文件的元素和屬性的默認命名空間。這裏表示當前這個XML文檔,也就是咱們的XAML文件,它的默認的命名空間就是http://schemas.microsoft.com/winfx/2006/xaml/presentation。

    而後是屬性和子元素,XML對屬性的表示除了能夠用Property外,還能夠用子元素,在XAML中也是如此,看一個簡單的例子:

<Button Width="6">

<Button.Background>White</Button.Background>

</Button> 
 
     例子當中就使用了屬性和子元素兩種方式來指定屬性。其中的Width是直接用屬性表示,Background屬性是用子元素表示。在多數時候,但不是全部,你能夠自由選擇這兩種表示方式之一。

    XAML被編譯爲BAML(Binary Application Markup Language)文件。一般,BAML文件比XAML更小,編譯後的BAML都是Pre-tokenized的,這樣在運行時能更快速的加載、分析 XAML等等。這些BAML文件被以資源的形式嵌入到Assembly當中。同時生成相應的代碼(文件名稱是**.g.cs或者**.g.vb),這些代 碼根據XAML元素分別生成命名的 Attribute字段。以及加載BAML的構造函數。

    最後,關於XAML的優勢,我附上一點翻譯過來的條款,可能更直觀。

    XAML除了有標記語言、XML的優勢外,還有以下一些優勢:

    用XAML設計UI更簡單
    XAML比其餘的UI設計技術所需編碼更少。
    XAML設計的UI方便轉移、方便在其餘環境提交。好比在Web或Windows Client。
    用XAML設計動態UI很是容易
    XAML給UI設計人員帶來新的革命,如今全部的設計人員再也不須要.NET開發的知識一樣能夠設計UI。在不遠的未來,終端用戶能夠看到更漂亮的UI。
WPF指南之WPF的結構
【IT168技術文檔】
    WPF進入咱們的生活已經有一段時間。我的認爲在UI的實踐中,用戶須要的是易於操做的,更加絢麗的界面。這兩個應該是最基本、也是最重要的宗旨。而對於 開發人員就是要用最簡單的方法開發出儘量漂亮的界面,而且效率也不能太差。除了在一些Web開發和特殊的應用中,不多有開發組配備單獨的美工,至少目前 是這樣吧!根據本身目前對WPF的瞭解程度,感受WPF在其中某些方面確實有超強的震撼力。

    客觀上講,Vista操做系統確實給咱們帶來了無可比擬的視覺效果。我本身深有體會,在近2個月的時間裏天天都是在Vista下的開發,回家後看到XP系統,始終有些不爽的感受。

    WPF能夠認爲是MS利用原有.NET框架的一些特點,加上DirextX的產物。從下圖的WPF組件中,咱們能夠看出最底層仍然是一些內核API。(如下兩張圖片都來自互聯網。)


    其中紅色顯示的組件是WPF的核心。Milcore是一個和DirectX交互的非託管組件,非託管代碼能帶給咱們更高效的處理,能更好的和 DirextX交互。WPF的全部顯示都是由Dirext完成的。milcore中一個很是重要的功能就是Composition引擎,這個引擎對效率的 要求很高,它的具體做用稍後介紹。因此milcore放棄了一些CLR的特徵來換取效率。而另外兩個紅色的組件都是創建在CLR基礎之上,利用了.NET 的優點。

    至於其中的User32組件有什麼做用,偶目前的知道的就是在WPF的某些應用場景中爲了某些兼容須要使用User32,其中就有DWM(桌面窗口管理)。DWM的內容又能夠寫上一大堆,感興趣的朋友能夠看SDK文檔。

    咱們除了關心WPF的基本結構外,更重要的 是WPF提供了什麼功能,請看下圖:




    圖中的每一個黃色塊都是一種媒體類型。這就表示WPF能夠處理幾乎全部的媒體類型:位圖、3D、音頻、視頻和文本等等。經過WPF,它集成了如今的 GDI/GDI+、D3D/OPENGL以及多媒體的DSHOW等等。全部的東西都是等同對象,無論的3D仍是2D,或者文本。

    結構圖中的Animate塊貫串了整個的結構,由於在WPF中咱們能夠對全部的可視內容進行動畫操做。這是很是讓人期待的功能。Animate下面咱們再 次看到了Composition引擎,前面提到過它是位於milcore組件中。開發過程當中,咱們的界面元素功能有多種,好比圖片,視頻等等,最後顯示到 窗口的內容能夠認爲只是一張圖片(準確說是Surface)。這個引擎的做用就是合成這些圖片和視頻元素最後進行提交顯示。
WPF的數據處理
【IT168 技術文檔】
    數據綁定,這是WPF提 供的一個真正的優勢。除了能夠用在傳統的綁定環境中,數據綁定已經被擴展應用到控件屬性上。學習應用數據綁定,也能真正的體現XAML的好處。到底什麼是 數據綁定呢?也許你從字面上已經理解的很不錯了。經過數據綁定,咱們在應用程序UI和程序邏輯之間創建了一種聯繫。正常創建綁定後,在數據的值發生改變 後,綁定到數據的元素將自動更新、體現出數據的變化。
    一樣,咱們先看幾個相關的知識點:

    1、DataContext屬性。設置DataContext屬性,其實就是指定數據上下文。那麼數據上下文又是什麼呢?又是一個新的概念:數據上下文允 許元素從它的父元素繼承數據綁定的數據源。很簡單,在某個元素的DataContext中指定的值,那麼在這個元素的子元素也能夠使用。注意,若是咱們修 改了FrameworkElement或者FrameworkContentElement元素的DataContext屬性,那麼元素將再也不繼承 DataContext值。也就是說新設置的屬性值將覆蓋父元素的設置。如何設置DataContext屬性,稍後說明。

    2、數據源的種類。也許,WPF提供的數據綁定只是實現了一項普通的功能而已,可是,WPF中所支持的多種數據源使得它的數據綁定功能將更增強大。如今,WPF支持以下四種綁定源:

    (1)、任意的CLR對象:數據源能夠是CLR對象的屬性、子屬性以及Indexers。對於這種類型的綁定源,WPF採用兩種方式來獲取屬性值:A)、 反射(Reflection);B)、CustomTypeDescriptor,若是對象實現了ICustomTypeDescriptor,綁定將使 用這個接口來獲取屬性值。
    (2)、XML結點:數據源能夠是XML文件片段。也能夠是XMLDataProvider提供的整個XML文件。
    (3)、ADO.NET數據表。我對ADO.NET的瞭解不夠,在此不作過多評論。
    (4)、Dependency對象。綁定源能夠是其它DependencyObject的DependencyProperty屬性。

    3、數據綁定的方式:(1)、OneWay,單一方向的綁定,只有在數據源發生變化後纔會更新綁定目標。(2)、TwoWay,雙向綁定,綁定的兩端任何 一端發生變化,都將通知另外一端。(3)、OneTime,只綁定一次。綁定完成後任何一端的變化都不會通知對方。

    在上面的第二點我介紹了數據源的種類,注意這裏的概念和下面要說明的指定數據源的方式的區別。目前,指定數據源有三種方式,咱們能夠經過任何一種方式來指定上述的任何一種數據源:

    (1)、經過Source標記。咱們能夠在使用Binding使用Source標記顯式指定數據源。
    (2)、經過ElementName標記。這個ElementName指定了一個已知的對象名稱,將使用它做爲綁定數據源。
    (3)、經過RelativeRource標記。這個標記將在後面說明ControlTemplate和Style時再進行說明。

    如今咱們說明了不少和數據源相關的內容。可是再綁定的時候,咱們還須要指定綁定對象的屬性名稱。因此WPT提供了一個Path標記。它被用來指定數據源的屬性。也便是數據源將在數據源對象的Path所指定的屬性上尋找屬性值。
  在介紹WPF數據綁定源的種類時,第一種就是任意的CLR對象。這裏須要注意的是WPF雖然支持任意的CLR對象,可是一個普通的CLR對象類卻不行。咱們還須要在CLR對象類上實現一種變化通知機制。

    WPF把這種通知機制封裝在了INotifyPropertyChanged接口當中。咱們的CLR對象類只要實現了這個接口,它就具備了通知客戶的能力,一般是在屬性改變後通知綁定的目標。

    下面是一個簡單的例子,實現了一個支持通知功能的Camera類:
using System;
using System.ComponentModel;
using System.Windows.Media.Media3D;
namespace LYLTEST
{
public class Camera : INotifyPropertyChanged
{
private PerspectiveCamera m_Camera;
public event PropertyChangedEventHandler PropertyChanged;
public Camera()
{
m_Camera = new PerspectiveCamera();
}
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public PerspectiveCamera CameraProp
{
get { return m_Camera; }
set
{
if (value != m_Camera)
{
this.m_Camera = value;
NotifyPropertyChanged("CameraProp");
}
}
}
}
}
 
    這一段代碼很簡單,首先引入類中使用的INotifyPropertyChanged和PerspectiveCamera須要的名字空間。這裏與普通 CLR類的區別在於首先有一個公有的PropertyChangedEventHandler事件類型。而後咱們在.NET屬性包裝CameraProp 判斷屬性是否發生了變化,若是是,則用當前是屬性名稱字符串「CameraProp」調用另外一個私有函數NotifyPropertyChanged。由 它根據屬性的名稱構造一個PropertyChangedEventArgs對象,並完成對PropertyChanged的調用。它纔是屬性變化時真正 應該調用的一個通知事件。

    最後一點,若是咱們須要通知因此的屬性都發生了變化,則將上面的屬性字符串「CameraProp」用參數NULL替代便可。
  前面講過,經過實現INotifyPropertyChanged,咱們能夠改變使任意的CLR對象支持WPF的綁定源。可是, INotifyPropertyChanged一般只應用在單個的類屬性上。在現實應用中,咱們還會遇到另一種狀況:咱們須要監視某一堆的數據是否發生 變化。也就是說咱們綁定的數據源再也不是一個單獨數據對象。好比,綁定源是一個數據表時,咱們但願在表中任何一條數據發生變化就能獲得通知。(這裏暫不考慮 WPF綁定對ADO.NET的支持。)

    WPF提供了一個ObservableCollection類,它實現了一個暴露了INotifyPropertyChanged的數據集合。也就是說我 們不須要本身對每一個單獨的數據實現INotifyPropertyChanged結構。咱們先看看如何實現一個簡單的綁定數據集合。
namespace NSLYL
{
public class LYLDataObj
{
public LYLDataObj(string name, string description)
{
this.name = name;
this.description = description;
}
public string Name
{
get { return name; }
set { name = value; }
}
public string Description
{
get { return description; }
set { description = value; }
}
private string name;
private string description;
}
public class LYLDataObjCol : ObservableCollection<LYLDataObj>
{
public LYLDataObjCol()
{
this.Add(new LYLDataObj("Microsot", "Operating System"));
this.Add(new LYLDataObj("Google", "Search"));
}
}
}
 
代碼很簡單,基本上就是這樣的一個模板。而後,咱們就能夠把LYLDataObjCol綁定到一個須要多項數據的Element之上,好比ListBox、ComboBox等等。
<ListBox ItemsSource="{StaticResource dataObj}" .../>
 
   綁定以後,只要個人LYLDataObjCol對象發送了變化,ListBox、ComboBox的數據也會有對應的變化。

    到如今,咱們已經知道在綁定的時候有兩種指定數據源的方式:1、DataContext,關於它咱們在這個Post有簡單介紹。2、直接用Binding 類的Source屬性。那麼,咱們在使用的時候如何區別呢?首先,Source的優先級比DataContext高,只有Source不存在,或者在當前 Source到不到須要的屬性時纔會查找DataContext。除此以外,這二者沒有真正的區別,只是建議使用Source,它能有助於咱們調試應用程 序。由於經過它能夠明確的獲得Source的信息。而DataContext支持一種繼承。能夠在父Element指定Source源。這同時也成爲了 DataContext的一個優勢:若是多個Element須要綁定同一個Source源,那麼咱們只須要在一個地方指定DataContext,就能夠 在其子Element使用。
WPF中的命令簡述
【IT168技術文檔】 
    在咱們平常的應用程序操做中,常常要處理各類各樣的命令和進行相關的事件處理,好比須要複製、粘貼文本框中的內容;上網查看網頁時,可能須要返回上一網頁 查看相應內容;而當咱們播放視頻和多媒體時,咱們可能要調節音量,快速拖動到咱們想看的片斷等等。在Winform編程中,咱們常用各類各樣的控件來 解決此類問題,固然咱們也必須編寫一堆代碼來處理各類各樣的命令和事件處理。那麼,Windows Presentation Foundation (WPF)做爲微軟新一代圖形圖像支援系統,它是如何處理這些命令及事件的呢?

    在WPF中,許多控件都自動集成了固有的命令集。好比文本框TextBox就提供了複製(Copy),粘貼(Paste),裁切(Cut),撤消(Undo)和重作(Redo)命令等。

   WPF提供經常使用應用程序所用的命令集,經常使用的命令集包括:ApplicationCommands, ComponentCommands, NavigationCommands, MediaCommands和EditingCommands。

ApplicationCommands(應用程序命令):
CancelPrint:取消打印
Close:關閉
ContextMenu:上下文菜單
Copy:複製
CorrectionList: Gets the value that represents the Correction List command.
Cut:剪切
Delete:刪除
Find:查找
Help:幫助
New:新建
NotACommand:不是命令,被忽略
Open:打開
Paste:粘貼
Print:打印
PrintPreview:打印預覽
Properties:屬性
Redo:重作
Replace:取代
Save:保存
SaveAs:另存爲
SelectAll:選擇全部的
Stop:中止
Undo:撤消

ComponentCommands(組件命令):
ExtendSelection:後接Down/Left/Right/Up, 好比:ExtendSelectionDown(Shift+Down,Extend Selection Down),ExtendSelectionLeft等
Move:後接Down/Left/Right/Up, 如:MoveDown
MoveFocus:後接Down/Forward/Back/Up, 如:MoveFocusDown
MoveFocusPage:後接Down/Up,如:MoveFocusPageUp
MoveTo:後接End/Home/PageDown/PageUp,好比:MoveToPageDown
ScrollByLine
ScrollPage:後接Down/Left/Right/Up,好比:ScrollPageLeft
SelectTo:End/Home/PageDown/PageUp,好比:SelectToEnd

NavigationCommands(導航命令):
Browse瀏覽: 後接Back/Forward/Home/Stop, 好比:BrowseBack
縮放顯示:DecreaseZoom, IncreaseZoom, Zoom
Favorites(收藏)
頁面:FirstPage, LastPage, PreviousPage, NextPage,GoToPage
NavigateJournal
Refresh(刷新)
Search(搜索)

MediaCommands(多媒體控制命令):
Treble高音:DecreaseTreble,IncreaseTreble
Bass低音:BoostBass,DecreaseBass,IncreaseBass
Channel頻道:ChannelDown,ChannelUp
MicrophoneVolume麥克風音量調節:DecreaseMicrophoneVolume,IncreaseMicrophoneVolume,MuteMicrophoneVolume
ToggleMicrophoneOnOff:麥克風開關
Volume音量: DecreaseVolume,IncreaseVolume,MuteVolume
Rewind, FastForward(回放,快進)
Track軌道:PreviousTrack,NextTrack [上一段(節)]
Play,Pause,Stop,Record(播放,暫停,中止,錄製)
TogglePlayPause
Select選擇

EditingCommands(編輯/排版類命令):
Align對齊:AlignCenter,AlignJustify,AlignLeft,AlignRight(居中,撐滿,左對齊,右對齊)
Backspace退格
TabForward,TabBackward(Tab前縮,Tab向後)
FontSize字體大小:DecreaseFontSize,IncreaseFontSize
Indentation縮排:DecreaseIndentation, IncreaseIndentation
Delete刪除: Delete選中部分,DeleteNextWord:刪除後一字,DeletePreviousWord:刪除前一字
EnterLineBreak:換行
EnterParagraphBreak:換段
CorrectSpellingError/IgnoreSpellingError:糾正/忽略拼寫錯誤
MoveUpByLine,MoveDownByLine: 上/下移一行,
MoveUpByPage,MoveDownByPage: 上/下移一頁
MoveUpByParagraph,MoveDownByParagraph: 上/下移一段
MoveLeftByCharacter/MoveRightByCharacter:左/右移一字符
MoveLeftByWord/MoveRightByWord 左/右移一詞
MoveToDocumentStart/MoveToDocumentEnd:到文章開頭/結尾
MoveToLineStart/MoveToLineEnd:到一行的開頭/結尾
SelectUpByLine,SelectDownByLine:向上/下選一行
SelectUpByPage,SelectDownByPage:向上/下選一頁
SelectUpByParagraph,SelectDownByParagraph:向上/下選一段
SelectLeftByCharacter,SelectRightByCharacter:向左/右選中一字
SelectLeftByWord,SelectRightByWord:向左/右選中一詞
SelectToDocumentStart,SelectToDocumentEnd: 選中到篇頭/篇尾
SelectToLineStart/SelectToLineEnd:選中到行首/行尾
ToggleBold, ToggleItalic, ToggleUnderline(加粗,斜體,下劃線)
ToggleBullets, ToggleNumbering(列表:加點,加數字)
ToggleInsert:插入
ToggleSuperscript,ToggleSubscript(上標字,下標字)
先來舉一個簡單的例子:

XAML代碼:

<StackPanel>
<Menu>
<MenuItem Command="ApplicationCommands.Paste" />
</Menu>
<TextBox />
</StackPanel>
 
C#代碼:
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);
pasteMenuItem.Command = ApplicationCommands.Paste;
 
上面代碼演示了將對文本框設置爲焦點時,菜單項可用,點擊菜單項時,將執行粘貼命令。


下面列出關於Command的四個概念和四個小問題:
1、WPF中Command(命令)的四個概念:
(1)命令command:要執行的動做。
(2)命令源command source:發出命令的對象(繼承自ICommandSource)。
(3)命令目標command target:執行命令的主體
(4)命令綁定command binding:映射命令邏輯的對象
比 如在上面示例中,粘貼(Paste)就是命令(command), 菜單項(MenuItem)是命令源(command source), 文本框(TextBox)是命令目標對象(command target), 命令綁定到command binding文本框(TextBox)控件上。

提示:WPF中的命令都繼承自ICommand接口。ICommand暴露兩個方法:Execute方法、 CanExecute方法和一個事件:CanExecuteChanged。
繼承自ICommandSource的有:ButtonBase, MenuItem, Hyperlink和InputBinding。
而Button, GridViewColumnHeader,ToggleButton,RepeatButton繼承自ButtonBase。 System.Windows.Input.KeyBinding和MouseBinding繼承自InputBinding。
2、四個小問題:
(1)如何指定Command Sources?
XAML:(請將「ApplicationCommands.Properties」換成對應的ApplicationCommands屬性值,好比:

ApplicationCommands.Copy)
<StackPanel>
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Command="ApplicationCommands.Properties" />
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
 
同等的C#代碼:

StackPanel cmdSourcePanel = new StackPanel();
ContextMenu cmdSourceContextMenu = new ContextMenu();
MenuItem cmdSourceMenuItem = new MenuItem();

cmdSourcePanel.ContextMenu = cmdSourceContextMenu;
cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem);

cmdSourceMenuItem.Command = ApplicationCommands.Properties;
 
 
(2)如何指定快捷鍵?
XAML代碼:
<Window.InputBindings>
<KeyBinding Key="B"
Modifiers="Control"
Command="ApplicationCommands.Open" />
</Window.InputBindings>

 
C#代碼:

KeyGesture OpenKeyGesture = new KeyGesture(
Key.B,
ModifierKeys.Control);

KeyBinding OpenCmdKeybinding = new KeyBinding(ApplicationCommands.Open,OpenKeyGesture);
this.InputBindings.Add(OpenCmdKeybinding);
//也能夠這樣(下面一句與上面兩句的效果等同):
//ApplicationCommands.Open.InputGestures.Add(OpenKeyGesture);
3)如何Command Binding?

XAML代碼:

<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Open"
Executed="OpenCmdExecuted"
CanExecute="OpenCmdCanExecute"/>
</Window.CommandBindings>
 
C#代碼:

CommandBinding OpenCmdBinding = new CommandBinding(
ApplicationCommands.Open,
OpenCmdExecuted,
OpenCmdCanExecute);

this.CommandBindings.Add(OpenCmdBinding);
 
 
具體的事件處理:
C#代碼:
void OpenCmdExecuted(object target, ExecutedRoutedEventArgs e)
{
MessageBox.Show("The command has been invoked.");
}

void OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}


 
(4)如何設置Command Target並進行綁定Command Binding?
XAML代碼:
<StackPanel>
<Menu>
<MenuItem Command="ApplicationCommands.Paste"
CommandTarget="{Binding ElementName=mainTextBox}" />
</Menu>
<TextBox Name="mainTextBox"/>
</StackPanel>

 

C#代碼:

StackPanel mainStackPanel = new StackPanel();
TextBox mainTextBox= new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(mainTextBox);

pasteMenuItem.Command = ApplicationCommands.Paste;
 


 
以上例子全是單條命令綁定的情形,事實上,你也能夠多個按鈕多條命令綁定到同一控件上,好比:

<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Orientation="Horizontal" Height="25">
<Button Command="Cut" CommandTarget="{Binding ElementName=textBoxInput}" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/>
<Button Command="Copy" CommandTarget="{Binding ElementName=textBoxInput}" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/>
<Button Command="Paste" CommandTarget="{Binding ElementName=textBoxInput}" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/>
<Button Command="Undo" CommandTarget="{Binding ElementName=textBoxInput}" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/>
<Button Command="Redo" CommandTarget="{Binding ElementName=textBoxInput}" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/>
<TextBox x:Name="textBoxInput" Width="200"/>
</StackPanel>
 
 
WPF中的控件模板(ControlTemplate)
【IT168技術文檔】WPF包含數據模板和控件模板,其中控件模板又包括ControlTemplate和ItemsPanelTemplate,這裏討論一下ControlTemplate。

   其實WPF的每個控件都有一個默認的模板,該模板描述了控件的外觀以及外觀對外界刺激所作出的反應。咱們能夠自定義一個模板來替換掉控件的默認模板以便打造個性化的控件。

    與Style不一樣,Style只能改變控件的已有屬性值(好比顏色字體)來定製控件,但控件模板能夠改變控件的內部結構(VisualTree,視覺樹) 來完成更爲複雜的定製,好比咱們能夠定製這樣的按鈕:在它的左辦部分顯示一個小圖標而它的右半部分顯示文本。

    要替換控件的模板,咱們只須要聲明一個ControlTemplate對象,並對該ControlTemplate對象作相應的配置,而後將該ControlTemplate對象賦值給控件的Template屬性就能夠了。

ControlTemplate包含兩個重要的屬性:
1,VisualTree,該模板的視覺樹,其實咱們就是使用這個屬性來描述控件的外觀的
2,Triggers,觸發器列表,裏面包含一些觸發器Trigger,咱們能夠定製這個觸發器列表來使控件對外界的刺激發生反應,好比鼠標通過時文本變成粗體等。

參考如下代碼
<Button>
<Button.Template>
<ControlTemplate>
<!--定義視覺樹-->
<Grid>
<Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/>
<TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" />
</Grid>
<!--定義視覺樹_end-->
</ControlTemplate>
</Button.Template>
 
</Button>在上面的代碼中,咱們修改了Button的Template屬性,咱們定義了一個ControlTemplate,在 <ControlTemplate> ... </ControlTemplate>之間包含的是模板的視覺樹,也就是如何顯示控件的外觀,咱們這裏使用了一個Ellipse(橢圓)和一 個TextBlock(文本塊)來定義控件的外觀。

    很容易聯想到一個問題:控件(Button)的一些屬性,好比高度、寬度、文本等如何在新定義的外觀中表現出來呢?
我 們使用TemplateBinding 將控件的屬性與新外觀中的元素的屬性關聯起來Width="{TemplateBinding Button.Width}" ,這樣咱們就使得橢圓的寬度與按鈕的寬度綁定在一塊兒而保持一致,同理咱們使用Text="{TemplateBinding Button.Content}"將TextBlock的文本與按鈕的Content屬性綁定在一塊兒。
除了定義控件的默認外觀外,也許咱們想還定義當外界刺激咱們的控件時,控件外觀作出相應的變化,這是咱們須要觸發器。參考如下代碼:
<Button Content="test btn" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" Grid.RowSpan="1" >
<Button.Template>
<ControlTemplate>
<!--定義視覺樹-->
<Grid>
<Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/>
<TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" />
</Grid>
<!--定義視覺樹_end-->
<!--定義觸發器-->
<ControlTemplate.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Button.Foreground" Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
<!--定義觸發器_End-->
</ControlTemplate>
</Button.Template>
 
</Button>在上面的代碼中注意到<ControlTemplate.Triggers>... </ControlTemplate.Triggers>之間的部分,咱們定義了觸發器 <Trigger Property="Button.IsMouseOver" Value="True">,其表示當咱們Button的IsMouseIOver屬性變成True時,將使用設置器<Setter Property="Button.Foreground" Value="Red" /> 來將Button的Foreground屬性設置爲Red。這裏有一個隱含的意思是:當Button的IsMouseIOver屬性變成False時,設 置器中設置的屬性將回復原值。

你能夠粘貼如下代碼到XamlPad查看效果:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ControlTemplateTest" Height="300" Width="300"
>
<Grid ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*"/>
<ColumnDefinition Width="0.6*"/>
<ColumnDefinition Width="0.2*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.4*"/>
</Grid.RowDefinitions>
<Button Content="test btn" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" Grid.RowSpan="1" >
<Button.Template>
<ControlTemplate>
<!--定義視覺樹-->
<Grid>
<Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/>
<TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" />
</Grid>
<!--定義視覺樹_end-->
<!--定義觸發器-->
<ControlTemplate.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Button.Foreground" Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
<!--定義觸發器_End-->
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</Window>
 
接下來的一個問題是:若是我要重用個人模板,應該怎麼辦呢?
你須要將模板定義爲資源,其實大多數狀況下,咱們也是這樣作的
參考如下代碼:
<Window.Resources>
<ControlTemplate TargetType="Button" x:Key="ButtonTemplate">
<!--定義視覺樹-->
<Grid>
<Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/>
<TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" />
</Grid>
<!--定義視覺樹_end-->
<!--定義觸發器-->
<ControlTemplate.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Button.Foreground" Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
<!--定義觸發器_End-->
</ControlTemplate>
</Window.Resources>
 
上面的代碼將咱們原來的模板定義爲窗體範圍內的資源,其中TargetType="Button"指示咱們的模板做用對象爲Button,這樣在整個窗體範圍內的按鈕均可以使用這個模板了,模板的使用方法也很簡單:
<Button Content="test btn" Template="{StaticResource ButtonTemplate}" />其中的ButtonTemplate是咱們定義的模板的x:Key

你能夠粘貼如下代碼到XamlPad查看效果:

<Window

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="ControlTemplateTest" Height="300" Width="300"

>


<Window.Resources>

<ControlTemplate TargetType="Button" x:Key="ButtonTemplate">

<!--定義視覺樹-->

<Grid>

<Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/>

<TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" />

</Grid>

<!--定義視覺樹_end-->

<!--定義觸發器-->

<ControlTemplate.Triggers>

<Trigger Property="Button.IsMouseOver" Value="True">

<Setter Property="Button.Foreground" Value="Red" />

</Trigger>

</ControlTemplate.Triggers>

<!--定義觸發器_End-->

</ControlTemplate>

</Window.Resources>


<Grid ShowGridLines="True">

<Grid.ColumnDefinitions>

<ColumnDefinition Width="0.2*"/>

<ColumnDefinition Width="0.6*"/>

<ColumnDefinition Width="0.2*"/>

</Grid.ColumnDefinitions>

<Grid.RowDefinitions>

<RowDefinition Height="0.3*"/>

<RowDefinition Height="0.3*"/>

<RowDefinition Height="0.4*"/>

</Grid.RowDefinitions>

<Button Content="test btn1" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="1" />

<Button Content="test btn2" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" Grid.RowSpan="1" Template="{StaticResource ButtonTemplate}" />

<Button Content="test btn2" Grid.Column="2" Grid.ColumnSpan="1" Grid.Row="2" Grid.RowSpan="1" Template="{StaticResource ButtonTemplate}" />

</Grid>

</Window>

 

額外提一下的是,咱們也能夠在觸發器中,調用一個故事板來達到對事件響應時的動畫效果
參考如下代碼 <!--定義動畫資源-->
<ControlTemplate.Resources>
<Storyboard x:Key="MouseClickButtonStoryboard">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="faceEllipse" Storyboard.TargetProperty="Width" BeginTime="00:00:00">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="50"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.3" Value="100"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
 
</ControlTemplate.Resources>咱們爲模板定義了一個動畫資源,此後在模板的觸發器中咱們就能夠調用該資源來實現一個動畫效果了:
<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="faceEllipse">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource MouseClickButtonStoryboard}"/>
</EventTrigger.Actions>
</EventTrigger>你能夠粘貼如下代碼到XamlPad查看效果:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ControlTemplateTest" Height="300" Width="300"
>
<Window.Resources>
<ControlTemplate TargetType="Button" x:Key="ButtonTemplate">
<!--定義視覺樹-->
<Grid>
<Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/>
<TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" />
</Grid>
<!--定義視覺樹_end-->
<!--定義動畫資源-->
<ControlTemplate.Resources>
<Storyboard x:Key="MouseClickButtonStoryboard">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="faceEllipse" Storyboard.TargetProperty="Width" BeginTime="00:00:00">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="50"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.3" Value="100"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<!--定義動畫資源_end-->
<!--定義觸發器-->
<ControlTemplate.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Button.Foreground" Value="Red" />
</Trigger>
<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="faceEllipse">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource MouseClickButtonStoryboard}"/>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="txtBlock">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource MouseClickButtonStoryboard}"/>
</EventTrigger.Actions>
</EventTrigger>
</ControlTemplate.Triggers>
<!--定義觸發器_End-->
</ControlTemplate>
</Window.Resources>
<Grid ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*"/>
<ColumnDefinition Width="0.6*"/>
<ColumnDefinition Width="0.2*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.4*"/>
</Grid.RowDefinitions>
<Button Content="test btn1" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="1" />
<Button Content="test btn2" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" Grid.RowSpan="1" Template="{StaticResource ButtonTemplate}" />
<Button Content="test btn2" Grid.Column="2" Grid.ColumnSpan="1" Grid.Row="2" Grid.RowSpan="1" Template="{StaticResource ButtonTemplate}" />
</Grid>
</Window>
 
最好的模板示例:咱們知道每一個控件都有本身默認的模板,這是MS編寫的,若是咱們可以獲得這些模板的XAML代碼,那麼它將是學習模板的最好的示例,
要想得到某個控件ctrl的默認模板,請調用如下方法:
string GetTemplateXamlCode(Control ctrl)
{
FrameworkTemplate template = ctrl.Template;
string xaml = "";
if (template != null)
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = new string(' ', 4);
settings.NewLineOnAttributes = true;
StringBuilder strbuild = new StringBuilder();
XmlWriter xmlwrite = XmlWriter.Create(strbuild, settings);
try
{
XamlWriter.Save(template, xmlwrite);
xaml = strbuild.ToString();
}
catch (Exception exc)
{
xaml = exc.Message;
}
}
else
{
xaml = "no template";
}
return xaml;
}
 
 
揭露WPF SDK「不能說的祕密」
【IT168 專稿】 若是經歷過.NET的1.0,1.1以及2.0版本,你就極可能發現.NET 3.0中的WPF區域中的一些文檔有點不一樣。具體來講,WPF負責介紹幾個CLR和託管代碼封裝方面的新概念。WPF SDK團隊爲在參考資料中展現這些新概念而作的努力是很大的進步,主要致力於改變,由於其它的技術也在它們的API中採用了相同或者相似的範例。

System.Reflection和.NET 3.0 SDK 

    微軟用於建立託管SDK框架的「魔法」其實就是映射進程(典型的就是採用託管映射,但有時也有非託管的映射,這取決於API)。編譯工具映射全部.NET 3.0的API,SDK做者就在綱要中填充映射告訴咱們的關於API的消息(至少這是一個目標)。可是對於比System.Reflection自己更新 的編程概念的映射,System.Reflection作得不是特別好。你能夠經過查看定製特性來獲得一些更具體的信息。但對於那些像XAML同樣的語 言,或者獨立屬性,因爲System.Reflectio從1.1開始就沒有本質的改變,因此不能爲3.0提供好的API。若是你要編寫本身的映射,你不 得不作些額外的創造工做(我不會在這裏描述這個問題,由於太複雜了,並且超出了本主題的範圍)。對咱們的SDK開發團隊來講,他們也不得不很是熟悉映射代 碼。而後,其餘的各類各樣的人,包括一些像咱們這樣的程序員不得不跟上表示策略——怎麼在更好,更標準的託管代碼段文檔中表示額外的信息,好比異常和返回 值等。
                    
    正在討論的隱藏的祕密是參考主題部分,這部分是大量的設計會議和開發工做的結果。每個隱藏祕密都表明着編程的一個具體方面,和WPF很是相關而且對 WPF來講是新內容。全部的信息都在SDK頁面,等待你的發掘。可是,咱們還沒搞清楚這些忽然在WPF參考文檔中出現的額外段落是什麼,以及他們意味着什 麼。
  
   拋開這些吧。讓咱們來照亮這些WPF文檔中隱藏的祕密。一切都會獲得解釋!

獨立屬性 

    不少時候會被問道一個問題「獨立屬性究竟是什麼?」或者一個相關的更尖銳的問題「我爲何要去關心一個東西是否是獨立屬性呢?」咱們的答案是:獨立屬性總覽。

    跨過這個概念上的障礙以後,下一個邏輯上的問題多是「好吧,獨立屬性就是一種支持CLR屬性的方式,那我該怎麼分別那些屬性是經過這種方式被支持的呢?」咱們遇到的第一個隱藏祕密是:獨立屬性,和獨立屬性信息段。
   
    在文檔中提到兩種方法來判斷某個給定的屬性是不是獨立屬性:
1)當你在瀏覽任何類型的成員表,而且看到公共屬性,讀描述的時候。若是這個屬性是獨立屬性,那麼描述的最後一句就是「這是一個獨立特性。」
2)假設你在說明CLR屬性的SDK的主題頁面。一樣,在成員表的相同的描述中,你能夠找到這句話「這是一個獨立屬性」。除了描述外,每個獨立屬性的屬性頁面內還有一段能夠被合適地成爲獨立屬性信息段。這一段在表格中包含兩點:

    一個到含有獨立屬性標誌符的域的連接。不少屬性系統API函數都須要這個標誌符以執行一個屬性或者得到關於它的信息。有人說,對那些只是獲取或設置某個已 經存在的具體屬性的應用程序編程來講,使用屬性系統API並不老是必須的,由於你能夠調用更簡單的獨立屬性CLR「封裝」。這樣,標誌符主要是和包含獨立 屬性的高級編程任務相關,好比處理屬性元數據,或者追蹤屬性值。

   若是一個屬性是框架層的屬性,那麼獨立屬性信息段會把元數據中「標記」爲true的列出來。當獨立屬性被WPF框架層解釋時,標記報告獨立屬性的某些共同 特徵,尤爲是子特徵,好比,總體系統,數據綁定,或屬性值繼承。SDK報告這些標記,由於這有助於實例瞭解改變屬性是否會強迫修改總體設計,或是否你能夠 省略指定綁定的兩個狀態,由於這是獨立屬性的默認設置。
默認值

    橫跨獨立屬性和「常規」屬性的一個概念是默認值。通常來講,.NET文檔會在屬性值段告訴你屬性的默認值,而且爲保持一致性,這也是默認屬性被報告給獨立 屬性的地方。可是,獨立屬性默認值實際上來自屬性元數據,儘管在一個平面上CLR屬性可能來自一個類或者是拓展執行。這樣,經過重寫屬性元數據,獨立屬性 就能夠被子類或新的屬性擁有者輕易改變。在已有的WPF獨立屬性中偶爾會發生這種狀況。當發生時,每個類的重寫都會被記錄在Remark段。
Routed事件

    在你不可避免地問「什麼是routed事件?」以前,能夠去這裏看看Routed Events Overview。如今咱們解決這個問題了。
不 像獨立屬性,routed事件沒有像「這是一個routed事件」之類的習慣性描述。基本元素(UIElement, FrameworkElement, ContentElement, FrameworkContentElement)有不少的routed事件:可能它們的事件有75%都是routed的。其它的類,像控件也會有少數的 routed事件,也許和着一些不route的標準CLR事件。爲了區分一個事件是否route,你須要在事件主題看看是否有Routed事件信息段。
    Routed事件信息段是一個有以下三項的表: 

   *一個指向包含有routed 事件標誌符的域的連接。和屬性系統API以及獨立屬性標誌符同樣,事件系統API也須要這個標誌符以接入事件。像增長或移除句柄一類的簡單操做並非必須 標誌符,這也是由於routed事件會有一個「包裝」,這樣就能夠支持CLR語言中增長/移除事件句柄的CLR語法。但,若是直接使用 AddHandler或調用RaiseEvent,就須要知道routed事件的標誌符。

    *Routing策略。有三種可能:Bubbling,Tunneling,和Direct。這兒有個小祕密:若是以前查看過事件名字,那麼 routing策略就老是Tunneling,這是一個慣例。但區分Bubbling和Direct就有點困難了,由於沒有不一樣的命名習慣。因此,咱們提 供了參照頁的信息。Direct事件實際上沒有在它的元素以外route,但他們仍然服務於WPF-specific目的,在這裏有描述Routed Events Overview。
  
* Delegate類型。你能夠在聲明事件的語法中找到列出的delegate。咱們在這裏重複是爲了方便,因此你能夠直接跳到「寫恰當的句柄」的地方。

附帶屬性

    嚴格地說,附帶屬性是XAML的概念,而不是WPF的。但WPF 是第一個發行的採用了XAML語言的工具。因此WPF是用文檔來描述XAML語法和XAML語言的先鋒。若是你是代碼編寫員,並看了某個附帶屬性的叫作 DockPanel.Dock的文檔,就會注意到一些特別的東西:沒有XAML語法,沒有代碼語法。若是瀏覽DockPanel類,去看映射或對象就能夠 證明:對CLR沒有相似於「Dock」屬性的東西。甚至爲了在其它的產生映射的參照中使這個頁面存在,SDK開發團隊不得不注入這個頁面。然而,對咱們這 些在標記和代碼間能夠隨意轉換的人來講,附帶屬性的語法段確實提供了一個到附帶屬性的「真實」代碼API的連接,這能夠成爲一系列獲取和設置的接入方法。 對DockPanel 類來講有 GetDock和SetDock。(不幸的是,在MSDN版本的這些頁面中彷佛沒有這些連接;在Visual Studio和offline Windows SDKs中有…相信我...)

附屬事件 

   相似地,這是一個XAML的概念,但在WPF獲得具體的應用。XAML語法,但對同等代碼接入來講是一個「祕密」接入方法。在附屬事件狀況下,這些方法的 類型是Add*Handler和Remove*Handler。例如,Mouse.MouseDown調用AddMouseDownHandler就能夠 有附帶到給定的UIElement實例的句柄,並能夠經過RemoveMouseDownHandler移除。對實際應用來講,附屬事件不必定那麼重要, 由於大多數都是被UIElement以更方便的方式從新使用而已。但若是你在寫一套相關的控件,或者在實現一個服務,那麼附屬事件就極可能進入你的視野。
XAML 語法 

   SDK文檔提供了XAML各類用途下的XAML語法,好比用來實例化一個XAML類的對象元素標籤,用來設置屬性或附帶事件句柄的屬性,還有XAML的具 體概念,如content property或property element語法。另外一個隱藏的祕密來了...好的,也不是那麼的隱祕了,由於我之前已經在博客上寫過了。XAML不必定非要被綁定到計劃中。最終決定 XAML是否有效的是XAML loader,最終成功仍是失敗取決於一些加載動做,好比試圖在組件中找到一個命名匹配的類型,調用它的默認ctor,以及返回一個實例。或者,一旦對象 存在,就是試圖找到一個匹配屬性名字的property,而後試圖用屬性值的string-to-X變換,或者甚至是更復雜的在property元素中以 子關係存在的XAML的什麼東西的封裝來填充它。對於保持XAML的擴展性來講是有必要的,可是這會讓編寫文檔和靜態的XAML詞典更困難一點兒。其一, parser會容許你實例化一個在對象模塊中沒有「家」的元素。好比,大多數EventArgs子類能夠在XAML中創建,由於它們能夠方便的擁有一個默 認ctor(加入XAML俱樂部是如此容易....)可是,而後呢?從狹義來講,XAML是UI定義上的。廣義上講,XAML是相關的對象和你但願創建的 他們的屬性的任何結構的定義。你不能把EventArgs做爲任何具備可疑資源收集異常的東西的子元素。這讓XAML文檔進退兩難:這是XAML嗎,或者 不是?這個困惑常常出現。通常來講,咱們用具體狀況來講明XAML。有什麼緣由讓你但願用XAML來實例化這個類嗎?若是有,咱們會給你看一個語法。若是 沒有,就像3.0的EventArgs子類同樣,咱們會告訴你這並非一個典型的應用,雖然XAML loader會在和其它面向應用的對象模型徹底隔離的狀況下用XAML來創建一個。

    另外一個祕密:雖然沒有寫出語法(會說這是不適用的東西),可是在frameworks中有不少支持XAML的類。通常來講,這是由於這些類來自更早的版 本,能夠在整個3.0 framework中使用,由於可能合法的XAML使用率會很大。因此,爲了填補空白,用戶須要知道一些XAML的技巧。對初學者,讀XAML and Custom Classes就能夠了。當以默認的ctor和一些其它的限定來符合XAML加載器的要求時,定製的類和已有的類沒什麼不一樣的。也去看看x:Array Markup Extension中的例子吧。這個例子講了系統名字空間和mscorlib組件,這樣你就能夠吧String類做爲一個直接對象進行實例化。你能夠外推 這些簡單的概念,並作些更有用的事情。甚至設想這個場景都有點困難,可是大家是一個充滿創造性的集體:讓咱們看看大家能作什麼!描繪一些 System.Data的結構?誰知道在pre-3.0 .NET的核心部分用了多少XAML?

沒有XAML屬性列表?

    SDK並非真的有一個「全部權頁面」,這是XAML特有的。你能夠把property列表看做是普通的成員表的一部分,但要選出XAML屬性有點困難, 由於有些是隻讀的,有些又不使用支持XAML的類型,等等。這裏,我想咱們不得不認可工具和設計者能夠比只利用SDK的成員表作得更好。由於工具和設計者 有語義的優點;他們知道你剛剛實例化了什麼面板子類,在元素樹裏面它處於什麼位置等。好好地綜合基於工具的只列出基本的可能性的智能,而後學會按F1來獲 取SDK頁面的更多的幫助信息。這是咱們追趕即未來臨的工具/SDK的發行的下一步。限於篇幅,不能細細講解。

沒有XAML XSD的計劃?

    我之前在博客上寫過這個。對一個像WPF這樣有不少維的產品來講,XAML的計劃很困難。但你也許已經注意到接下來的XAML綜合技術(Workflow Foundation, Silverlight)確實包括了XSD類型的計劃。說到WPF XAML的計劃,咱們仍然保持觀望。
WPF圖形系統
【IT168 技術文檔】
    在Windows NT中,圖形多媒體系統基於層次結構。應用程序與頂層的API(其實是多個用戶模式的系統DLL,好比GDI32.DLL)交互,這些系統DLL最終會 經過系統服務調用處於內核模式的系統服務。NT系統的詳細信息可參考《Windows 圖形編程》的第1、二章。在Windows Vista中,圖形系統已經移出了內核模式,並有本身的空間--WPF。 WPF移出內核除了全面提升穩定性(內核模式中一般須要共享資源),也使得實現內核模式高級控制成爲可能。
    首先,WPF再也不惟一依靠GDI圖形API,Vista更多的依靠Direct3D處理圖形操做,這樣容許更多的D3D特徵,實現更酷的外觀和效果。當 然,爲了實現這樣的效果,Vista的設計上也有所改變。Windows Vista儘可能不用CPU進行顯示,而是用顯示卡的圖形處理單元(Graphics Processing Unit, GPU),用矢量圖代替位圖。 Windows Vista還採用了另一種驅動模型--Windows顯示驅動模型(WDDM)。實現操做系統和圖形卡GPU之間更復雜的通訊。爲了處理操做系統更高的3D處理請求,新的WDDM驅動類型須要更多的顯示卡內存。Vista能儘量的利用顯示卡以達到最好的性能。
     如今基本瞭解了Windows Vista圖形系統,再看看其可測量性。Windows Vista可提供很是高的終端圖形,據此不少人都錯誤的認爲:爲了運行Windows Vista操做系統,必需要很是高端的圖形硬件。事實上並不徹底如此。Windows Vista圖形系統一個關鍵好處就是能夠檢測顯示卡的性能。Vista根據不一樣的性能和驅動模型(WDDM,XPDM)提供給用戶不一樣的用戶體驗。

    在Windows NT中,圖形多媒體系統基於層次結構。應用程序與頂層的API(其實是多個用戶模式的系統DLL,好比GDI32.DLL)交互,這些系統DLL最終會 經過系統服務調用處於內核模式的系統服務。NT系統的詳細信息可參考《Windows 圖形編程》的第1、二章。在Windows Vista中,圖形系統已經移出了內核模式,並有本身的空間--WPF。
    WPF移出內核除了全面提升穩定性(內核模式中一般須要共享資源),也使得實現內核模式高級控制成爲可能。首先,WPF再也不惟一依靠GDI圖形API, Vista更多的依靠Direct3D處理圖形操做,這樣容許更多的D3D特徵,實現更酷的外觀和效果。固然,爲了實現這樣的效果,Vista的設計上也 有所改變。Windows Vista儘可能不用CPU進行顯示,而是用顯示卡的圖形處理單元(Graphics Processing Unit, GPU),用矢量圖代替位圖。
    Windows Vista還採用了另一種驅動模型--Windows顯示驅動模型(WDDM)。實現操做系統和圖形卡GPU之間更復雜的通訊。爲了處理操做系統更高的 3D處理請求,新的WDDM驅動類型須要更多的顯示卡內存。Vista能儘量的利用顯示卡以達到最好的性能。
WPF性能優化點
【IT168 技術文檔】
    在創建漂亮UI的同時,咱們還須要關注應用程序的性能,WPF尤爲如此。下面從MS的文檔中總結出了一些有用的性能優化點。在實際編寫的過程當中,能夠參考。這個Post非徹底原創,是根據一些文檔總結出來的。

    1、創建邏輯樹的時候,儘可能考慮從父結點到子結點的順序構建。由於當邏輯樹的一個結點發生變化時(好比添加或刪除),它的父結點和全部的子結點都會激發Invalidation。咱們應該避免沒必要要的Invalidation。

    2、當咱們在列表(好比ListBox)顯示了一個CLR對象列表(好比List)時,若是想在修改List對象後,ListBox也動態的反映這種變 化。此時,咱們應該使用動態的ObservableCollection對象綁定。而不是直接的更新ItemSource。二者的區別在於直接更新 ItemSource會使WPF拋棄ListBox已有的全部數據,而後所有從新從List加載。而使用ObservableCollection能夠避 免這種先所有刪除再重載的過程,效率更高。

    3、在使用數據綁定的過程當中,若是綁定的數據源是一個CLR對象,屬性也是一個CLR屬性,那麼在綁定的時候對象CLR對象所實現的機制不一樣,綁定的效率也不一樣。

    A、數據源是一個CLR對象,屬性也是一個CLR屬性。對象經過TypeDescriptor/PropertyChanged模式實現通知功能。此時綁定引擎用TypeDescriptor來反射源對象。效率最低。
    B、數據源是一個CLR對象,屬性也是一個CLR屬性。對象經過INotifyPropertyChanged實現通知功能。此時綁定引擎直接反射源對象。效率稍微提升。
    C、數據源是一個DependencyObject,並且屬性是一個DependencyProperty。此時不須要反射,直接綁定。效率最高。

    4、訪問CLR對象和CLR屬性的效率會比訪問DependencyObject/DependencyProperty高。注意這裏指的是訪問,不要和 前面的綁定混淆了。可是,把屬性註冊爲DependencyProperty會有不少的優勢:好比繼承、數據綁定和Style。因此有時候咱們能夠在實現 DependencyProperty的時候,利用緩存機制來加速訪問速度:看下面的緩存例子:
public static readonly DependencyProperty MagicStringProperty =
DependencyProperty.Register("MagicString", typeof(string), typeof(MyButton), new PropertyMetadata(new PropertyInvalidatedCallback(OnMagicStringPropertyInvalidated),new GetValueOverride(MagicStringGetValueCallback)));
private static void OnMagicStringPropertyInvalidated(DependencyObject d)
{
// 將緩存的數據標識爲無效
((MyButton)d)._magicStringValid = false;
}
private static object MagicStringGetValueCallback(DependencyObject d)
{
// 調用緩存的訪問器來獲取值
return ((MyButton)d).MagicString;
}
// 私有的CLR訪問器和本地緩存
public string MagicString
{
get
{
// 在當前值無效時,獲取最新的值保存起來
if (!_magicStringValid)
{
_magicString = (string)GetValueBase(MagicStringProperty);
_magicStringValid = true;
}
return _magicString;
}
set
{
SetValue(MagicStringProperty, value);
}
}
private string _magicString;
private bool _magicStringValid;
 
    另外,由於註冊的DependencyProperty在默認是不可繼承的,若是須要繼承特性,也會下降DependencyProperty值刷新的效 率。註冊DependencyProperty屬性時,應該把DefaultValue傳遞給Register方法的參數來實現默認值的設置,而不是在構 造函數中設置。

    5、使用元素TextFlow和TextBlock時,若是不須要TextFlow的某些特性,就應該考慮使用TextBlock,由於它的效率更高。

    6、在TextBlock中顯式的使用Run命令比不使用Run命名的代碼要高。

    7、在TextFlow中使用UIElement(好比TextBlock)所需的代價要比使用TextElement(好比Run)的代價高。

    8、把Label(標籤)元素的ContentProperty和一個字符串(String)綁定的效率要比把字符串和TextBlock的Text屬性 綁定的效率低。由於Label在更新字符串是會丟棄原來的字符串,所有從新顯示內容。

    9、在TextBlock塊使用HyperLinks時,把多個HyperLinks組合在一塊兒效率會更高。看下面的兩種寫法,後一種效率高。

A、
<TextBlock Width="600" >
<Hyperlink TextDecorations="None">MSN Home</Hyperlink>
</TextBlock>
<TextBlock Width="600" >
<Hyperlink TextDecorations="None">My MSN</Hyperlink>
</TextBlock> 
 
B、
<TextBlock Width="600" >
<Hyperlink TextDecorations="None">MSN Home</Hyperlink>
<Hyperlink TextDecorations="None">My MSN</Hyperlink>
</TextBlock> 
 
    10、任與上面TextDecorations有關,顯示超連接的時候,儘可能只在IsMouseOver爲True的時候顯示下劃線,一直顯示下劃線的代碼高不少。

    11、在自定義控件,儘可能不要在控件的ResourceDictionary定義資源,而應該放在Window或者Application級。由於放在控件中會使每一個實例都保留一份資源的拷貝。

    12、若是多個元素使用相同的Brush時,應該考慮在資源定義Brush,讓他們共享一個Brush實例。

    13、若是須要修改元素的Opacity屬性,最後修改一個Brush的屬性,而後用這個Brush來填充元素。由於直接修改元素的Opacity會迫使系統建立一個臨時的Surface。

    14、在系統中使用大型的3D Surface時,若是不須要Surface的HitTest功能,請關閉它。由於默認的HitTest會佔用大量的CPU時間進行計算。 UIElement有應該IsHitTestVisible屬性能夠用來關閉HitTest功能。
WPF中的Style
【IT168 技術文檔】
    Style是一種修改屬性值是方法。咱們能夠將其理解爲對屬性值的批處理。對批處理你們應該不會感到默認。對,經過Style咱們能夠批量修改屬性的值。先從一個簡單的Style例子開始: 
<Window x:Class="Viewer3D.WindowSettins"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Viewer3D Settings"
>
<Window.Resources>
<Style TargetType="CheckBox">
<Setter Property="Height" Value="20"/>
<Setter Property="Width" Value="50"/>
<EventSetter Event="Checked" Handler="Checked_Click"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</Window.Resources>
</Window> 
 
   第一感受你可能會奇怪,爲何Style在資源裏呢?我我的直接將理解爲「批處理」的緣故。所以Style是修改多個對象的屬性值,它不從屬於單獨的元素 對象。另外一個疑惑的問題是Style沒有設置x:Key屬性。這是一個很是關鍵的設置。若是咱們設置了Style的x:Key屬性,至關於在當前 Window是資源中定義了一個名稱爲x:Key設定值的Style對象。記住定義的效果至關於對象。若是沒有設置x;Key,那麼這個Style將對屬 於這個Window中全部CheckBox生效。這就起到了批處理的效果。

    首先設定的是Style的TargetType屬性,它表示咱們但願修改的目標類型。而後定義一個Setters的集合。每一個Setter都表示修改的一 個屬性或者事件。Property設置屬性名稱,Value設置屬性值。Event設置事件名稱,Handler設置事件的響應函數名稱。只要你在 Resource作了相似的定義,在此Window中所使用的任何ChekcBox都會默認這些屬性值。是否是很方便呢?咱們在此定義一次,能夠節省不少 代碼。

    也許你還會問:這樣的統一修改屬性太武斷、霸道了吧!也許是的。咱們只修改部分Element的屬性值,而但願對某些特殊的Element作特殊處理。這樣的需求WPF固然也是支持的。看看下面的代碼:
<Style BasedOn="{StaticResource {x:Type CheckBox}}"
TargetType="CheckBox"
x:Key="WiderCheckBox">
<Setter Property="Width" Value="70"/>
</Style> 
 
    WPT經過BasedOn對這種特殊的Style提供了支持。很明顯,BasedOn的意思是咱們當前的Style基於在資源的CheckBox。這裏又 看到了x;Key擴展標記。由於咱們須要的是一個特例,一個特殊的Style對象。爲了之後引用這個Style,咱們須要x:Key的標識做用。其它的代 碼與前面相似。

    定義後,引用這個特殊Style的CheckBox的代碼是這樣的:
<CheckBox Style="{StaticResource WiderCheckBox}">Win</CheckBox> 
 
    你已經看到,咱們在CheckBox中指定了Style屬性,並引用前面的StaticResource標記。
WPF與Win32
【IT168 技術文檔】
    GDI是當今應用程序的主流圖形庫,GDI圖形系統已經造成 了不少年。它提供了2D圖形和文本功能,以及受限的圖像處理功能。雖然在一些圖形卡上支持部分GDI的加速,可是與當今主流的Direct3D加速相比還 是很弱小。GDI+開始出現是在2001年,它引入了2D圖形的反走樣,浮點數座標,漸變以及單個象素的Alpha支持,還支持多種圖像格式。可是, GDI+沒有任何的加速功能(所有是用軟件實現)。

    當前版本的WPF中,對一些Win32功能尚未很好的支持,好比WMF/EMF文件,單個象素寬度的線條等等。對於這些需求還須要使用GDI/GDI+來實現。

    在Windows Vista中,GDI和GDI+仍然支持,它們與WPF並行存在,可是基本上沒有任何功能性的改進。對GDI和GDI+的改進主要集中在安全性和客戶相關 問題上。WPF的全部提交都不依賴於GDI和GDI+,而是Direct3D。而且全部的Primitive都是經過Direct3D的本地接口實現的。 還記得我前面隨筆中提到過的Milcore嗎?它就是和Direct3D交互的非託管代碼組件。因爲WPF的大部分代碼都是以託管代碼的形式存在的,因此 WPF中有不少託管、非託管的交互。固然,在一些圖形卡不支持WPF所須要的功能時,WPF也提供了稍微低效的軟件實現,以此來支持在某些PC上運行 WPF應用程序。

    在Windows Vista中,Direct3D的關鍵改進就是引入了新的顯示驅動模型。VDDM驅動模型虛擬化了顯卡上的資源(主要是顯示內存),提供了一個調度程序, 所以多個基於Direct3D的應用程序能夠共享顯卡(好比WPF應用程序和基於WPF的Windows Vista桌面窗口管理)。VDDM的健壯性、穩定性也獲得了提升,大量的驅動操做從內核(Kernel)模式移動到了用戶(User)模式,這樣提升了 安全性,也簡化了顯示驅動的開發過程。

    在Windows Vista中存在兩個版本的Direct3D:Direct3D 9和Direct3D 10。WPF依賴於Direct3D 9,這樣能更普遍的解決兼容性問題。另一個很是重要的緣由就是爲Vista的服務器版本提升方便,由於服務器版本的Vista對顯卡和Direct3D 基本上沒有任何的要求。同時WPF也支持Direct3D 10。Direct3D 10依賴與VDDM,只能在Windows Vista上使用。因爲Windows XP沒有VDDM,雖然Microsoft作了很大的努力來改善XP中Direct3D 9相關驅動,提升內容的顯示質量,可是因爲XP中沒有對顯卡資源的虛擬化,強制全部的應用程序都用軟件提交。

    WPF對某些多媒體的功能支持還須要依賴老的技術,好比DirectShow。當咱們進行音頻視頻的捕捉或者其它任務時,只能直接用DirectShow實現,而後再用HwndHost嵌入到WPF內容當中。

    利用相似的技術,咱們能夠在WPF應用程序中顯示自定義格式的內容。經過提供自定義的DirectShow CODEC,而後用Media元素實現和WPF內容毫無限制的集成。

    另外,WPF對XPS等文檔的打印輸出也獲得了極大的改善。XPS文檔自己的規範也極大的提升了其打印的質量,XPS文檔的規範能夠參考MSDN的資料。 除了打印,Vista操做系統中對遠程的改進也部分依賴於WPF,好比有遠程協助、遠程桌面和終端服務等等。它們的實現過程是經過發送一系列的「遠程」命 名到客戶端,客戶根據本身PC的性能和命名進行顯示,這樣顯示的質量能獲得極大的提升。

    在WPF中,對Direct3D進行各類封裝。固然,若是你自己對Direct3D/OpenGL很熟悉,也能夠直接在WPF中使用。封裝後的 Direct3D更容易使用。而且在Web應用程序(XBAP)也能夠使用Direct3D。在WPF中使用的Direct3D,沒有直接用非託管代碼控 制所擁有的靈活性,也不能直接對硬件進行底層控制。

    WPF中全部的提交都是矢量形式的,咱們能夠對圖像或窗口進行任意級的放縮,而圖像的質量不會有任何的損耗。
WPF的邏輯樹和視覺樹
【IT168 技術文檔】
    這部分的內容來自於即將出版的新書《WPF Unleashed》的第三章樣章。關於什麼是邏輯樹,咱們先看下面的一個僞XAML代碼的例子:
<Window ......>
<StackPanel>
<Label>LabelText</Lable>
</StackPanel>
</Window> 
 
    在這樣一個簡單UI中,Window是一個根結點,它有一個子結點StackPanel。而StackPanel有一個子結點Label。注意Label 下還有一個子結點string(LabelText),它同時也是一個葉子結點。這就構成了窗口的一個邏輯樹。邏輯樹始終存在於WPF的UI中,無論UI 是用XAML編寫仍是用代碼編寫。WPF的每一個方面(屬性、事件、資源等等)都是依賴於邏輯樹的。

    視覺樹基本上是邏輯樹的一種擴展。邏輯樹的每一個結點都被分解爲它們的核心視覺組件。邏輯樹的結點對咱們而言基本是一個黑盒。而視覺樹不一樣,它暴露了視覺的 實現細節。下面是Visual Tree結構就表示了上面四行XAML代碼的視覺樹結構:




    並非全部的邏輯樹結點均可以擴展爲視覺樹結點。只有從System.Windows.Media.Visual和 System.Windows.Media.Visual3D繼承的元素才能被視覺樹包含。其餘的元素不能包含是由於它們自己沒有本身的提交 (Rendering)行爲。

    在Windows Vista SDK Tools當中的XamlPad提供查看Visual Tree的功能。須要注意的是XamlPad目前只能查看以Page爲根元素,而且去掉了SizeToContent屬性的XAML文檔。以下圖所示:



    注意圖中工具欄特別標記的地方。咱們能夠看到Visual Tree確實比較複雜,其中還包含有不少的不可見元素,好比ContentPresenter。Visual Tree雖然複雜,可是在通常狀況下,咱們不須要過多地關注它。咱們在從根本上改變控件的風格、外觀時,須要注意Visual Tree的使用,由於在這種狀況下咱們一般會改變控件的視覺邏輯。

    WPF中還提供了遍歷邏輯樹和視覺樹的輔助類:System.Windows.LogicalTreeHelper和 System.Windows.Media.VisualTreeHelper。注意遍歷的位置,邏輯樹能夠在類的構造函數中遍歷。可是,視覺樹必須在經 過至少一次的佈局後才能造成。因此它不能在構造函數遍歷。一般是在OnContentRendered進行,這個函數爲在佈局發生後被調用。

    其實每一個Tree結點元素自己也包含了遍歷的方法。好比,Visual類包含了三個保護成員方法VisualParent、 VisualChildrenCount、GetVisualChild。經過它們能夠訪問Visual的父元素和子元素。而對於 FrameworkElement,它一般定義了一個公共的Parent屬性表示其邏輯父元素。特定的FrameworkElement子類用不一樣的方式 暴露了它的邏輯子元素。好比部分子元素是Children Collection,有是有時Content屬性,Content屬性強制元素只能有一個邏輯子元素。
WPF中的命令與命令綁定
【IT168 技術文檔】
    說到用戶輸入,可能咱們 更多地會聯想到鍵盤、鼠標、手寫筆,其實還用一種高級別的輸入——命令(Commands),從WPF類庫角度講他們分別對於Keyboard, Mouse,Ink與ICommand。命令是一種語義級別的輸入而不是設備級別的,好比「複製」與「粘貼」,但實現一個命令能夠有不少中方式,好比「粘 貼」,咱們能夠使用CTRL-V,也能夠使用主菜單或右鍵菜單(上下文菜單)等等。在以往的.net版本中,要在軟件界面上添加一個「粘貼」按鈕,是很是 麻煩的事情,你得監視剪切板中是否有可用的文本以及對應的文本框是否得到了焦點以便啓用或禁用該按鈕,當粘貼時你還得從剪切板中取得相應的文本並插入到文 本框的合理位置,等等。

    在WPF中提供的命令機制能很是簡單地實現這些任務,下面的Demo演示瞭如何簡單到不用手動編寫一行後臺邏輯代碼便解決上面的難題的,你能夠粘貼下面的代碼到XamlPad:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Window"
Title="Window1"
Width="640" Height="480">
<DockPanel LastChildFill="True">
<Menu Width="Auto" Height="20" DockPanel.Dock="Top">
<MenuItem Command="ApplicationCommands.Copy" Header="{Binding Path=Command.Text, RelativeSource={RelativeSource Self}}"/>
<MenuItem Command="ApplicationCommands.Paste" Header="{Binding Path=Command.Text, RelativeSource={RelativeSource Self}}"/>
<MenuItem Command="ApplicationCommands.Cut" Header="{Binding Path=Command.Text, RelativeSource={RelativeSource Self}}"/>
<MenuItem Command="ApplicationCommands.Redo" Header="{Binding Path=Command.Text, RelativeSource={RelativeSource Self}}"/>
<MenuItem Command="ApplicationCommands.Undo" Header="{Binding Path=Command.Text, RelativeSource={RelativeSource Self}}"/>
</Menu>
<RichTextBox>
<FlowDocument>
<Paragraph/>
</FlowDocument>
</RichTextBox>
</DockPanel>
</Window> 
 


    Demo中菜單欄的菜單項不只僅能完美地完成任務並且能根據文本框的狀態和剪切板自動的啓用與禁用,而咱們卻沒有爲這些複雜的邏輯編寫任何的後臺代碼。這就是WPF中的命令機制爲咱們提供了方便。

    注意這一行代碼:
<MenuItem Command="ApplicationCommands.Copy" Header="{Binding Path=Command.Text, RelativeSource={RelativeSource Self}}"/> 
 
    咱們將「複製」命令(ApplicationCommands.Copy)賦值給了菜單項的Command屬性,實現了ICommandSource接口 的元素都擁有該屬性,這表示該元素能夠做爲一個「命令源」來引起某個命令,其Command屬性就指示了其將引起的命令。

    其實一個命令系統是被分爲四個部分的:
    Command(命令):一個語義級別的輸入,好比「複製」,「左對齊」,「播放」等
    CommandSource(命令源):引起某命令的元素,好比按鈕,菜單項,鍵盤(Ctrl-C,F1等),鼠標等。
    CommandTarget(命令目標):命令被做用的目標,好比文本框,播放器等。
    CommandBinding(命令綁定):用於將命令和命令的處理邏輯連接起來,好比一樣的"粘貼",但粘貼文本和粘貼圖片的處理邏輯是不同的,命令綁定負責將「粘貼」命令與合理的處理邏輯鏈接起來。
    關於命令系統將在本文章的後續部分中講解,不過值得一提的是,在上面的Demo中咱們只指定了命令和命令源,並未指定命令目標,但它會以獲取鍵盤焦點的元 素(這裏是咱們的RichTextBox)做爲默認值,而命令綁定以及命令的後臺執行邏輯被隱藏到了RichTextBox內部,那些編寫 RichTextBox控件的開發人員會爲咱們編寫該部分代碼。

    另外,你可能已經發現,在Demo中咱們並無爲菜單項標題直接設置「複製」「粘貼」這樣的文本,而是使用了以下的一個綁定:
Header="{Binding Path=Command.Text, RelativeSource={RelativeSource Self}}"/>
 
    咱們將菜單文本綁定到了命令的Text屬性,這是由於,若是一個命令爲RoutedUICommand類型,那麼該命令將有一個Text屬性來講明該命令 對應到的文本名稱,該Text屬性會自動本地化的,也就是說若是你的計算機使用語言是簡體中文的話該菜單項顯示的是「複製」,若是你的計算機使用的語言是 英語的話該菜單項顯示的將是「Copy」。

    WPF爲咱們提供了大量內置命令,包括ApplicationCommands,NavigationCommands,,MediaCommands, EditingCommands與ComponentCommands,以及控件開發人員爲它們的控件也提供了不少特有的命令(好比 Slider.DecreaseLarge 與 Slider.DecreaseSmall),這些足以應付平時的大多數應用,若是還不夠的話,你能夠爲本身的應用自定義更多的命令。
在WPF中,命令(Commanding)被分割成了四個部分,分別是ICommand,ICommandSource,CommandTarget和CommandBinding。下面咱們來分別探討這四個部分。

    1,ICommand
    Command也就是咱們的「命令」自己,好比「複製」「粘貼」。在WPF中,全部的命令都必須實現ICommand接口,它爲全部的命令提供一個抽象, 這個抽象對於咱們實現Undo、Redo操做很是重要,若是你學習一下設計模式中的「命令」模式,你會更加深入的理解。
    ICommand接口中擁有Execute()方法,該方法用於命令的執行(不過,注意:命令的執行邏輯——好比將剪切板中的文本去出來放到文本框的合適 位置——並無被編寫到該方法中,稍後咱們會講到這其中的奧妙),另外的一個方法是CanExecute()用於指示當前命令在目標元素上是否可用,當這 種可用性發生改變時其便會引起該接口的尾頁一個事件CanExecuteChanged。
    在目前的WPF類庫中,你能看到惟一一個實現了ICommand接口的類型RoutedCommand(其實還有一個名爲SecureUICommand 的類也實現了該接口,不過該類未被公開),「Routed」是一個不太容易被翻譯的修飾詞(有人將它翻譯爲「路由」),但這意味着該類型的命令能夠向 WPF中的RoutedEvent同樣在元素樹中上下傳遞。
RoutedCommand的子類RoutedUICommand是咱們常用 的類型,它與RoutedCommand的不一樣之處僅僅在與它多了一個Text屬性來描述該命令,不過大多數WPF內置命令的Text屬性有一個很不錯的 特色:其支持自動本地化。這至少會爲咱們的軟件的本地化減小工做量。 

     2,ICommandSource與CommandTarget
    命令源,用來觸發咱們的命令,好比用一個菜單項來觸發「複製」命令,那麼該菜單項就是命令源。要使一個元素成爲命令源,其必須實現 ICommandSource接口。命令源決定了它所要觸發的命令、該命令所做用的對象以及命令參數(若是須要的話),這分別對應於它的三個屬性: Command、CommandTarget以及CommandParameter。其中須要注意的是CommandTarget,由於在WPF中若是你 不爲命令源指定其命令對象,那麼其將會把界面上得到鍵盤焦點的元素做爲默認的命令對象,這爲咱們提供了方便,好比界面上有兩個文本框,咱們沒必要擔憂主菜單 項上的「粘貼」操做是針對哪一個文本框的,誰得到焦點便針對誰,這符合你們的習慣。但引入的問題是,若是命令目標不具有獲取鍵盤焦點的能力(好比 Label)或命令源會搶佔焦點(好比用Button來代替菜單項,點擊按鈕時焦點由文本框轉移到了按鈕上),你的命令將會無效,這時你就必須爲命令源指 定命令目標。

    3,CommandBinding
    前面已經提到咱們並無將命令的執行邏輯編寫到其Excute()方法中,這是有道理的,好比"粘貼"命令 (ApplicationCommands.Paste),粘貼一段文本到文本框和粘貼一個圖片到繪圖板的執行邏輯確定是不同的,負責開發該「粘貼」命 令的開發人員不可能知道全部的粘貼操做的具體邏輯,使用「粘貼」命令的客戶也不該該爲該執行邏輯負責,編寫該執行邏輯的任務應該被分發給那些支持「粘貼」 操做的控件的開發人員以及那些但願爲本身的控件添加「粘貼」操做的客戶。也就是說咱們須要將「行爲的請求者(命令)」和「行爲的執行者(命令的執行邏 輯)」分開而實現一種鬆耦合,而CommandBinding(命令綁定)即是命令和命令執行邏輯的橋接器。
    咱們使用CommandBinding將命令與其合適的執行邏輯綁定在一塊兒: 

CommandBinding CloseCommandBinding = new CommandBinding(

ApplicationCommands.Close, CloseCommandHandler, CanExecuteHandler);
 
    CommandBinding構造方法的最後兩個參數分別是ExecutedRoutedEventHandler 與 CanExecuteRoutedEventHandler 類型的委託,用於指示如何執行命令和如何判斷命令可否被執行。
    與CommandBinding同樣扮演着中間角色的還有CommandManager類,它爲命令綁定(以及輸入綁定)提供了不少實用方法。
WPF中的傳遞事件
【IT168 技術文檔】
    傳遞事件

    WPF在.NET簡單事件通知之上添加了不少基礎結構。傳遞事件的設計使得事件能夠與元素樹一塊兒很好的工做。事件發生後,能夠在視覺樹和邏輯樹自動地進行上下傳遞,咱們不須要添加任何額外的代碼。

    傳遞事件使得咱們不須要過多關注於視覺樹,這樣封裝對於咱們理解WPF的元素合成很是重要。好比,咱們點擊一個按鈕的事件,在點擊的時候咱們實際上點擊的 是一個ButtonChrome或者TextBlock,也就是說咱們點擊的是Button的內容元素。正是由於事件能夠沿視覺樹傳遞,Button才發 現這個事件,而且能夠處理。所以,咱們能夠給Button的Content當中添加任意的元素,而不會對事件有任何的影響。若是沒有這樣的事件傳遞,咱們 點擊Button內的元素時,必須手動編寫代碼觸發Button點擊事件。

    傳遞事件的的實現和行爲與Dependency屬性相似。一樣,咱們看看如何實現簡單的傳遞事件。多數時候,傳遞事件並不比普通的.NET事件難。與 Dependency屬性同樣,.NET語言(除了XAML)自己並不明白傳遞目標。這些支持都是基於WPF API。
public class Button
{
// 傳遞的事件
public static readonly RoutedEvent ClickEvent;
static Button()
{
// 註冊事件
Button.DoubleClickEvent = EventManager.RegisterRoutedEvent(「Click」,
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Button));
…
}
// .NET事件保證 (可選的)
public event RoutedEventHandler Click
{
add { AddHandler(Button.ClickEvent, value); }
remove { RemoveHandler(Button.ClickEvent, value); }
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
…
// 激發事件
RaiseEvent(new RoutedEventArgs(Button.ClickEvent, this));
…
}
…
}
 
    從上面的實現能夠看出,事件與Dependency屬性有不少類似之處。也是定義一個靜態的RoutedEvent成員,一樣在靜態構造函數裏註冊事件。 爲了方便,也包裝了一個普通的.NET事件。這裏的AddHandler/RemoveHandler不是從DependencyObject派生,而是 更高一級的基類System.Windows.UIElement。這兩個方法爲相應的事件添加/刪除一個委派。在 OnMouseLeftButtonDown中,咱們構造一個事件參數,傳入事件源對象this,而後調用RaiseEvent函數。

    事件策略和處理函數

    註冊WPF事件時,咱們須要爲傳遞事件選擇一種策略,這個策略指定了事件在元素樹中傳遞的方式。WPF支持這樣三種策略:

    Tunneling:事件首先在根元素激發,而後到達樹下的每一個元素直到源元素(或者有處理函數處理這個事件終止了傳遞)。
    Bubbling:事件首先在源元素激發,而後向上直到根元素(或者有處理函數處理這個事件終止了傳遞。
    Direct:事件只在源元素激發。這與普通的.NET事件同樣,除了參與事件觸發器。

    在上面的例子中,咱們註冊的事件策略就是Bubbling。

    傳遞事件的處理函數的參數與普通.NET事件同樣。第一個參數System.Object表示處理函數依附的元素。第二個的System.EventArgs派生類,提供了以下四個有用的屬性:

    Source:邏輯樹中激發事件的原始元素。
    OriginalSource:視覺樹中激發事件的原始元素。
    Handled:布爾值,表示事件是否被處理。
    RoutedEvent:實際的傳遞事件對象(好比Button.ClickEvent)。這個對於相同的處理函數處理多個傳遞事件時很是有用,能夠用來區別傳遞事件。
    Source和OriginalSource表明了邏輯樹和視覺樹對象。這有利於咱們進行一些低級控制,可是對於有的事件,不須要區別它們,這兩個的值是相同的。
如今,咱們看看WPF究竟是如何處理Bubbling和Tunneling事件的。最後介紹了Attached事件。

    在UIElement類,預約義了不少的傳遞事件,好比鍵盤、鼠標等等。其中大多數是Bubbling事件,其中不少的事件都還有一個對應的 Tunneling事件。全部的Tunneling事件都是Preview前綴命名,它們都在對應的Bubbling事件以前激發。好比 PreviewMouseMove這個Tunneling事件是在MouseMove這個Bubbling事件以前激發的。

    Tunneling事件的好處就是能夠有機會改變或者取消後面的Bubbling事件。WPF內建的響應事件只會對Bubbling事件進行響應,固然, 前提了Bubbling和Tunneling同時定義。這種行爲有什麼好處呢?看下面的一個例子:好比,咱們想實現一種特殊的編輯框,只容許輸入一些特定 的字符。之前的實現方法在處理編輯框的KeyDown或者編輯框的WM_CHAR事件,而後判斷新輸入的字符是否知足條件,若是不知足,咱們再把編輯框的 值設置爲原來的值。這種實現技術會有字符的一個回退過程。而在WPF中,實現方法不一樣,直接在PrevewKeyDown等Tunneling事件中處 理,若是是不須要的字符,把事件設置爲已經處理過。這樣這個事件就不會進入到後面的Bubbling事件KeyDown中,WPF也根本不會顯式這個字 符。這種方法的效果將比以前的回退處理好不少。

    雖然咱們能夠經過RoutedEventArgs參數的Handled屬性爲True來終止事件的傳遞。可是,有時候咱們須要某個事件始終被接受處理,這 能夠經過程序代碼實現。使用重載的AddHanlder方法。好比,咱們給窗口添加一個鼠標右鍵的處理方法(其中MRBD_Handler是類的一個事件 方法):
public AboutDialog()
{
InitializeComponent();
this.AddHandler(Window.MouseRightButtonDownEvent,
new MouseButtonEventHandler(MRBD_Handler), true);
}
 
    這樣,任何條件下,MRBD_Handler均可以接收到窗口的鼠標右鍵事件。即便鼠標右鍵是點擊在窗口中的某個子控件之上。

    Attached事件
    與Attached屬性相似,WPF的Element在事件沒有定義的狀況下也支持Tunneling或者Bubbling事件。好比,咱們能夠在一個簡單的窗口程序中這樣指定事件函數:
<Window xmlns=」http://schemas.microsoft.com/winfx/2006/xaml/presentation」
xmlns:x=」http://schemas.microsoft.com/winfx/2006/xaml」
x:Class=」Window1」
Button.Click=」Button_Click」
<Button Text="TestButton" Width="50" Height="30">
</Window> 
 
    例子中,由於Window自己沒有定義Click事件,因此咱們必須指定Click事件屬性的名稱前綴,也就是定義事件的類名。通過這樣的定義後,點擊在 Window中的TestButton,也會激發屬性聲明的Click事件,調用對應的Button_Click方法。

    爲何這樣的定義能夠經過呢?首先編譯時,XAML會看到Button類確實定義了一個Click的.NET事件。在運行時,會直接調用 AddHandler把這兩個事件依附到Window對應的類當中。因此上面用XAML屬性聲明的事件代碼與下面的程序代碼等效:
public Window1
{
InitializeComponent();
this.AddHandler(Button.ClickEvent, new RoutedEventHandler(Button_Click));
}
 
WPF的Attached屬性
【IT168 技術文檔】
    本篇,咱們再繼續看一種特殊的Dependency屬性:Attached屬性。Attached屬性能夠很是高效地Attach到其餘的對象中。
咱們仍然用前面的一個簡單XAML代碼爲例:
<Window>
<StackPanel>
<Label>LabelText</Lable>
</StackPanel>
</Window> 
 
    如今,若是須要對StackPanel及其子元素設置字體大小,應該如何作呢?在Window元素中,它有一個屬性FontSize,能夠直接設置。但 是,StackPanel本身自己並無FontSize這樣的屬性。這就該Attached屬性出場了。這裏咱們須要用定義在TextElement元 素中的Attached屬性FontSize來設置StackPanel的字體。
<Window>
<StackPanel TextElement.FontSize=」30」>
<Label>LabelText</Lable>
</StackPanel>
</Window> 
 
    這樣,StackPanel的子元素就能經過屬性值繼承獲得新的FontSize屬性。對於這樣的XAML代碼,XAML編譯器或者解析器看到這種語法 時,就要求TextElement(有時也稱爲Attached屬性提供者)有相應的靜態方法SetFontSize來設置對應的屬性值。所以,上面的 Attached屬性設置代碼,能夠以下用C#實現:
StackPanel panel = new StackPanel();
TextElement.SetFontSize(panel, 30);
 
    從這裏的代碼能夠看出,Attached屬性並不神祕。只是調用方法把元素和不相關的屬性關聯起來。而SetFontSize實現也比較簡單。它只是調用 了Dependency屬性訪問函數所調用的DependencyObject.SetValue方法。注意調用的對象是傳入的 DependencyObject,而不是當前的實例:
public static void SetFontSize(DependencyObject element, double value)
{
element.SetValue(TextElement.FontSizeProperty, value);
}
 
    一樣地,Attached屬性也定義了對應的GetXXX函數。它調用的DependencyObject.GetValue方法:
public static double GetFontSize(DependencyObject element)
{
return (double)element.GetValue(TextElement.FontSizeProperty);
}
 
    與普通的Dependency屬性同樣,這些GetXXX和SetXXX方法除了實現對GetValue和SetValue的調用,不能作任何其餘額外的工做。

    其實,在WPF應用中,Attached屬性更多的用來控制UI的佈局。除了前面的StackPanel,還有Grid等等。

    補充說明:上面的代碼還有一個問題須要說明。咱們設置StackPanel的字體屬性時用的是TextElement元素。爲何不用其餘的元素Control、Button呢?

    這個問題的關鍵之處在於Dependency屬性的註冊方法。我曾在Dependency屬性[1]作過簡單的說明。咱們看看Element的FontSizeProperty屬性的註冊代碼:
TextElement.FontSizeProperty = DependencyProperty.RegisterAttached(
「FontSize」, typeof(double), typeof(TextElement), new FrameworkPropertyMetadata(
SystemFonts.MessageFontSize, FrameworkPropertyMetadataOptions.Inherits |
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.AffectsMeasure),
new ValidateValueCallback(TextElement.IsValidFontSize));
 
    這裏與咱們前面的IsDefault屬性相似,只是RisterAttached方法優化了Attached屬性須要的屬性元數據的處理過程。

    另外一方面,Control的FontSize屬性是在TextElement元素已經註冊的屬性之上調用AddOwner方法,獲取一個徹底相同的實例引用:
Control.FontSizeProperty = TextElement.FontSizeProperty.AddOwner(
typeof(Control), new FrameworkPropertyMetadata(SystemFonts.MessageFontSize,
FrameworkPropertyMetadataOptions.Inherits));
 


    因此,在實現Attached屬性時咱們使用的是TextElement,而不是Control等等。
WPF的Dependency Property System
【IT168 技術文檔】
    今天咱們來看看WPF的 基礎:Dependency Property System。在WPF中,全部的屬性(能夠在XAML進行設置的屬性)都依賴於此功能,因此說他是WPF的基礎我想並不爲過吧。.在WPF中, Dependency Property System爲咱們提供了一個訪問XAML中設置的屬性的入口,經過他咱們能夠爲屬性設置默認的值,設置屬性的表達式、值的範圍等,還能夠經過他得到屬性 改變的事件。
    Dependency Property System在框架中表現爲DependencyProperty類。
    全部能夠在XAML中使用的屬性都必須用DependencyProperty類中的Register方法進行註冊(在之後咱們開發自定義控件的時候將會頻繁的使用到它)。
    若是咱們想在自定義的控件中使用自定義的類型須要完成如下2步:(此段內容翻譯自Win SDK)
    1. 使用Register方法來註冊自定義類型。方法將返回一個DependencyProperty對象,用於接收此返回值的變量必須是
靜態只讀(static readonly)的,且變量的名字必須已Property結尾,而他的訪問域應該是public.
    例如:在自定義Class1中你要註冊一個自定義類型:Test
public static readonly DenpendencyProperty TestProperty = DenpendencyProperty.Register(「Test」,
typeof(Test),typeof(Class1));
 
    Register的另一個重載將接受一個PropertyMetadata類型的參數,用於實現對屬性值進行限制,接收事件更改通知等功能。
    2. 爲程序提供訪問的入口。所謂的入口就是咱們通常的屬性。例:
public Test SampleTest
{
get
{
return this.GetValue(TestProperty);
}
set
{
this.SetValue(TestProperty, value);
}
}
 


    今天只是簡單的介紹了下Dependency Property System。目前3.0資料還很少,但願個人介紹對你們學習3.0有點幫助。
明天我將會給出一個簡單的例子。等不及的朋友能夠用Reflector去反編譯3.0的類庫,隨便找個控件看看。推薦:DockPanel
注:本文基於.Net FrameWork 3.0正式版,參考Win SDK _Vista_RC1 。文章爲目前所學知識的總結,隨着學習的深刻,將會隨時更改文章的內容。
WPF指南之XAML的名字空間
【IT168 技術文檔】
    xmlns的做用是設置XML文件的命名空間。相似的,xmlns:x的做用也是指定命名空間。這裏爲何是x而不是其餘的,咱們能夠簡單的理解爲其只是MS的一個命名而已,沒有任何特殊的意義,固然,爲了不和它的衝突,咱們定義本身的命名空間的時候不能是x。
    而另外一個x:Class的做用就是支持當前Window所對應的類,前面已經說過每一個XAML元素都是一個CLR類型,這裏的x:Class是 Window的一個屬性,屬性的內容指出當前的窗口類是FirstXAML名字空間下的Windows1。爲何須要類,而不所有用XAML實現? XAML的主要做用仍是編寫UI部分,咱們仍然須要用代碼對程序邏輯進行更深層次的控制。
    好了,這是兩個最基本的名字空間。一樣地,名字空間也能夠自定義,而且這個自定義會給咱們帶來很大的方便。咱們定義以下的一個類:

namespace DataBind4Image


{

public class GroupData


{

//具體的細節忽略

}

}

 
    若是想在XAML文件中使用這個GroupData類對象,咱們就能夠經過自定義的名字空間引入這個類:

xmlns:local="clr-namespace:DataBind4Image"
 
    這裏的後綴local只是一個標識,你能夠設置爲任何你喜歡的惟一標識。經過這個引入定義咱們就能夠在XAML文件中用local來標識 DataBind4Image當中的任何類。訪問GroupData類時只須要加上local就能夠識別了:<local: DrawingGroupData/>
利用名字空間,除了能夠引入咱們定義的當前工程的類,還能夠引入任何的Assembly。直接看例子是最簡單的:

<Window x:Class="WindowsApplication1.Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:sys="clr-namespace:System;assembly=System"

>

<ListBox>

<sys:String>One</sys:String>

</ListBox>

</Window> 

 
    例子當中引入.NET的System Assembly,經過它咱們就能夠直接使用System的任何類。利用這種相似的方式,咱們能夠在XAML中使用幾乎全部的DOTNET框架類。
    最後說明一下在XAML中inline嵌入程序邏輯處理代碼的狀況。利用<CDATA[…]]>關鍵字引入處理代碼。這種狀況在實際當中不太 合適,咱們不該該採用UI和邏輯混合的方式。詳細的解釋能夠參數Windows SDK文檔。

<![CDATA[

void Clicked(object sender, RoutedEventArgs e)

{

button1.Content = "Hello World";

}

]]></x:Code> 
 
    前面提到過每一個XAML元素表示一個.NET CLR類。多數的XAML元素都是從System.Windows.UIElement, System.Windows.FrameworkElement, System.Windows.FrameworkContentElement和System.Windows.ContentElement繼承。沒 有任何的XAML元素與.NET CLR的抽象類對應。可是不少元素都有一個抽象類的派生類對應。

    一般有以下四種通用的XAML元素:
    Root元素:Windows和Page是最經常使用的根元素。這些元素位於XAML文件的根元素,幷包含其餘元素。
    Panel元素:幫助佈置UI位置。經常使用的是StackPanel, DockPanel, Grid和Canvas。
    Control元素:定義XAML文件的控件類型。容許添加控件並自定義。
    Document元素:幫助實現文檔提交。主要分爲Inline和Block元素組,幫助設計的外觀相似文檔。一些有名的Inline元素有Bold, LineBreak, Italic。Block元素有Paragraph, List, Block, Figure和Table。

    XAML元素的屬性與.NET類對象的屬性相似,XAML的面向對象特徵使得它的行爲與以前的HTML相似。每一個屬性(其實是類屬性)繼承了父元素的屬性或者重載(若是從新設置了屬性)。
爲你的.NET語言增長WPF支持
【IT168技術資訊】微軟發佈了Visual Studio 2008 SDK預覽版的一個新版本。在附帶的例子中有一個是關於爲.NET語言增長WPF支持的。
    Visual Studio 2008會包括一個Shell版本。這個版本能夠無償使用和發行,並容許語言設計師爲本身的編程語言提升Visual Studio IDE的能力。
    雖然多數語言開發人員不須要使本身的語言和.NET相適應,但要這麼作仍是有不少可觀的好處的。除了可以訪問.NET框架,語言設計師還能夠深刻到像 WPF設計器這樣的.NET功能內部。其餘的一些能夠深刻的地方包括WCF(Windows Communication Foundation)的一些功能,好比服務的引用和發現、數據設計器擴展等。
    許多Visual Studio功能須要一個表達式運算器。這個SDK的版本包括了一個基於IronPython的例子。做爲微軟在CLR中支持動態語言嘗試的IronPython,也被做爲WPF的例子使用。
    原文地址:http://www.infoq.com/cn/news/2007/08/WPF-Support
WPF使用流文檔靈活地顯示內容
【IT168 技術文檔】
    Windows® Presentation Foundation (WPF) 提供了一系列功能。事實上,功能是如此之多,以致於其中一些很是重要的功能都沒有獲得應有的關注。一個最好的例子就是「流文檔」功能,它可以讓開發人員在 WPF 中本機建立文檔。「流文檔」針對屏幕顯示以及提供更動態和能夠論證的更復雜模型。「流文檔」幾乎適用於與文本內容相關的全部方面,從產品說明到整本書籍。

    文本顯示無疑是更重要的 UI 功能之一。在 WPF 界面中,您一般使用標籤等控件來顯示文本。可是在許多情形下,您須要的不僅是簡單地顯示幾個單詞。流文檔提供了一種更高級的方法,而它們實質上很是簡單。 它們經過相似 HTML 文檔的格式定義文本流,但其功能更強大,並可提供明顯更先進的佈局選項。

    一般使用基於 XML 的標準標記語言——可擴展應用程序標記語言 (XAML) 來定義「流文檔」。XAML 對於流文檔特別直觀,主要是由於它與 HTML 相似。如下流文檔示例建立了一段文字,並只對其中幾個單詞應用了粗體格式:
<FlowDocument
xmlns=’http://schemas.microsoft.com/winfx/2006/xaml/presentation’
xmlns:x=’http://schemas.microsoft.com/winfx/2006/xaml’>
<Paragraph>The quick <Bold>brown fox</Bold> jumps over the lazy dog.
</Paragraph>
</FlowDocument> 
 
    能夠看到,與 HTML 的類似性比在其餘 XAML UI 中更明顯。真正的元素名稱是不一樣的,可是至少對簡單的文檔來講,模式很是類似。流文檔通常以包含多個塊的 FlowDocument 根元素開頭。「塊」是指流內的元素,一般是如上例所示的文本段落(固然還有其餘塊類型)。段落又能夠包含其餘元素,例如本例中的兩個粗體單詞。請注意,對 於任何其餘 XAML 文檔,根元素必須包含 XAML 特定的命名空間定義,不然沒法被識別。這是 XAML 特定的實現細節,與流文檔無關。請注意,命名空間定義只在獨立的流文檔中才須要。(流文檔能夠是更大的 XAML UI 的一部分,在這種狀況下,該 UI 的根元素中會包含命名空間定義。)
    固然,用戶永遠不會看到流文檔的 XAML(而 HTML 源則可在瀏覽器中查看),這與他們沒法看到任何其餘 UI 元素的 XAML 同樣。相反,用戶看到的是文檔的最終呈現。對於這個特定的示例,您可經過多種方式看到結果。或許最簡單的方式是將其鍵入 Windows SDK 附帶的實用工具 XamlPad 中(請參見圖 1)。


圖 1 XamlPad 中顯示的極其簡單的流文檔 (單擊該圖像得到較大視圖)
    固然,這是一個很是簡單的例子,文檔的定義和嵌入式佈局會複雜得多。流文檔支持您能想到的全部格式,例如斜體、下劃線、字體顏色和字體等。圖2顯示的是一個稍微高級的示例,其結果可在圖 3 中看到。

圖2 更多格式和項目符號列表
<FlowDocument
xmlns=’http://schemas.microsoft.com/winfx/2006/xaml/presentation’
xmlns:x=’http://schemas.microsoft.com/winfx/2006/xaml’>
<Paragraph FontFamily=」Calibri」 FontWeight=」Bold」 FontSize=」24」>
WPF Flow Documents</Paragraph>
<Paragraph>WPF Flow Documents are <Italic>quite sophisticated</Italic>.
They support all common layout options, as well as many you probably
do <Span Foreground=」Red」>not expect</Span>.</Paragraph>
<List>
<ListItem><Paragraph>First List Item</Paragraph></ListItem>
<ListItem><Paragraph>Second List Item</Paragraph></ListItem>
<ListItem><Paragraph>Third List Item</Paragraph></ListItem>
</List>
<Paragraph>Of course, Flow Documents also support the definition
of <Bold><Span FontFamily=」Comic Sans MS」 FontSize=」24」
Foreground=」Blue」>in line</Span></Bold> font sizes and font faces.
</Paragraph>
</FlowDocument>3 帶有稍微高級格式的流文檔 (單擊該圖像得到較大視圖)
    本示例顯示的是帶有內嵌格式的若干段落。它還提供另外一類型塊元素的第一個示例,即列表,毫無疑問,它包含多個列表項。請注意,每一個列表項反過來也只是包含 更多塊元素的容器。所以,我不是簡單地將文本置於一個列表項中,而是向每一個列表項中添加一個段落元素。就此而論,我應該已向每一個列表項或任何其餘塊類型添 加了多個段落。這可讓您在列表的單個列表項內建立高級佈局,這在 HTML 等格式中通常行不通,由於此類格式只會讓簡單的文本字符串流向每一個列表元素。
流文檔基礎知識

    至此您已瞭解了一些流文檔的基礎知識,接下來讓咱們回顧一下某些基礎知識。如您所見,流文檔是塊的集合。在內部,全部塊都是從 System.Windows.Documents.Block 類派生而來的 WPF 類。塊又是從 ContentElement 派生而來(沿此鏈向上追尋幾步),ContentElement 是 WPF 中專門爲文檔定義優化的一個至關低級別的類。此方法有些相似於您用來定義 WPF 界面的控件,它們都從 UIElement 派生而來。二者的繼承樹在概念上很類似,但並不徹底相同。這意味着 WPF 控件和塊不能直接組合。例如,一個按鈕的標題不能設爲一段文本,一個段落也不能直接包含一個按鈕。這些控件和塊之間存在一些細微差異,這是因爲內容控件內 的佈局和塊內的佈局的運做方式大相徑庭這一事實所致。幸運的是,這兩類 WPF 元素之間須要彌合的差別很是小。就按鈕而言,它能夠包含由帶格式的文本構成的 TextBlock 對象;而塊能夠經過特殊 BlockUIContainer 塊類包含任何 WPF 控件。這意味着,流文檔能夠包含全部類型的 WPF 元素(包括交互式用戶界面、媒體和三維元素),而從另外一個角度看,流文檔也但是任何 WPF 用戶界面的一部分,例如能夠是控件內容的一個高級佈局元素,也能夠是一個真正的元素,例如銷售點應用程序中的某一項的描述。

    可用塊的列表理論上是可擴充的,由於開發人員能夠派生他們本身的塊類,而後建立他們本身的針對文檔呈現引擎的加強功能。這提供了我所瞭解的任何其餘文檔呈 現引擎都沒法提供的自由度。可是,對通常的文檔建立者公開的塊數量一般有限。圖4顯示了最重要的塊類型的列表。

圖4 重要塊類型
塊 說明
段落 包含(潛在格式豐富的)文本
列表 包含各類不一樣類型(編號、項目符號等)的列表
表 包含與 Microsoft Word 或 HTML 中的表相似的表
BlockUIContainer 包含做爲整個流的一部分的各類 UI 元素
段 包含一組其餘塊。段對於將常見屬性應用於一組塊中很方便,例如將同一字體屬性應用於多個段落

    當使用 XAML 建立 WPF 流文檔時,您事實上只要實例化某些類型。請看下面的 XAML 代碼段(今後處起,我將省略命名空間定義,以讓示例儘可能簡單):
<FlowDocument>
<Paragraph>Hello World!</Paragraph>
</FlowDocument> 
 
    這會實例化一個 FlowDocument 類和 Paragraph 類(其文本設爲「Hello World!」)。該段落被添加到 FlowDocument 的塊集合中。請注意,對於全部 XAML 而言,元素名稱都區分大小寫,而且精確映射到做爲 WPF 一部分而提供的類。您也可經過編程方式建立相同文檔,以下所示:
FlowDocument doc = new FlowDocument();
Paragraph para = new Paragraph();
para.Inlines.Add(「Hello World!」);
doc.Blocks.Add(para);
 
    固然,這遠不及 XAML 提供的聲明性方法那麼直觀,所以編程的方法只在特殊情形下采用。(當我須要建立一個格式豐富的報告,結果要更像一份真實的文檔,而非經過許多報告引擎建立的表格形式的輸出時,有時會使用此方法。)

    在許多情形下,段落自己帶有格式豐富的內容,這也是經過實例化類實現的,以下所示:
<Paragraph>Hello <Bold>World!</Bold></Paragraph> 
 
    在本例中,該段落包含兩個文本段——「Hello」(使用默認格式)和「World!」(粗體)。這比較有趣,由於這表示此 XAML 不僅是實例化一個段落,並將其文本設爲一個簡單的字符串;相反,它建立了含有兩個子段的一個段落,每一個子段包含不一樣格式的文本。在 WPF 中,這些段稱爲內嵌元素。就如一個流文檔能夠包含多個不一樣類型的塊同樣,段落也能夠包含各類類型的內嵌元素。內嵌元素有多種變體。有些內嵌元素就是所謂的 Span,它們表明應用了特定格式選項的文本段。此例中的 Bold 元素是 Span 的一個特殊情形,其默認字體粗細設爲粗體。內嵌元素的另外一種類型是 Run,它是帶有默認格式的文本段。所以,上面的 XAML 其實只是下例的簡化:
<Paragraph>
<Run>Hello </Run>
<Bold>World!</Bold>
</Paragraph> 
 
    固然,它要方便得多,您沒必要使用 XAML 定義每一個內嵌元素,可是若是您要以編程方式建立相同示例,瞭解內嵌元素的概念就很是重要了,由於它們不能夠在代碼中省略。如下是前面兩個 XAML 示例的對等代碼段:
Paragraph para = new Paragraph();
para.Inlines.Add(new Run(「Hello 「));
Bold b = new Bold();
b.Inlines.Add(「World!」);
para.Inlines.Add(b);
 
    Bold 是 Span 的特殊版本,其默認字體粗細設爲粗體;Bold 類型由 Span 子類化而來,而且會覆蓋 FontWeight 屬性。相似特殊的 Span 還有 Italic 和 Underline。不過,這些特殊的 Span 並非必不可少的,由於您也能夠使用默認的 Span,並設置相應屬性:
<Paragraph>Hello <Span FontWeight=」Bold」>World!</Span></Paragraph>
 
    固然,經過將某一文本段包到粗體或斜體標記中,來直接指定諸如粗體和斜體等屬性的功能很是方便和直觀,所以一般更多的是使用 <Bold>,而不是 <Span FontWeight="Bold">。不過,<Span> 元素仍是很是有用的,由於有許多屬性都要設爲粗體之外的屬性,並且那其中的大多數格式選項都沒有單獨的 Span 類型。事實上,許多很是常見的格式選項沒有特殊的 Span。一個典型的示例就是設置字體。與 HTML 不一樣,流文檔沒有 <Font> 元素。相反,字體按以下方式設置:
<Paragraph>Hello <Span FontFamily=」Comic Sans MS」 FontSize=」24」>
World!</Span></Paragraph> 
 
    諸如 FontFamily 等許多屬性均可以始終在全部流文檔類中找到。例如,若要設置一個完整段落而非只是一個內嵌元素的字體,您不使用 Span 便可作到:
<Paragraph FontFamily=」Comic Sans MS」 FontSize=」24」>Hello World!</Paragraph> 
 
    還有 Span 和 Run 以外的一些內嵌元素。下面就是其餘一些更有趣的內嵌元素:

    Figure Figure 是有些不尋常的內嵌元素,由於它們包含塊。所以,從某種意義上講,Figure 幾乎就像流文檔內的迷你流文檔。Figure 常常用於高級佈局功能,例如段落中被普通文本流包圍的圖像。 
    Floater Floater 是輕型的圖形。它們不支持任何圖形放置選項,可是若是您須要的只是除標準段落對齊以外還能作些簡單對齊的功能,Floater 會比較有用。
    LineBreak LineBreak 元素的做用與其名稱所指的意義徹底相同:它們會在段落內引入換行符。
    InlineUIContainer InlineUIContainer 是 BlockUIContainer 的內嵌元素等同項。若是您須要將任何類型的 WPF 控件與您其餘的內嵌元素組合使用(例如讓一個按鈕在一個段落文本內移動),InlineUIContainer 正是您所須要的。
    Figure 始終用於流文檔中(LineBreak 也是如此,不過它們幾乎不須要詳細討論)。如下示例使用一個圖形,將一個圖像顯示爲一個更大流文檔的一部分:
<Paragraph>
<Figure Width=」200」>
<BlockUIContainer>
<Image Source=」Pictures\Humpback Whale.jpg」 />
</BlockUIContainer>
<Paragraph Foreground=」Blue」 FontFamily=」Consolas」>
The Whale</Paragraph>
</Figure>
The quick brown fox jumps over the lazy dog. The quick brown...
</Paragraph> 
 
請注意,WPF 流文檔中沒有 Image 塊。相反,圖像以標準的 WPF Image 控件內嵌爲 BlockUIContainer。(相同的方法也用於流文檔內諸如視頻或交互式三維模型等內容)。圖 5 顯示了與此相似的一個文檔的呈現。




圖 5 文本環繞圖片和標題 (單擊該圖像得到較大視圖)
查看流文檔

    如今,您已瞭解如何建立一些簡單的流文檔以及如何在 XamlPad 中查看它們。而目前我所忽略的是該如何在天然狀態下查看流文檔。畢竟,您不會指望用戶打開 XamlPad,而後粘貼文檔的 XAML。查看 XAML 流文檔的一種方法是將其另存爲一個擴展名爲 .xaml 的文件,而後在 Windows 資源管理器中雙擊它。這會啓動與 XAML 文件相關聯的默認應用程序(一般是 Internet Explorer®),從而顯示該文檔。結果如圖6所示。




圖 6 在 Internet Explorer 中顯示的 XAML 流文檔 (單擊該圖像得到較大視圖)
    Internet Explorer(及其餘瀏覽器)能夠顯示 XAML 內容這一事實特別有趣,由於這是將流文檔做爲您的 Web 應用程序一部分顯示的一張票證。換句話說,若是您將 XAML 流文檔上傳到您的 Web 服務器,而有人瀏覽到了該文件,他就會看到相似於圖6的效果(假設該用戶已安裝 Microsoft® .NET Framework 3.0)。固然,這也是動態運做的。若是您的 ASP.NET Web 應用程序(或任何其餘服務器端技術)動態生成了一個 XAML 流文檔,並將其做爲輸出返回(假設內容類型已適當設爲「application/xaml+xml」),用戶就會看到做爲您應用程序一部分的流文檔,這在 許多情形下必然至關有用。圖 7 顯示了一個簡單的生成流文檔的 ASP.NET 頁面。

圖7 動態 ASP.NET 流文檔
<%@ Page Language=」C#」 ContentType=」application/xaml+xml」 %>
<FlowDocument
xmlns=’http://schemas.microsoft.com/winfx/2006/xaml/presentation’
xmlns:x=’http://schemas.microsoft.com/winfx/2006/xaml’>
<Paragraph FontFamily=」Calibri」 FontWeight=」Bold」 FontSize=」24」>
WPF Flow Documents
</Paragraph>
<Paragraph FontFamily=」Calibri」 FontWeight=」Bold」 FontSize=」12」>
<%= DateTime.Now.ToString(「d」) %>
</Paragraph>
<Paragraph>
The quick brown fox jumps over the lazy dog. ...
</Paragraph>
</FlowDocument> 
 
顯示流文檔

    您可能已經注意到,每當顯示流文檔時(不管是在瀏覽器中仍是在 XamlPad 中),顯示的彷佛不僅是文檔自己,還會顯示其餘少許內容。特別是,文檔底部會呈現一些控件。如圖 8 所示,流文檔默認會經過 FlowDocumentReader 控件呈現,它提供了一組標準功能,例如縮放、分頁、不一樣視圖模式切換,甚至查找功能。事實上,流文檔須要由一些可以顯示它們的某類控件承載。流文檔的默認 查看器是 FlowDocumentReader 控件,除非您明確使用其餘控件,不然該控件會自動實例化。WPF 目前提供三個不一樣的控件用於查看流文檔:




圖 8 FlowDocumentReader 控件中的控件按鈕 (單擊該圖像得到較大視圖)
    FlowDocumentScrollViewer 此控件使用一個滾動條以連續的流顯示文檔,相似網頁或 Microsoft Word 中的「Web 版式」。圖 9 顯示的是滾動查看器中的文檔。




圖 9 使用 FlowDocumentScrollViewer 控件 (單擊該圖像得到較大視圖)
    FlowDocumentPageViewer 此控件以單獨的頁面顯示流文檔,讓頁面翻轉而非滾動。這與 Word 中的「閱讀版式」相似。圖 10 顯示的是頁面查看器。在這裏,圖 9 中的文檔使用 FlowDocumentPageViewer 控件呈現,滾動條被分頁機制取代。這種簡單的流佈局方法已被一種更高級、多列的分頁佈局所取代。



圖 10 使用 FlowDocumentPageViewer 控件 (單擊該圖像得到較大視圖)
    FlowDocumentReader 此控件組合了滾動查看器和頁面查看器,讓用戶能夠在兩種方法之間切換。這是用於流文檔的默認控件,並且對於以顯示覆雜文本爲特點的應用程序一般是一個不錯 的選擇。在圖 11 中,圖 9 和圖 10 中顯示的同一文檔經過 FlowDocumentReader 呈現,它將滾動查看器和頁面查看器兩種方法結合在一塊兒。此外,它還啓用了其餘控件中默認隱藏的搜索功能(其餘查看器的確支持查找功能,經過執行 ApplicationCommands.Find 命令或從鍵盤上按 Ctrl+F 可實現該功能)。讀取器控件還支持多頁視圖,這稍微改變了基於頁面的呈現,以及列和圖的呈現方式。



圖 11 使用 FlowDocumentReader 控件 (單擊該圖像得到較大視圖)
    雖然 FlowDocumentReader 幾乎對全部基本使用情形都頗有吸引力,但選擇怎樣的控件還需視您的狀況而定。它用途普遍且功能強大,並支持分頁佈局,這在許多情形下是比滾動更高級的功 能。關於該主題的更詳細討論不在本文探討範圍以內,但事實證實,滾動及重合等相關效果是人們較之數字化文本更喜歡打印文本的主要緣由之一。分頁方法在許多 狀況下更爲天然,有助於讓數字化閱讀被更廣泛接受。

    那麼您如何定義要使用哪一個控件呢?一個簡單但至關強力的方法是將想要的控件添加到文檔的 XAML 中:
<FlowDocumentScrollViewer
xmlns=’http://schemas.microsoft.com/winfx/2006/xaml/presentation’
xmlns:x=’http://schemas.microsoft.com/winfx/2006/xaml’>
<FlowDocument>
<Paragraph>The quick <Bold>brown fox</Bold> jumps over the lazy
dog.</Paragraph>
</FlowDocument>
</FlowDocumentScrollViewer> 
 
    在本例中,文檔根已被設爲一個 FlowDocumentScrollViewer 標記。也就是說,您再也不只是定義一個單純的文檔而已。相反,您在定義一個完整的 XAML 界面,而它碰巧使用滾動查看器做爲其根。滾動查看器的內容是最開始示例中的流文檔。(請注意,命名空間定義如今使用滾動查看器標記,而非流文檔標記)。圖 9 到圖 11 是使用此方法建立的,不一樣的查看器控件用做根元素。

    我爲什麼把這稱爲強力方法呢?這是由於,從結構角度看,將用戶界面定義與其實際數據相混合會致使一些問題。而更理想的情況是將文檔與其界面分開。將讀取器與 文檔混合在一塊兒有點像建立一個 SQL Server™ 表,並出於某種緣由定義該表只能在 Windows Forms DataGrid 中顯示。有若干方法可以讓文檔與 UI 定義分離。若是想使用上文所示的 ASP.NET 方法將流文檔做爲 Web 應用程序的一部分顯示,您可以使用所需的查看器控件定義 ASP.NET 頁面,而後只要使用標準 ASP.NET 代碼合併到實際內容(單獨存儲,可能在數據庫中)便可。

    另外一方面,在一個典型的 WPF 應用程序中,您能夠只要使用標準 WPF、Windows 和 XAML 瀏覽器應用程序 (XBAP) 方法來定義您的用戶界面,而後動態加載您的文檔便可。圖 12 顯示的是使用我文章中的一個虛構庫的一個簡單示例,這些文章顯示在左上角的一個列表框中。用戶從列表中選擇一篇文章時,該文檔會自動加載到佔用大部分窗體 的 Flow Document Reader 控件。請注意,諸如 alpha 值混合處理等標準 WPF 技術在此設置中也能使用。您會注意到,實際的流文檔是半透明的,背景中個人照片也在閃爍。另外也請注意,應用程序使用了一個列表框、圖像,一個標籤和一個 FlowDocumentReader 控件來建立虛構文章的庫。




    圖 12 使用列表框、圖像、標籤和 FlowDocumentReader 控件 (單擊該圖像得到較大視圖)
這個例子最棘手的地方是將實際文檔加載到查看器控件中。這經過 System.Windows.Markup.XamlReader 類實現,它容許動態加載任何 XAML 內容,包括但不限於流文檔。如下是我綁定到列表框選定更改事件的一行代碼:
documentReader.Document =
(FlowDocument)XamlReader.Load(
File.OpenRead(fileName));
 
    Load 方法會返回一個對象,由於 XAML 文件中的根元素能夠表明許多不一樣類型。在個人例子中,我知道返回值爲 FlowDocument,所以我只要執行一個轉換,並將該文檔指定給 FlowDocumentReader 控件的 Document 屬性便可(此例中,我將控件實例命名爲 documentReader)。請記住,這只是個例子。生產品質的代碼此處固然還須要一些錯誤處理。

    請注意,您瞭解的關於 WPF 的全部東西都適用於本例。例如,讀取器控件只是支持樣式的標準 WPF 控件。也就是說,您能夠徹底更改全部 UI 元素的外觀,例如縮放欄、視圖模式切換或分頁控件。(您的控制能力受到限制的惟一元素是搜索框,雖然若是您不喜歡它,就根本沒必要用它。)

    此外,個人例子顯示的是基於 Windows 的應用程序,相同的應用程序也能夠做爲 XBAP 部署,並在 Web 瀏覽器內運行(固然,咱們仍是假設用戶已安裝了 .NET Framework 3.0)。請注意,Microsoft Silverlight™(原代號爲「WPF/E」)是不夠的,由於 Silverlight 只支持 WPF 的子集,且並不支持流文檔。
建立流文檔 

    如何編寫流文檔?固然,開發人員始終能夠使用諸如 XamlPad 等低級工具來編寫流文檔。可是,在現實環境下,這不大可能。一般,流文檔是使用 WYSIWYG 編輯器或經過從現有文檔格式進行的內容轉換來建立的。因爲流文檔能夠使用 XAML 定義,所以轉換現有 XML 內容特別簡單。但也能夠轉換 HTML 和 Word 文檔,而無需付出過大的精力(儘管須要編碼,由於迄今爲止還沒有出現現成工具)。

    對於 WYSIWYG 編輯,WPF 提供了一個現成的控件。WPF RichTextBox 控件能夠本機編輯 XAML 流文檔。該控件名稱讓人誤覺得它是專門針對 RTF 格式。儘管這個控件也支持 RTF,但實際上它主要用於流文檔。事實上,該控件實際上會反映流文檔查看控件,只不過它也支持編輯。有些人甚至會說,RichTextBox 控件應該被視爲顯示流文檔的另外一種方式。

    將下列示例鍵入 XamlPad 中,以查看運行中的 RichTextBox 控件:
<RichTextBox
xmlns=’http://schemas.microsoft.com/
winfx/2006/xaml/presentation’
xmlns:x=’http://schemas.microsoft.com/winfx/2006/xaml’>
<FlowDocument>
<Paragraph>
The quick brown fox jumps over the lazy dog.
</Paragraph>
</FlowDocument>
</RichTextBox> 
 
    恰如讀取器控件同樣,RichTextBox 也有一個 Document 屬性,您能夠自動以此會話中的流文檔填充其值。這實際上會建立一個與 FlowDocumentScrollViewer 控件看起來很類似的 UI,只不過其中的文本能夠編輯。請注意,此文本框控件始終以滾動方式處理流文檔。在分頁或多列模式下,沒法在 RichTextBox 中編輯流文檔。不過,編輯操做的結果是一個標準流文檔,該文檔能夠使用您已看到的任何一種查看器機制顯示,其中包括多列和分頁模式。

    關於 RichTextBox,值得一提的其中一項功能是集成的拼寫檢查。您能夠按以下所示啓用該功能: 
<RichTextBox SpellCheck.IsEnabled=」true」>
<FlowDocument>...</FlowDocument>
</RichTextBox>13 顯示了運行中的拼寫檢查程序。



    圖 13 帶有拼寫檢查功能的 RichTextBox 控件 (單擊該圖像得到較大視圖)
使 用此控件惟一複雜的地方是加載與保存。在許多情形下,您可能不會像在以前的例子中那樣,將 RichTextBox 內容編碼到 UI XAML 中,而是要動態加載和保存文檔。RichTextBox 中文本的加載操做與爲查看器控件加載流文檔相同(見上文)。保存文檔本質上則徹底相反:您要先拿到文檔對象,而後將其序列化回 XAML,以下所示:
System.Windows.Markup.XamlWriter.
Save(richTextBox.Document)
 
    這會將 XAML 做爲一個字符串返回,而後您能夠將其存儲到文件或數據庫中,或者使用您能想到的任何其餘方式。

    RichTextBox 很是方便,不過在這裏仍是要提醒幾句話。雖然流文檔表明了可用於呈現屏幕文檔的最複雜的技術,但 RichTextBox 控件卻一點也不復雜。它是編輯小型文檔和文本段的極佳選擇,可是您不會用它來編寫書籍、雜誌或營銷手冊。對於這些長格式,它的呈現過於簡單,由於它不支持 除滾動佈局以外的其餘任何佈局(也就是說,尚未一種很好的可視方式可用於建立我稍後將談到的高級佈局)。一樣,用於保存文檔的方法也常常不盡人意。 XmlWriter 類只是使用實時的內存中文檔,並將其轉換爲 XAML,但遺憾的是,對於大規模的流文檔操做很是重要的許多概念(例如樣式),它並未注意。結果,儘管 XAML 忠實地保存了文檔的外觀,但文檔看起來每每不太清爽,而且很大。RichTextBox 控件固然仍是頗有用的,可是別期望將它做爲屏幕內容的桌面出版解決方案(雖然這類應用程序很是急需)。

    探究佈局可能性

    至此您已瞭解瞭如何編寫和查看流文檔,接着讓咱們回到文檔自己,看看更多的功能。流文檔很是複雜,探究全部可用功能超出了本文範圍,不過我想再討論幾項功能。

    其中一項一直讓我着迷的功能是「最佳段落」。啓用該功能後,能夠在指定段落內儘量平均地分佈空白,從而帶來顯著改進的閱讀體驗。「最佳段落」特別適合與 另外一項內置功能「斷字」搭配使用,該功能(竟然)會執行動態整個流文檔或者個別段落的斷字。

    啓用最佳段落和斷字功能是項很是簡單的操做:
<FlowDocument IsOptimalParagraphEnabled=」true」
IsHyphenationEnabled=」true」>14 顯示的是相同的文檔,只是呈現時啓用或禁用了這些功能。兩個版本間的區別很是細微,可是很是重要。請注意,左邊的版本看起來更平和,主要由於詞與詞之間的 空白分佈得更平均,且從總體上減小了。特別是在屏幕上閱讀大量文本時,這個看起來細小的區別會變得極爲重要。



圖 14 最佳段落和斷字 (單擊該圖像得到較大視圖)
    如您所見,FlowDocumentReader 控件採起多列的方法呈現文本。這是另外一項很是重要的可讀性功能,由於人們不喜歡讀跨越整個寬屏顯示頁面寬度的一行行文字。實際列寬因各類因素會有所不一樣, 例如用於內容顯示的可用總寬度、縮放係數和定義的列寬等。流文檔的默認列寬爲字體大小的 20 倍,默認字體大小約爲 300 個與設備無關的像素(3 1/8 英寸的精確尺寸顯示)。您能夠很輕鬆地覆蓋此默認設置:
<FlowDocument ColumnWidth=」400」>
 
    這會產生寬度約 400 像素的列。不過,還有其餘一些因素會影響實際寬度。舉例來講,若是縮放比例是 50%,那麼實際列寬就只有 200 像素。另外,到目前爲止,列寬更多地會被看做最小列寬。這意味着,若是可用總寬度爲 900 像素,要呈現結果包含兩列,而且要充分填滿這整個 900 像素的話,就要讓每列的寬度都超過定義的 400 像素。一般都須要這樣,由於它會讓呈現結果看起來很是美觀。不過,若是您不想執行該行爲,而只但願列寬實際就是 400 像素的話,能夠確保列寬不是靈活可變的:
<FlowDocument ColumnWidth=」400」 IsColumnWidthFlexible=」false」>
 
    如今,全部列都正好是 400 像素(100% 縮放),剩餘空間就讓它顯示爲空白。

    另外一個您可能想嘗試的與列相關的設置是列之間的空隙。這能夠經過 ColumnGap 屬性調整(此設置也是基於與設備無關的像素數):
<FlowDocument ColumnGap=」25」>
 
其中一個相關的設置是列規則,它容許在列之間定義一個可視元素。請看此例(其結果見圖 15):




圖 15 列之間採用簡單規則的流文檔 (單擊該圖像得到較大視圖)
<FlowDocument ColumnRuleWidth=」5」 ColumnRuleBrush=」Red」>
 
    固然,在許多出版物中,文檔並不僅是採用簡單的列布局。一般還存在從通常流中提取出來的其餘元素。您已見過這樣的例子,例如將圖像置於文檔中。圖 12 顯示了圖形設計師經常使用的一種排列方式。此圖像位於兩列之間,周圍環繞文字,圖像方方正正地位於內容中間,並無影響任何一列的文字佈局。這是一種常見的布 局選擇,只是還不能用於流文檔以前我所瞭解的動態屏幕閱讀環境。
   
    建立此類佈局的關鍵是圖形塊,它容許定義不與文檔其他部分那樣佈局的內容。將圖像置於圖形標記內部就是一例,但圖形還有許多其餘用途。例如,您能夠使用圖形來定義橫跨整個文檔寬度的標題:
<Paragraph>
<Figure HorizontalAnchor=」ContentLeft」 VerticalAnchor=」ContentTop」
Width=」1Content」>
<Paragraph FontSize=」36」 FontWeight=」Bold」>Go With
The Flow</Paragraph>
</Figure>
Windows Presentation Foundation in Windows Vista provides a great set
of features.
</Paragraph> 
 
    在本代碼中,圖形包含另外一個段落,即用做標題的文本。請注意,這裏有一些您可用來建立高級、靈活文檔的便捷屬性。例如,看一下圖形的寬度。我沒有將寬度設 爲特定像素數,而是將其設爲內容的確切寬度,這會根據整個內容的寬度自動調整圖形寬度。

    請看圖 16。其中,您會注意到標題(經過圖形放置)設爲橫跨整個內容寬度,這就將全部四列的位置都向下推移了。該圖像從垂直和水平方向看都定位於頁面中央。




圖 16 標題橫跨四列 (單擊該圖像得到較大視圖)
    請注意,其寬度與內容相關的圖形沒必要始終與內容同樣寬。如下例來講,圖形寬度設爲內容寬度的 75%<Figure Width=」0.75Content」>
 
    寬度也可與其餘項相關,例如列寬。下例圖形始終是兩列寬(除非只顯示一列,那樣寬度就會減爲一列):
<Figure Width=」2Column」>
 
    固然,圖形高度可經過相似方式定義(雖然圖形一般是隨着內容縱向變化)。

    另外一重要方面是圖形的位置。在代碼段中,它設爲橫向定位爲靠左,縱向定位爲靠上。也就是說,圖形會出如今當前內容頁面的左上角,而不管其實際如何定義。然 而在本示例中,圖形被定義爲文檔的第一個元素,但即便該標題以前已有段落,它也會因爲這些設置而被上移和左移。圖 12 和圖 16 中的照片已按相似方式,將其橫向定位爲「PageCenter」,在列之間移動。(全部這些設置的可用屬性值均可以在 WPF 文檔中找到)。

    您可能已經注意到,本文涉及了大量手動編碼。例如,每當須要改變字體時,您都要將該信息添加到塊或內嵌元素中。到目前爲止,這還不是一個大問題,由於大部 分示例都很小。可是,若是有一本每 50 頁爲一章的書,您要改變每一段的字體,每次都手動來改的話,無疑會很繁重。幸運的是,如今有了一個更好的辦法:如 WPF 中的其餘任何內容同樣,流文檔支持樣式。樣式可被定義爲實際流文檔中指定名稱的資源。如下是定義字體信息的樣式:
<FlowDocument>
<FlowDocument.Resources>
<Style x:Key=」MyStyle」>
<Setter Property=」TextElement.FontSize」 Value=」12」 />
<Setter Property=」TextElement.FontFamily」 Value=」Bodoni MT」 />
</Style>
<FlowDocument.Resources>
...
</FlowDocument> 
 
    而後,該樣式會經過如下方式應用到段落(和其餘元素):
<Paragraph Style=」{StaticResource MyStyle}」>The quick... </Paragraph> 
 
    因爲流文檔的特性,樣式特別經常使用。建議您對於最簡單情形以外的任何情形,都使用樣式來定義大部分格式選項,而不是經過個別內嵌元素的屬性。樣式可以讓您的文檔保持緊湊,並且更易維護。
View Code

WPF入門基礎知識編程

 WPF基礎知識 
Windows Presentation Foundation (WPF) 是下一代顯示系統,用於生成能帶給用戶震撼視覺體驗的 
Windows 客戶端應用程序。使用 WPF,您能夠建立普遍的獨立應用程序以及瀏覽器承載的應用程
序。 
WPF 的核心是一個與分辨率無關而且基於向量的呈現引擎,旨在利用現代圖形硬件的優點。WPF 
經過一整套應用程序開發功能擴展了這個核心,這些功能包括 可擴展應用程序標記語言 (XAML)、
控件、數據綁定、佈局、二維和三維圖形、動畫、樣式、模板、文檔、媒體、文本和版式。WPF 
包含在 Microsoft .NET Framework 中,使您可以生成融入了 .NET Framework 類庫的其餘元素的應用
程序。 
爲了支持某些更強大的 WPF 功能並簡化編程體驗,WPF 包括了更多編程構造,這些編程構造加強
了屬性和事件:依賴項屬性和路由事件。有關依賴項屬性的更多信息,請參見依賴項屬性概述。
有關路由事件的更多信息,請參見路由事件概述。 
這種外觀和行爲的分離具備如下優勢: 
1下降了開發和維護成本,由於外觀特定的標記並無與行爲特定的代碼緊密耦合。 
2開發效率更高,由於設計人員能夠在開發人員實現應用程序行爲的同時實現應用程序的外觀。 
3能夠使用多種設計工具實現和共享 XAML 標記,以知足應用程序開發參與者的要求:Microsoft 
Expression Blend 提供了適合設計人員的體驗,而 Visual Studio 2005 針對開發人員。 
4 WPF 應用程序的全球化和本地化大大簡化(請參見 WPF 全球化和本地化概述)。 

在運行時,WPF 將標記中定義的元素和屬性轉換爲 WPF 類的實例。例如,Window 元素被轉換爲 Window 類的實
例,該類的 Title 屬性 (Property) 是 Title 屬性 (Attribute) 的值。 
注意在constructor中Call: InitializeComponent(); 
x:Class 屬性用於將標記與代碼隱藏類相關聯。InitializeComponent 是從代碼隱藏類的構造函數中調用的,用於將標記中定義的 
UI 與代碼隱藏類相合並。((生成應用程序時將爲您生成 InitializeComponent,所以您不須要手動實現它。)x:Class 和 
InitializeComponent 的組合確保您的實現不管什麼時候建立都能獲得正確的初始化。 

.NET Framework、System.Windows、標記和代碼隱藏構成了 WPF 應用程序開發體驗的基礎 

窗口: WPF 對話框:MessageBox、OpenFileDialog、SaveFileDialog 和 PrintDialog。 
WPF 提供瞭如下兩個選項做爲替代導航宿主: 

. Frame,用於承載頁面或窗口中可導航內容的孤島。 
. NavigationWindow,用於承載整個窗口中的可導航內容。 


啓動:StartupUri="MainWindow.xaml" />此標記是獨立應用程序的應用程序定義,並指示 WPF 建立一個在應用程
序啓動時自動打開 MainWindow 的 Application 對象。 

WPF 控件一覽 


此處列出了內置的 WPF 控件。 

. 按鈕:Button 和 RepeatButton。 
. 對話框:OpenFileDialog、PrintDialog 和 SaveFileDialog。 
. 數字墨跡:InkCanvas 和 InkPresenter。 
. 文檔:DocumentViewer、FlowDocumentPageViewer、FlowDocumentReader、FlowDocumentScrollViewer 
和 StickyNoteControl。 
. 輸入:TextBox、RichTextBox 和 PasswordBox。 
. 佈局:Border、BulletDecorator、Canvas、DockPanel、Expander、Grid、GridView、GridSplitter、
GroupBox、Panel、ResizeGrip、Separator、ScrollBar、ScrollViewer、StackPanel、Thumb、Viewbox、
VirtualizingStackPanel、Window 和 WrapPanel。 
. 媒體:Image、MediaElement 和 SoundPlayerAction。 
. 菜單:ContextMenu、Menu 和 ToolBar。 
. 導航:Frame、Hyperlink、Page、NavigationWindow 和 TabControl。 
. 選擇:CheckBox、ComboBox、ListBox、TreeView、RadioButton 和 Slider。 
. 用戶信息:AccessText、Label、Popup、ProgressBar、StatusBar、TextBlock 和 ToolTip。 


輸入和命令 :控件一般檢測和響應用戶輸入。WPF 輸入系統使用直接事件和路由事件來支持文本輸入、焦點管理和鼠標定位。有
關更多信息,請參見輸入概述。 
佈局系統的基礎是相對定位,它提升了適應窗口和顯示條件變化的能力。此外,佈局系統還管理控件之間的協商以肯定佈局。協
商過程分爲兩步:第一步,控件向父控件通知它所需的位置和大小;第二步,父控件通知該控件它能夠具備多大空間 

. Canvas:子控件提供其本身的佈局。 
. DockPanel:子控件與面板的邊緣對齊。 
. Grid:子控件按行和列放置。 
. StackPanel:子控件垂直或水平堆疊。 
. VirtualizingStackPanel:子控件被虛擬化,並沿水平或垂直方向排成一行。 
. WrapPanel:子控件按從左到右的順序放置,若是當前行中的控件數多於該空間所容許的控件數,則換至下一行 


由父控件實現的、供子控件使用的屬性是一種 WPF 構造,稱爲「附加屬性」 

爲了簡化應用程序開發,WPF 提供了一個數據綁定引擎以自動執行這些步驟。數據綁定引擎的核心單元是 Binding 類,它
的任務是將控件(綁定目標)綁定到數據對象(綁定源)。下圖說明了這種關係。 


基本數據綁定示意圖 
WPF 數據綁定引擎還提供了其餘支持,包括驗證、排序、篩選和分組。此外,當標準 WPF 控件顯示的 UI 不合適時,數據綁定
還支持使用數據模板爲綁定的數據建立自定義 UI。 
WPF 引進了一組普遍的、可伸縮且靈活的圖形功能,它們具備如下優勢: 

. 與分辨率和設備無關的圖形。WPF 圖形系統的基本度量單位是與設備無關的像素,它等於一英寸的 1/96,而無論實際的
屏幕分辨率是多少,爲與分辨率和設備無關的呈現提供了基礎。每一個與設備無關的像素都會自動縮放,以符合呈現該像素的
系統上的每英寸點數 (dpi) 設置。 
. 更高的精度。WPF 座標系是使用雙精度浮點數字測量的,而不是使用單精度浮點數字。轉換值和不透明度值也以雙精度表
示。WPF 還支持普遍的顏色域 (scRGB),併爲管理來自不一樣顏色空間的輸入提供完整的支持。 
. 高級圖形和動畫支持。WPF 經過爲您管理動畫場景簡化了圖形編程;您不須要擔憂場景處理、呈現循環和雙線性內插算法。
此外,WPF 還提供了命中測試支持和全面的 alpha 合成支持。 
. 硬件加速。WPF 圖形系統利用了圖形硬件的優點來最小化 CPU 使用率。 


 


Path 對象可用於繪製閉合或開放形狀、多線形狀,甚至曲線形狀。 

Geometry 對象可用於對二維圖形數據進行剪裁、命中測試和呈現。 
WPF 二維功能的子集包括漸變、位圖、繪圖、視頻繪製、旋轉、縮放和扭曲等視覺效果。這些均可以使用畫筆完成;下圖演示了某
些示例。 
WPF 動畫支持能夠使控件變大、旋轉、調節和淡化,以產生有趣的頁面過渡和更多效果。您能夠對大多數 WPF 類(甚至自定義類)
進行動畫處理。下圖演示了一個簡單的活動動畫。 
爲了加快高質量的文本呈現,WPF 提供瞭如下功能: 

. OpenType 字體支持。 
. ClearType 加強。 
. 利用硬件加速優點的高性能。 
. 文本與媒體、圖形和動畫的集成。 
. 國際字體支持和回退機制。 



WPF 自己支持使用三種類型的文檔:流文檔、固定文檔和 XML 紙張規範 (XPS) 文檔。WPF 還提供了用於建立、查看、管理、批註、
打包和打印文檔的服務。 
XML 紙張規範 (XPS) 文檔創建在 WPF 的固定文檔基礎上。XPS 文檔使用基於 XML 的架構進行描述,該架構本質上就是電子紙的
分頁表示。XPS 是一個開放的、跨平臺的文檔格式,旨在簡化分頁文檔的建立、共享、打印和存檔。XPS 技術的重要功能包括: 

打包 

WPF System.IO.Packaging API 容許您的應用程序將數據、內容和資源組織成一個可移植、易於分發和訪問的 ZIP 文檔。能夠包
括數字簽名以對程序包中包含的項目進行身份驗證,並肯定簽名的項目未被篡改或修改。您還能夠使用權限管理對軟件包進行加密,
以限制對受保護信息的訪問。 

打印 

.NET Framework 包括一個打印子系統,WPF 經過支持更好的打印系統控制對其進行了加強。打印加強功能包括: 

. 實時安裝遠程打印服務器和隊列。 
. 動態發現打印機功能。 
. 動態設置打印機選項。 
. 打印做業從新路由和從新排列優先級次序。 


內容模型 

大多數 WPF 控件的主要目的都是爲了顯示內容。在 WPF 中,構成控件內容的項目類型和數量被稱爲控件的「內容模型」。有些控件
只能包含一個項目和內容類型;例如,TextBox 的內容爲字符串值,該值被分配給 Text 屬性。 

觸發器 

儘管 XAML 標記的主要目的是實現應用程序的外觀,但您仍然能夠使用 XAML 實現應用程序行爲的某些方面。一個示例就是使用觸
發器根據用戶交互更改應用程序的外觀。有關更多信息,請參見樣式設置和模板化中的「觸發器」。 

數據模板 

控件模板使您能夠指定控件的外觀,數據模板則容許您指定控件內容的外觀。數據模板一般用於改進綁定數據的顯示方式。下圖演示 
ListBox 的默認外觀,它被綁定到一個 Task 對象集合,該集合中的每一個任務都有一個名稱、說明和優先級。 

開發人員和設計人員使用樣式能夠對其產品的特定外觀進行標準化。WPF 提供了一個強大的樣式模型,其基礎是 Style 元素。下面
的示例建立一個樣式,該樣式將窗口中的每一個 Button 的背景色設置爲 Orange。 

資源 

一個應用程序中的各控件應共享相同的外觀,包括從字體和背景色到控件模板、數據模板和樣式的全部方面。您能夠使用 WPF 對 用
戶界面 (UI) 資源的支持將這些資源封裝到一個位置,以便於重複使用。 
資源範圍有多種,包括下面按解析順序列出的範圍: 

1. 單個控件(使用繼承的 FrameworkElement..::.Resources 屬性)。 
2. Window 或 Page(也使用繼承的 FrameworkElement..::.Resources 屬性)。 
3. Application(使用 Application..::.Resources 屬性)。 


範圍的多樣性使您能夠靈活選擇定義和共享資源的方式。 


 做爲將資源與特定範圍直接關聯的一個備用方法,您能夠使用單獨的 ResourceDictionary(能夠在應用程序的其餘部分引用)
打包一個或多個資源。例如,下面的示例在資源字典中定義默認背景色。 
因爲 WPF 的外觀由模板定義,所以 WPF 爲每一個已知 Windows 主題包括了一個模板, 
WPF 中的主題和外觀均可以使用資源字典很是輕鬆地進行定義 
自定義控件 
儘管 WPF 提供了大量自定義項支持,您仍然可能會遇到現有 WPF 控件不能知足應用程序或用戶需求的狀況。在如下狀況下可能會
出現這種情形: 

. 沒法經過自定義現有 WPF 實現的外觀來建立您須要的 UI。 
. 現有 WPF 實現不支持(或很難支持)您須要的行爲。 
. 用戶控件模型。從 UserControl 派生的自定義控件,由其餘一個或多個控件組成。 
. 控制模型。從 Control 派生的自定義控件,用於生成使用模板將其行爲和外觀相分離的實現,與多數 WPF 控件很是類似。
從 Control 派生使您能夠比用戶控件更自由地建立自定義 UI,但可能須要投入更多精力。 
. 框架元素模型。從 FrameworkElement 派生的自定義控件,其外觀由自定義呈現邏輯(而不是模板)定義。 


 
WPF 是一種全面的顯示技術,用於生成多種類型的具備視覺震撼力的客戶端應用程序。本文介紹了 WPF 的關鍵功能。 
下一步爲生成 WPF 應用程序! 
將數據鏈接到控件 

在此步驟中,您將編寫代碼來檢索從 HomePage 上的人員列表中選定的當前項,並在實例化過程當中將對該當前項的引用傳遞給 
ExpenseReportPage 的構造函數。ExpenseReportPage 使用已傳入的項設置數據上下文,這就是 ExpenseReportPage.xaml 
中定義的控件要綁定的內容。 
WPF 3.5 定義了一個新的 XML 命名空間 http://schemas.microsoft.com/netfx/2007/xaml/presentation。在使用 WPF 
3.5 生成應用程序時,能夠使用此命名空間或在 WPF 3.0 中定義的命名空間。 
應用程序 

應用程序模型已獲得下列改進: 

. 提供全面的外接程序支持,能夠支持獨立應用程序和 XAML 瀏覽器應用程序 (XBAP) 中的非可視化和可視化外接程序。 
. XBAP 如今可在 Firefox 中運行。 
. 能夠在 XBAP 與同一源站點中的 Web 應用程序之間共享 Cookie。 
. 爲提升工做效率而改進的 XAML IntelliSense 體驗。 
. 更普遍的本地化支持。 


WPF 中的可視化和非可視化外接程序 

可擴展的應用程序能夠公開它的功能,從而容許其餘應用程序與該應用程序集成並擴展其功能。外接程序是應用程序公
開其擴展性的一種常見方式。在 .NET Framework 中,外接程序一般是做爲動態連接庫 (.dll) 打包的程序集。外接程


http://i.msdn.microsoft.com/Platform/Controls/CollapsibleArea/resources/minus.gif
序由宿主應用程序在運行時動態加載,以便使用和擴展由宿主公開的服務。宿主和外接程序經過已知協定進行交互,該
協定一般是由宿主應用程序發佈的公共接口。 


對 XBAP 的 Firefox 支持 

WPF 3.5 的一個插件使得 XBAP 可以從 Firefox 2.0 中運行,WPF 3.0 中沒有這個功能。其中的重要功能包括: 

. 若是 Firefox 2.0 是默認瀏覽器,XBAP 將使用這一配置。也就是說,若是 Firefox 2.0 是默認瀏覽器,XBAP 將不使
用 Internet Explorer。 
. 運行 Internet Explorer 的 XBAP 所具有的安全功能對於在 Firefox 2.0 中運行的 XBAP 一樣可用,其中包括部分信
任的安全沙盒。由瀏覽器提供的其餘安全功能因瀏覽器而異。 


Cookie 

獨立 WPF 應用程序和 XBAP 能夠建立、獲取和刪除會話和持久性 Cookie。在 WPF 3.5 中,能夠在 XBAP、Web 
服務器和同一源站點中的 HTML 文件之間共享持久性 Cookie。 
圖形 

如今,您能夠將經過 HTTP 下載的圖像緩存到本地 Microsoft Internet Explorer 臨時文件緩存中,這樣,對該圖像
的後續請求未來自本地磁盤而非 Internet。根據圖像大小的不一樣,這一功能能夠顯著改善網絡性能。爲支持此功能,
添加了下面的成員: 

. BitmapImage..::.UriCachePolicy 
. BitmapDecoder..::.Create(Uri, BitmapCreateOptions, BitmapCacheOption, RequestCachePolicy) 
. BitmapFrame..::.Create(Uri, RequestCachePolicy) 
. BitmapFrame..::.Create(Uri, BitmapCreateOptions, BitmapCacheOption, RequestCachePolicy) 


添加了 BitmapSource..::.DecodeFailed 事件,用以在圖像因爲文件頭損壞而加載失敗時向用戶發出通知。 
數據綁定 
數據綁定已獲得下列改進: 

. 新的調試機制下降了調試數據綁定的難度。 
. 數據模型經過提供對 IDataErrorInfo 接口的支持,能夠實現對業務層的驗證。另外,驗證模型如今還支持使用屬性語法
來設置驗證規則。 
. 數據綁定模型如今支持 LINQ 和 XLINQ。 


 文檔 

FlowDocumentPageViewer、FlowDocumentScrollViewer 和 FlowDocumentReader 各有一個名爲 Selection 的
新的公共屬性。該屬性獲取表示文檔中選定內容的 TextSelection。 
應用程序是否具備特定於語言或非特定於語言的資源。例如,您是否爲 Application、Page 和 Resource 類型指定了 
UICulture 項目屬性或可本地化的元數據? 

本主題包括下列各節。 


WPF 在 .NET Framework 中的位置。
. System.Object 
. System.Threading.DispatcherObject 
. System.Windows.DependencyObject 
. System.Windows.Media.Visual 
. System.Windows.UIElement 
. System.Windows.FrameworkElement 
. System.Windows.Controls.Control 
. 摘要 
. 相關主題 
. WPF 主要編程模型是經過託管代碼公開的。在 WPF 的早期設計階段,曾有過大量關於如何界定系統的託管組件和非託
管組件的爭論。CLR 提供一系列的功能,能夠令開發效率更高而且更加可靠(包括內存管理、錯誤處理和通用類型系統
等),但這是須要付出代價的。 
. 下圖說明了 WPF 的主要組件。關係圖的紅色部分(PresentationFramework、PresentationCore 和 milcore)是 
WPF 的主要代碼部分。在這些組件中,只有一個是非託管組件 – milcore。milcore 是以非託管代碼編寫的,目的是
實現與 DirectX 的緊密集成。WPF 中的全部顯示是經過 DirectX 引擎完成的, 
. 
. System.Threading.DispatcherObject 



. WPF 中的大多數對象是從 DispatcherObject 派生的,這提供了用於處理併發和線程的基本構造。WPF 基於調度程序實
現的消息系統。其工做方式與常見的 Win32 消息泵很是相似;事實上,WPF 調度程序使用 User32 消息執行跨線程調用。 
. System.Windows.DependencyObject 
. 生成 WPF 時使用的主要體系結構原理之一是首選屬性而不是方法或事件。屬性是聲明性的,使您更方便地指定意圖而
不是操做。它還支持模型驅動或數據驅動的系統,以顯示用戶界面內容。這種理念的預期效果是建立您能夠綁定到的更
多屬性,從而更好地控制應用程序的行爲。 


WPF 提供一個豐富的屬性系統,該屬性系統是從 DependencyObject 類型派生的。該屬性系統實際是一個「依賴」屬性系
統,由於它會跟蹤屬性表達式之間的依賴關係,並在依賴關係更改時自動從新驗證屬性值。例如,若是您具備一個會繼承的
屬性(如 FontSize),當繼承該值的元素的父級發生屬性更改時,會自動更新系統。 
WPF 屬性系統的基礎是屬性表達式的概念。 
屬性系統還提供屬性值的稀疏存儲 
屬性系統的最後一個新功能是附加屬性的概念 

Visual 其實是到 WPF 組合系統的入口點 
可視對象和繪製指令的整個樹都要進行緩存 
System.Windows.UIElement 

UIElement 定義核心子系統,包括 Layout、Input 和 Event。 
輸入是做爲內核模式設備驅動程序上的信號發出的,並經過涉及 Windows 內核和 User32 的複雜進程路由到正確的進程
和線程。與輸入相對應的 User32 消息一旦路由到 WPF,它就會轉換爲 WPF 原始輸入消息,併發送到調度程序。WPF 
容許原始輸入事件轉換爲多個實際事件,容許在保證傳遞到位的狀況下在較低的系統級別實現相似「MouseEnter」的功能。 
每一個輸入事件至少會轉換爲兩個事件 – 「預覽」事件和實際事件。WPF 中的全部事件都具備經過元素樹路由的概念。如
果事件從目標向上遍歷樹直到根,則被稱爲「冒泡」,若是從根開始向下遍歷到目標,它們被稱爲「隧道」。輸入預覽事件
隧道,使樹中的任何元素都有機會篩選事件或對事件採起操做。而後,常規(非預覽)事件將從目標向上冒泡到根。 

爲了進一步深化此功能,UIElement 還引入了 CommandBindings 的概念。WPF 命令系統容許開發人員以命令終結點(一種用於
實現 ICommand 的功能)的方式定義功能 

FrameworkElement 引入的主要策略是關於應用程序佈局。FrameworkElement 在 UIElement 引入的基本佈局協定之上生成,
並增長了佈局「插槽」的概念,使佈局製做者能夠方便地擁有一組面向屬性的一致的佈局語義。HorizontalAlignment、
VerticalAlignment、MinWidth 和 Margin 等屬性使得從 FrameworkElement 派生的全部組件在佈局容器內具備一致的行爲。 

FrameworkElement 引入的兩個最關鍵的內容是數據綁定和樣式。WPF 中數據綁定的最值得關注的功能之一是引入了數據模板 
樣式其實是輕量級的數據綁定 
System.Windows.Controls.Control 
控件的最重要的功能是模板化。 
數據模型(屬性)、交互模型(命令和事件)及顯示模型(模板)之間的劃分,使用戶能夠對控件的外觀和行爲進行徹底自定義。最
常見的控件數據模型是內容模型 


您就可以建立更豐富的應用程序,這些應用程序在根本上會將數據視爲應用程序的核心驅動力。 
可擴展應用程序標記語言 (XAML) 語言支持,以便您可以在可擴展應用程序標記語言 (XAML) 標記中建立大部分應用程序 UI。 
XAML 簡化了爲 .NET Framework 編程模型建立 UI 的過程。您能夠在聲明性 XAML 標記中建立可見的 UI 元素,而後使用代碼隱
藏文件(經過分部類定義與標記相鏈接)將 UI 定義與運行時邏輯相分離。 
與其餘大多數標記語言不一樣,XAML 直接呈現託管對象的實例化。這種常規設計原則簡化了使用 XAML 建立的對象的代碼和調試訪
問。 
XAML 有一組規則,這些規則將對象元素映射爲類或結構,將屬性 (Attribute) 映射爲屬性 (Property) 或事件,並將 XML 命名空
間映射爲 CLR 命名空間。XAML 元素映射爲被引用程序集中定義的 Microsoft .NET 類型,而屬性 (Attribute) 則映射爲這些類型
的成員。 
每一個實例都是經過調用基礎類或結構的默認構造函數並對結果進行存儲而建立的。爲了可用做 XAML 中的對象元素,該類或結構必須
公開一個公共的默認(無參數)構造函數。 
<Button.Content> 
This is a button 
</Button.Content> 
XAML 的屬性 (Property) 元素語法表示了與標記的基本 XML 解釋之間的巨大背離。對於 XML,<類型名稱.屬性> 表明了另外一個
元素,該元素僅表示一個子元素,而與 TypeName 父級之間沒有必然的隱含關係。在 XAML 中,<類型名稱.Property> 直接表示 
Property 是類型名稱 的屬性(由屬性元素內容設置),而毫不會是一個名稱類似(碰巧名稱中有一個點)但卻大相徑庭的元素。 
引用值和標記擴展 

標記擴展是一個 XAML 概念。在屬性語法中,花括號({ 和 })表示標記擴展用法。此用法指示 XAML 處理不要像一般那樣將屬性
值視爲一個字符串或者可直接轉換爲文本字符串的值。 

WPF 應用程序編程中最經常使用的標記擴展是 Binding(用於數據綁定表達式)以及資源引用 StaticResource 和 DynamicResource。
經過使用標記擴展,即便屬性 (Property) 不支持對直接對象實例化使用屬性 (Attribute) 語法,也能夠使用屬性 (Attribute) 語法
爲屬性 (Property) 提供引用值 
資源只是 WPF 或 XAML 啓用的一種標記擴展用法 
Typeconverter 的屬性值: 可是不少 WPF 類型或這些類型的成員擴展了基本字符串屬性處理行爲,所以更復雜的對象類型的實例可
經過字符串指定爲屬性值 
該對象元素的任何 XML 子元素都被看成包含在一個表示該內容屬性的隱式屬性元素標記中來處理。在標記中,能夠省略 XAML 內容
屬性的屬性元素語法。在標記中指定的任何子元素都將成爲 XAML 內容屬性的值。 
XAML 內容屬性值必須連續 
XAML 處理器和序列化程序將忽略或刪除全部無心義的空白,並規範化任何有意義的空白。只有當您在 XAML 內容屬性中指定字符
串時,纔會體現此行爲的重要性。簡言之,XAML 將空格、換行符和製表符轉化爲空格,若是它們出如今一個連續字符串的任一端,
則保留一個空格。 

一個 XAML 文件只能有一個根元素,這樣才能成爲格式正確的 XML 文件和有效的 XAML 文件。一般,應選擇屬於應用程序模型一
部分的元素(例如,爲頁面選擇 Window 或 Page,爲外部字典選擇 ResourceDictionary,或爲應用程序定義根選擇 
Application)。下面的示例演示 WPF 頁面的典型 XAML 文件的根元素,其中的根元素爲 Page。 


代碼隱藏、事件處理程序和分部類要求 
1分部類必須派生自用做根元素的類的類型。您能夠在代碼隱藏的分部類定義中將派生留空,但編譯的結果會假定頁根做爲
分部類的基類,即便在沒有指定的狀況下也是如此(由於分部類的標記部分確實將頁根指定爲基)。 
2編寫的事件處理程序必須是 x:Class 標識的命名空間中的分部類所定義的實例方法。您不能限定事件處理程序的名稱來
指示 XAML 處理器在其餘類範圍中查找該處理程序,也不能將靜態方法用做事件處理程序。 
3事件處理程序必須與相應事件的委託匹配。 
類要可以實例化爲對象元素,必須知足如下要求: 

. 自定義類必須是公共的且支持默認(無參數)公共構造函數。(託管代碼結構隱式支持這樣的構造函數。) 
. 自定義類不能是嵌套類(嵌套類和其語法中的「點」會干擾其餘 WPF 功能,例如附加屬性)。 
. x:Class 能夠聲明爲充當可擴展應用程序標記語言 (XAML) 元素樹的根元素而且正在編譯(可擴展應用程序標記語言 
(XAML) 經過 Page 生成操做包括在項目中)的任何元素的屬性,也能夠聲明爲已編譯應用程序的應用程序定義中的 
Application 根的屬性。在頁面根元素或應用程序根元素以外的任何元素上以及在未編譯的可擴展應用程序標記語言 
(XAML) 文件的任何環境下聲明 x:Class 都會致使編譯時錯誤。 
. 用做 x:Class 的類不能是嵌套類。 
. 徹底能夠在沒有任何代碼隱藏的狀況下擁有 XAML 頁,從這個角度而言,x:Class 是可選的,可是,若是頁面聲明瞭事件
處理屬性值,或者實例化其定義類在代碼隱藏類中的自定義元素,那麼將最終須要爲代碼隱藏提供對適當類的 x:Class 引
用(或 x:Subclass)。 
. x:Class 屬性的值必須是一個指定類的徹底限定名的字符串。對於簡單的應用程序,只要命名空間信息與代碼隱藏的構建
方式相同(定義從類級別開始),就能夠省略命名空間信息。頁面或應用程序定義的代碼隱藏文件必須在代碼文件內,而該
代碼文件應做爲產生已編譯應用程序的項目的一部分而包括在該項目中。必須遵循 CLR 類的命名規則;有關詳細信息,請
參見 Type Definitions(類型定義)。默認狀況下,代碼隱藏類必須是 public 的,但也能夠經過使用 x:ClassModifier 
屬性定義爲另外一訪問級別。 
. 請注意,x:Class 屬性值的此含義是 WPF XAML 實現所特有的。WPF 外部的其餘 XAML 實現可能不使用託管代碼,所以
可能使用不一樣的類解析公式。 


 
x:Code 是在 XAML 中定義的一種指令元素。x:Code 指令元素能夠包含內聯編程代碼。 
自定義類做爲 XAML 元素的要求 

爲了可方便地用做路由事件,CLR 事件應實現顯式 add 和 remove 方法,這兩種方法分別添加和移除 
CLR 事件簽名的處理程序,並將這些處理程序轉發到 AddHandler 和 RemoveHandler 方法 
XAML 處理器是指可根據其規範(經過編譯或解釋)將 XAML 接受爲語言、而且能夠生成結果基礎類以供運行時對象模型使用(也
是根據 XAML 規範)的任意程序 
當用於提供屬性 (Attribute) 值時,將標記擴展與 XAML 處理器區分開來的語法就是左右大括號({ 和 })。而後,由緊跟在左大括
號後面的字符串標記來標識標記擴展的類型。 

StaticResource 經過替換已定義資源的值來爲 XAML 屬性提供值 


DynamicResource 經過將值推遲爲對資源的運行時引用來爲 XAML 屬性提供值 
Binding 按應用於元素的數據上下文來爲屬性提供數據綁定值。此標記擴展相對複雜,由於它會啓用大量內聯語法來指定數據綁定。
有關詳細信息, 
經過 TemplateBinding,控件模板能夠使用來自要利用該模板的類的對象模型定義屬性中的模板化屬性的值 
XAML 處理器中的屬性處理使用大括號做爲標記擴展的指示符。 
這些聲明之間的關係是:XAML 其實是語言標準,而 WPF 是將 XAML 做爲語言使用的一個實現。XAML 語言指定一些爲了兼容
而假定要實現的語言元素,每一個元素都應當能經過針對 XAML 命名空間執行的 XAML 處理器實現進行訪問。WPF 實現爲其本身的 
API 保留默認命名空間,爲 XAML 中須要的標記語法使用單獨的映射前綴。按照約定,該前綴是 x:,此 x: 約定後面是項目模板、
示例代碼和此 SDK 中語言功能的文檔。XAML 命名空間定義了許多經常使用功能,這些功能即便對於基本的 WPF 應用程序也是必需的。
例如,若要經過分部類將任何代碼隱藏加入 XAML 文件,您必須將該類命名爲相關 XAML 文件的根元素中的 x:Class 屬性。或者,
在 XAML 頁中定義的、您但願做爲鍵控資源訪問的任何元素應當對相關元素設置了 x:Key 屬性。 
映射到自定義類和程序集: clr-namespace: 在包含要做爲元素公開的公共類型的程序集中聲明的公共語言運行庫 (CLR) 命名空間。 

assembly= 是指包含部分或所有引用的 CLR 命名空間的程序集。該值一般只是程序集的名稱,而不是路徑。該程序集的路徑必須
在生成編譯的 XAML 的項目文件中以項目引用的形式創建。另外,爲了合併版本管理和強名稱簽名,該值也能夠是 
AssemblyName 定義的字符串。 
請注意,分隔 clr-namespace 標記和其值的字符是冒號 (:),而分隔 assembly 標記和其值的字符是等號 (=)。這兩個標記之間使
用的字符是分號。例如: 
xmlns:custom="clr-namespace:SDKSample;assembly=SDKSampleLibrary" 
惟一標識對象元素,以便於從代碼隱藏或通用代碼中訪問實例化的元素。x:Name 一旦應用於支持編程模型,即可被視爲與由構造函
數返回的用於保存對象引用的變量等效。 

x:Name 沒法應用於某些範圍。例如,ResourceDictionary 中的項不能有名稱,由於它們已有做爲惟一標識符的 x:Key 屬性。 

由於在 WPF 命名空間爲幾個重要基類(如 FrameworkElement/FrameworkContentElement)指定的 Name 依賴項屬性也具
有此用途。仍然有一些常見的 XAML 以及框架方案須要在不使用 Name 屬性的狀況下經過代碼訪問元素,這種狀況在某些動畫和演
示圖板支持類中最爲突出。例如,您應當在時間線以及在 XAML 中建立的轉換上指定 x:Name,前提是您計劃在代碼中引用它們。 

若是 Name 定義爲元素的一個屬性,則 Name 和 x:Name 可互換使用,但若是在同一元素上同時指定了二者,將會產生錯誤。 
x:Static 標記擴展:引用以符合公共語言規範 (CLS) 的方式定義的任何靜態的按值代碼實體。引用的屬性在加載 
XAML 頁的餘下部分以前計算,可用於以 XAML 提供 
<object> 
<object.property> 
<x:Static Member="prefix:typeName.staticMemberName" .../> 
</object.property> 

</object> 


http://i.msdn.microsoft.com/Platform/Controls/CollapsibleArea/resources/minus.gif
引用的代碼實體必須是下面的某一項: 

. 常量 
. 靜態屬性 
. 字段 
. 枚舉值 
. x:Subclass 用法主要針對不支持分部類聲明的語言。 


派生類中的事件處理程序必須是 internal override(在 Microsoft Visual Basic .NET 中必須是 Friend Overrides),
才能重寫編譯期間在中間類中建立的處理程序的存根。不然,派生類實現將隱藏中間類實現,而且中間類處理程序沒法被調
用。 

x:Type 本質上是 C# 中的 typeof() 運算符或 Microsoft Visual Basic .NET 中的 GetType 運算符的等效標記擴展。 
{} 轉義序列用來對屬性語法中用於標記擴展的 { 和 } 進行轉義。嚴格來講,轉義序列自己並非標記擴展, 
WPF 命名空間 XAML 擴展 

 本節內容 

綁定標記擴展 

ColorConvertedBitmap 標記擴展 

ComponentResourceKey 標記擴展 

DynamicResource 標記擴展 

RelativeSource MarkupExtension 

StaticResource 標記擴展 

TemplateBinding 標記擴展 

ThemeDictionary 標記擴展 

PropertyPath XAML 語法 

PresentationOptions:Freeze 屬性 
DynamicResource 標記擴展 

key 

所請求的資源的鍵。若是資源是在標記中建立的,則這個鍵最初是由 x:Key 屬性分配的;若是資源是在代碼中建立的,則
這個鍵是在調用 ResourceDictionary..::.Add 時做爲 key 參數提供的。 



DynamicResource 將在初始編譯過程當中建立一個臨時表達式,於是會將資源查找延遲到實際須要所請求的資源值來構造對象時才執
行。這多是在加載 XAML 頁以後。將基於鍵搜索在全部活動的資源字典中查找資源值(從當前頁範圍開始),而且資源值將取代編
譯期間的佔位符表達式。 


Windows Presentation Foundation (WPF) 中的大部分類都從四個類派生而來,這四個類在 SDK 文檔中經常被稱爲基元素類。
這些類包括 UIElement、FrameworkElement、ContentElement 和 FrameworkContentElement。DependencyObject 也
是一個相關類,由於它是 UIElement 和 ContentElement 的通用基類。 

UIElement 和 ContentElement 都是從 DependencyObject 派生而來,但途徑略有不一樣。此級別上的拆分涉及到 UIElement 或 
ContentElement 如何在用戶界面上使用,以及它們在應用程序起到什麼做用。UIElement 在其類層次結構中也有 Visual,該類爲 
Windows Presentation Foundation (WPF) 公開較低級別的圖形支持。Visual 經過定義獨立的矩形屏幕區域來提供呈現框架。實
際上,UIElement 適用於支持大型數據模型的元素,這些元素用於在能夠稱爲矩形屏幕區域的區域內進行呈現和佈局,在該區域內,
內容模型特地設置得更加開放,以容許不一樣的元素進行組合。ContentElement 不是從 Visual 派生的;它的模型由其餘對象(例如,
閱讀器或查看器,用來解釋元素並生成完整的 Visual 供 Windows Presentation Foundation (WPF) 使用)來使用 
ContentElement。某些 UIElement 類可用做內容宿主:它們爲一個或多個 ContentElement 類(如 DocumentViewer)提供
宿主和呈現。ContentElement 用做如下元素的基類:所具備的對象模型較小,而且多用於尋址可能宿主在 UIElement 中的文本、
信息或文檔內容。 
建立用於擴展 WPF 的自定義類的最實用方法是從某個 WPF 類中派生,這樣您能夠經過現有的類層次結構得到儘量多的所需功能。
本節列出了三個最重要的元素類附帶的功能,以幫助您決定要從哪一個類進行派生。 

若是您要實現控件(這的確是從 WPF 類派生的更常見的緣由之一),您可能須要從如下類中派生:實際控件、控件系列基類或至少
是 Control 基類 

DependencyObject 派生的類,則將繼承如下功能: 

. GetValue 和 SetValue 支持以及通常的屬性系統支持。 
. 使用依賴項屬性以及做爲依賴項屬性實現的附加屬性的能力。 


從 UIElement 派生的類,則除了可以繼承 DependencyObject 提供的功能外,還將繼承如下功能: 

對動畫屬性值的基本支持。有關更多信息,請參見動畫概述。 

對基本輸入事件和命令的支持。有關更多信息,請參見輸入概述和命令概述。 
能夠重寫以便爲佈局系統提供信息的虛方法。 

FrameworkElement 派生的類,則除了可以繼承 UIElement 提供的功能外,還將繼承如下功能: 

. 對樣式設置和演示圖板的支持。有關更多信息,請參見 Style 和演示圖板概述。 
. 對數據綁定的支持。有關更多信息,請參見數據綁定概述。 
. 對動態資源引用的支持。有關更多信息,請參見資源概述。 
. 對屬性值繼承以及元數據中有助於向框架服務報告屬性的相關狀況(如數據綁定、樣式或佈局的框架實現)的其餘標誌的支
持。有關更多信息,請參見框架屬性元數據。 
. 邏輯樹的概念。有關更多信息,請參見 WPF 中的樹。 


對佈局系統的實際 WPF 框架級實現的支持, 

ContentElement 派生的類,則除了可以繼承 DependencyObject 提供的功能外,還將繼承如下功能: 

. 對動畫的支持。有關更多信息,請參見動畫概述。 
. 對基本輸入事件和命令的支持。有關更多信息 



FrameworkContentElement 派生的類,則除了可以繼承 ContentElement 提供的功能外,還將得到如下功能: 

. 對樣式設置和演示圖板的支持。有關更多信息,請參見 Style 和動畫概述。 
. 對數據綁定的支持。有關更多信息,請參見數據綁定概述。 
. 對動態資源引用的支持。有關更多信息,請參見資源概述。 
. 對屬性值繼承以及元數據中有助於向框架服務報告屬性狀況(如數據綁定、樣式或佈局的框架實現)的其餘標誌的支持。有
關更多信息,請參見框架屬性元數據。 
. 您不會繼承對佈局系統修改(如 ArrangeOverride)的訪問權限。佈局系統實現只在 FrameworkElement 上提供。但
是,您會繼承 OnPropertyChanged 重寫(能夠檢測影響佈局的屬性更改並將這些更改報告給任何內容宿主)。 


DispatcherObject 

DispatcherObject 爲 WPF 線程模型提供支持,並容許爲 WPF 應用程序建立的全部對象與 Dispatcher 相關聯。即便您不從 
UIElement, DependencyObject 或 Visual 派生,也應考慮從 DispatcherObject 派生,以得到此線程模型支持 

Visual 

Visual 實現二維對象在近似矩形的區域中一般須要具備可視化表示的概念。Visual 的實際呈現發生在其餘類中(不是獨立的),但
是 Visual 類提供了一個由各類級別的呈現處理使用的已知類型。 

Freezable 對象的示例包括畫筆、鋼筆、變換、幾何圖形和動畫。Freezable 提供了一個 Changed 事件以將對對象所作
的任何修改通知給觀察程序。凍結 Freezable 能夠改進其性能,由於它再也不須要因更改通知而消耗資源。凍結的 
Freezable 也能夠在線程之間共享,而解凍的 Freezable 則不能。 

並非每一個 Freezable 對象均可以凍結。若要避免引起 InvalidOperationException,請在嘗試凍結 Freezable 對象以前,查看
其 CanFreeze 屬性的值,以肯定它是否能夠被凍結。 
當再也不須要修改某個 Freezable 時,凍結它能夠改進性能。若是您在該示例中凍結畫筆,則圖形系統將再也不須要監視它的
更改狀況。圖形系統還能夠進行其餘優化,由於它知道畫筆不會更改。 
SolidColorBrush a = new SolidColorBrush(Colors.Yellow ); 
if (a.CanFreeze) 
{ 
a.Freeze(); 
} 
若是下列任一狀況屬實,則沒法凍結 Freezable: 

. 它有動畫或數據綁定的屬性。 
. 它有由動態資源設置的屬性 (有關動態資源的更多信息,請參見資源概述)。 
. 它包含沒法凍結的 Freezable 子對象。 


爲了不引起此異常,能夠使用 IsFrozen 方法來肯定 Freezable 是否處於凍結狀態。 
if (myBrush.IsFrozen) // Evaluates to true. 


在前面的代碼示例中,使用 Clone 方法對一個凍結的對象建立了可修改副本。下一節將更詳細地討論克隆操做。 

從標記凍結. 


若要凍結在標記中聲明的 Freezable 對象,請使用 PresentationOptions:Freeze 屬性。在下面的示例中,將一個 
SolidColorBrush 聲明爲頁資源,並凍結它。隨後將它用於設置按鈕的背景。 
<Page.Resources> 
<!-- This resource is frozen. --> 
<SolidColorBrush 
x:Key="MyBrush" 
PresentationOptions:Freeze="True" 
Color="Red" /> 
</Page.Resources> 
若要使用 Freeze 屬性,必須映射到表示選項命名空間:
http://schemas.microsoft.com/winfx/2006/xaml/presentation/options。PresentationOptions 是用於映射該命名空間的
推薦前綴: 

 xmlns:PresentationOptions=http://schemas.microsoft.com/winfx/2006/xaml/presentation/options 

因爲並不是全部 XAML 讀取器都能識別該屬性,所以建議使用 mc:Ignorable 屬性 將 Presentation:Freeze 屬性標記爲可忽略: 

 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
mc:Ignorable="PresentationOptions" 

Freezable 一旦凍結,便不能再修改或解凍;不過,您能夠使用 Clone 或 CloneCurrentValue 方法建立一個解凍的複本 

從 Freezable 派生的類能夠獲取如下功能。 

. 特殊的狀態:只讀(凍結)狀態和可寫狀態。 
. 線程安全:凍結的 Freezable 能夠在線程之間共享。 
. 詳細的更改通知:與其餘 DependencyObject 不一樣,Freezable 對象會在子屬性值更改時提供更改通知。 
. 輕鬆克隆:Freezable 類已經實現了多種生成深層複本的方法。 


每一個 Freezable 子類都必須重寫 CreateInstanceCore 方法。若是您的類對於其全部數據都使用依賴項屬性,則您的工做已完成。 

FrameworkElement 類公開一些用於精肯定位子元素的屬性。本主題論述其中四個最重要的屬性:HorizontalAlignment、
Margin、Padding 和 VerticalAlignment。務必要了解這些屬性的做用,由於這些屬性是控制元素在 Windows Presentation 
Foundation (WPF) 應用程序中的位置的基礎。 

在一個元素上顯式設置的 Height 和 Width 屬性優先於 Stretch 屬性值。若是嘗試設置 Height, Width 以及 Stretch 的 
HorizontalAlignment 值,將會致使 Stretch 請求被忽略。 

Padding 在大多數方面相似於 Margin。Padding 屬性只會在少數類上公開,主要是爲了方便起見而公開:Block、Border、
Control 和 TextBlock 是公開 Padding 屬性的類的示例。Padding 屬性可將子元素的有效大小增大指定的 Thickness 值。 



元素樹和序列化:WPF 編程元素彼此之間一般以某種形式的樹關係存在。例如,在 XAML 中建立的應用程序 UI 能夠被概念
化爲一個元素樹。能夠進一步將元素樹分爲兩個離散但有時會並行的樹:邏輯樹和可視化樹。WPF 中的序列化涉及到保存這兩個樹
和應用程序的狀態並將狀態寫入文件(一般以 XAML 形式)。 
WPF 中主要的樹結構是元素樹。若是使用 XAML 建立應用程序頁,則將基於標記中元素的嵌套關係建立樹結構。若是使用代碼建立
應用程序,則將基於爲屬性(實現給定元素的內容模型)指定屬性值的方式建立樹結構。在 Windows Presentation Foundation 
(WPF) 中,處理和使用概念說明元素樹的方法實際上有兩種:即邏輯樹和可視化樹。邏輯樹與可視化樹之間的區別並不始終很重要,
但在某些 WPF 子系統中它們可能會偶爾致使問題,並影響您對標記或代碼的選擇。 
邏輯樹 

在 WPF 中,可以使用屬性向元素中添加內容。例如,使用 ListBox 控件的 Items 屬性可向該控件中添加項。經過此方式,可將項放
置到 ListBox 控件的 ItemCollection 中。若要向 DockPanel 中添加元素,可以使用其 Children 屬性。此時,將向 DockPanel 的 
UIElementCollection 中添加元素 

邏輯樹用途 

邏輯樹的存在用途是使內容模型能夠容易地循環訪問其可能包含的子元素,從而能夠對內容模型進行擴展。此外,邏輯樹還爲某些通
知提供了框架,例如當加載邏輯樹中的全部元素時。 

此外,在 Resources 集合的邏輯樹中首先向上查找初始請求元素,而後再查找父元素,這樣能夠解析資源引用。當同時存在邏輯樹
和可視化樹時,將使用邏輯樹進行資源查找 

屬性值繼承 

屬性值繼承經過混合樹操做。包含用於啓用屬性繼承的 Inherits 屬性的實際元數據是 WPF 框架級
別 FrameworkPropertyMetadata 類。所以,保留原始值的父元素以及繼承該父元素的子元素都必須是 FrameworkElement 或 
FrameworkContentElement,而且它們都必須屬於某個邏輯樹的一部分。可是,容許父元素的邏輯樹與子元素的邏輯樹相互獨立,
這樣能夠經過一個不在邏輯樹中的中介可視元素使屬性值繼承永續進行。若要使屬性值繼承在這樣的界限中以一致的方式工做,必須
將繼承屬性註冊爲附加屬性。經過幫助器類實用工具方法沒法徹底預測屬性繼承確切使用的樹,即便在運行時也同樣。有關更多信息,
請參見屬性值繼承。 
可視化樹 

WPF 中除了邏輯樹的概念,還存在可視化樹的概念。可視化樹描述由 Visual 基類表示的可視化對象的結構。爲控件編寫模板時,將
定義或從新定義適用於該控件的可視化樹。對於出於性能和優化緣由想要對繪圖進行較低級別控制的開發人員來講,他們也會對可視
化樹感興趣。做爲常規 WPF 應用程序編程一部分的可視化樹的一個公開狀況是,路由事件的事件路由大多數狀況下遍歷可視化樹,
而不是邏輯樹。這種微妙的路由事件行爲可能不會很明顯,除非您是控件做者。在可視化樹中路由使得在可視化級別實現組合的控件
可以處理事件或建立事件 setter。 
樹、內容元素和內容宿主 

內容元素(從 ContentElement 派生的類)不是可視化樹的一部分;內容元素不從 Visual 繼承而且沒有可視化表示形式。若要徹底
在 UI 中顯示,則必須在既是 Visual,也是邏輯樹元素(一般是 FrameworkElement)的內容宿主中承載 ContentElement。您
能夠使用概念說明,內容宿主有點相似於內容的「瀏覽器」,它選擇要在該宿主控制的屏幕區域中顯示內容的方式。承載內容時,能夠
使內容成爲一般與可視化樹關聯的某些樹進程的參與者。一般,FrameworkElement 宿主類包括實現代碼,該代碼用於經過內容邏
輯樹的子節點將任何已承載的 ContentElement 添加到事件路由,即便承載內容不是真實可視化樹的一部分時也將如此。這樣作是
必要的,以便 ContentElement 能夠爲路由到除其自身以外的任何元素的路由事件提供來源。 

LogicalTreeHelper 類爲邏輯樹遍歷提供 GetChildren、GetParent 和 FindLogicalNode 方法。在大多數狀況下 


資源和樹 

資源查找基本上遍歷邏輯樹。不在邏輯樹中的對象能夠引用資源,但查找將從該對象鏈接到邏輯樹的位置開始。僅邏輯樹節點能夠有
包含 ResourceDictionary 的 Resources 屬性,所以這意味着,遍歷可視化樹來查找資源沒有好處。 
可是,資源查找也能夠超出直接邏輯樹。 
序列化的結果是應用程序的結構化邏輯樹的有效表示形式,但並不必定是生成該樹的原始 XAML。 

Save 的序列化輸出是獨立的;序列化的全部內容都包含在單個 XAML 頁面中,該頁面具備單個根元素並且沒有除 URI 之外的外部
引用。例如,若是您的頁面從應用程序資源引用了資源,則這些資源看上去如同正在進行序列化的頁面的一個組件 
由於序列化是獨立的並侷限於邏輯樹,因此沒有工具可用於存儲事件處理程序 
應注意 Windows Presentation Foundation (WPF) 類的對象初始化的幾個方面特地不在調用類構造函數時所執行的代碼的某一部
分實現。這種狀況對於控件類尤其突出,該控件的大部分可視化表示都不是由構造函數定義的,而是由控件的模板定義的。模板可能
來自於各類源,可是最多見的狀況是來自於主題樣式。模板其實是後期綁定的;只有在相應的控件已準備好應用佈局時,纔會將所
需的模板附加到該控件上。 

 若是針對其設置屬性的元素是 FrameworkElement 或 FrameworkContentElement 派生類,則您能夠調用 BeginInit 和 
EndInit 的類版本,而不是強制轉換爲 ISupportInitialize。 
WPF) 提供了一組服務,這些服務可用於擴展公共語言運行庫 (CLR) 屬性的功能。這些服務一般統稱爲 WPF 屬性系統。由 WPF 屬
性系統支持的屬性稱爲依賴項屬性。本概述介紹 WPF 屬性系統以及依賴項屬性的功能 
依賴項屬性的用途在於提供一種方法來基於其餘輸入的值計算屬性值。這些其餘輸入能夠包括系統屬性(如主題和用戶首選項)、實
時屬性肯定機制(如數據綁定和動畫/演示圖板)、重用模板(如資源和樣式)或者經過與元素樹中其餘元素的父子關係來公開的值。
另外,能夠經過實現依賴項屬性來提供獨立驗證、默認值、監視其餘屬性的更改的回調以及能夠基於可能的運行時信息來強制指定屬
性值的系統。派生類還能夠經過重寫依賴項屬性元數據(而不是重寫現有屬性的實際實現或者建立新屬性)來更改現有屬性的某些具
體特徵。 

定義 WPF 屬性系統的另外一個重要類型是 DependencyObject。DependencyObject 定義能夠註冊和擁有依賴項屬性的基類。 

. 依賴項屬性:一個由 DependencyProperty 支持的屬性。 
. 依賴項屬性標識符:一個 DependencyProperty 實例,在註冊依賴項屬性時做爲返回值得到,以後將存儲爲一個類成員。
在與 WPF 屬性系統交互的許多 API 中,此標識符用做一個參數。 


屬性以及支持它的 DependencyProperty 字段的命名約定很是重要。字段老是與屬性同名,但其後面追加了 Property 後綴。 
<Button.Background> 
<ImageBrush ImageSource="wavy.jpg"/> 
</Button.Background> 
</Button> 
在代碼中設置依賴項屬性值一般只是調用由 CLR「包裝」公開的 set 實現。獲取屬性值實質上也是在調用 get「包裝」實現: 

. 依賴項屬性提供用來擴展屬性功能的功能,這與字段支持的屬性相反。每一個這樣的功能一般都表示或支持整套 WPF 功能中
的特定功能 
資源 數據綁定 樣式 動畫 元數據重寫 屬性值繼承 WPF 設計器集成 
. <DockPanel.Resources> 
. <SolidColorBrush x:Key="MyBrush" Color="Gold"/> 



. </DockPanel.Resources> 


在定義了某個資源以後,能夠引用該資源並使用它來提供屬性值: 
<Button Background="{DynamicResource MyBrush}" Content="I am gold" /> 
//StaticResource 
<Style x:Key="GreenButtonStyle"> 
<Setter Property="Control.Background" Value="Green"/> 
</Style> 
//
<Button Style="{StaticResource GreenButtonStyle}">I am green!</Button> 
資源被視爲本地值,這意味着,若是您設置另外一個本地值,該資源引用將被消除 
綁定被視爲本地值,這意味着,若是您設置另外一個本地值,該綁定將被消除 
<Button>I am animated 
<Button.Background> 
<SolidColorBrush x:Name="AnimBrush"/> 
</Button.Background> 
<Button.Triggers> 
<EventTrigger RoutedEvent="Button.Loaded"> 
<BeginStoryboard> 
<Storyboard> 
<ColorAnimation 
Storyboard.TargetName="AnimBrush" 
Storyboard.TargetProperty="(SolidColorBrush.Color)" 
From="Red" To="Green" Duration="0:0:5" 
AutoReverse="True" RepeatBehavior="Forever" /> 
</Storyboard> 
</BeginStoryboard> 
</EventTrigger> 
</Button.Triggers> 
</Button> 

屬性值繼承 

元素能夠從其在樹中的父級繼承依賴項屬性的值。 

說明: 

屬性值繼承行爲並未針對全部的依賴項屬性在全局啓用,由於繼承的計算時間確實會對性能產生必定的影響。屬性值繼承一般只
有在特定方案指出適合使用屬性值繼承時纔對屬性啓用 



 
做用一類Button的property: 


<StackPanel> 
<StackPanel.Resources> 
<Style TargetType="{x:Type Button}"> 
<Setter Property="Background" Value="Red"/> 
</Style> 
</StackPanel.Resources> 
<Button Background="Green">I am NOT red!</Button> 
<Button>I am styled red</Button> 
</StackPanel> 

爲何存在依賴項屬性優先級? 

一般,您不會但願老是應用樣式,並且不但願樣式遮蓋單個元素的哪怕一個本地設置值(不然,一般將很難使用樣式或元素)。所以,
來自樣式的值的操做優先級將低於本地設置的值 
附加屬性是一種類型的屬性,它支持 XAML 中的專用語法。附加屬性一般與公共語言運行庫 (CLR) 屬性不具備 1:1 對應關係,並且
不必定是依賴項屬性。附加屬性的典型用途是使子元素能夠向其父元素報告屬性值,即便父元素和子元素的類成員列表中均沒有該屬
性也是如此。附加屬性旨在用做可在任何對象上設置的一類全局屬性。在 Windows Presentation Foundation (WPF) 中,附加屬
性一般定義爲沒有常規屬性「包裝」的一種特殊形式的依賴項屬性。 
。附加屬性是一個 XAML 概念, 而依賴項屬性則是一個 WPF 概念 

附加屬性的一個用途是容許不一樣的子元素爲實際在父元素中定義的屬性指定惟一值。此方案的一個具體應用是讓子元素通知父元素它
們將如何在用戶界面 (UI) 中呈現。一個示例是 DockPanel..::.Dock 屬性。DockPanel..::.Dock 屬性建立爲附加屬性,由於它將
在 DockPanel 中包含的元素上設置,而不是在 DockPanel 自己設置。DockPanel 類定義名爲 DockProperty 的靜態 
DependencyProperty 字段,而後將 GetDock 和 SetDock 方法做爲該附加屬性的公共訪問器提供。 
定義附加屬性的類型一般採用如下模型之一: 

. 設計定義附加屬性的類型,以便它能夠是將爲附加屬性設置值的元素的父元素。以後,該類型將在內部邏輯中循環訪問其子
元素,獲取值,並以某種方式做用於這些值。 
. 定義附加屬性的類型將用做各類可能的父元素和內容模型的子元素。 
. 定義附加屬性的類型表示一個服務。其餘類型爲該附加屬性設置值。以後,當在服務的上下文中計算設置該屬性的元素時,
將經過服務類的內部邏輯獲取附加屬性的值。 


若是您但願對屬性啓用屬性值繼承,則應使用附加屬性,而不是非附加的依賴項屬性。有關詳細信息 
DockPanel myDockPanel = new DockPanel(); 
CheckBox myCheckBox = new CheckBox(); 
myCheckBox.Content = "Hello"; 
myDockPanel.Children.Add(myCheckBox); 
DockPanel.SetDock(myCheckBox, Dock.Top); 

什麼時候建立附加屬性 

當確實須要有一個可用於定義類以外的其餘類的屬性設置機制時,您可能會建立附加屬性。這種狀況的最多見方案是佈局。現有佈局
屬性的示例有 DockPanel..::.Dock、Panel..::.ZIndex 和 Canvas..::.Top。這裏啓用的方案是做爲佈局控制元素的子元素存在的
元素可以分別向其佈局父元素表達佈局要求,其中每一個元素都設置一個被父級定義爲附加屬性的屬性值 
前面已提到,若是您但願使用屬性值繼承,應註冊爲一個附加屬性。 

如何建立附加屬性 

若是您的類將附加屬性嚴格定義爲用於其餘類型,那麼該類沒必要從 DependencyObject 派生。可是,若是您遵循使附加屬性同時也
是一個依賴項屬性的總體 WPF 模型,則須要從 DependencyObject 派生。 


http://cajon.cnblogs.com/Images/OutliningIndicators/None.gif
public static readonly DependencyProperty IsBubbleSourceProperty = DependencyProperty.RegisterAttached( 
"IsBubbleSource", 
typeof(Boolean), 
typeof(AquariumObject), 
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender) 
); 
public static void SetIsBubbleSource(UIElement element, Boolean value) 
{ 
element.SetValue(IsBubbleSourceProperty, value); 
} 
public static Boolean GetIsBubbleSource(UIElement element) 
{ 
return (Boolean)element.GetValue(IsBubbleSourceProperty); 
} 


DependencyProperty和DependencyObject配合,提供了WPF中基本的數據存儲、訪問和通知的機制。也正是由於這兩個東西
的存在,使得XAML,Binding,Animation都成爲可能。 
public static readonly DependencyProperty CounterProperty = 
DependencyProperty.Register( 
"PropertyName", // 屬性名 
typeof(int), // 屬性的類型 
typeof(MyButtonSimple), // 屬性所在的類型 
new PropertyMetadata(0) // 屬性的默認值 


 ) 
不會強制屬性的默認值。若是屬性值仍然採用其初始默認值,或經過使用 ClearValue 清除其餘值,則可能存在等於默認值的屬性
值。 
什麼是依賴項屬性? 
您能夠啓用本應爲公共語言運行庫 (CLR) 屬性的屬性來支持樣式設置、數據綁定、繼承、動畫和默認值, 

依賴項屬性只能由 DependencyObject 類型使用, WPF 中的全部依賴項屬性(大多數附加屬性除外)也是 CLR 屬性 
在類體中定義依賴項屬性是典型的實現,可是也能夠在類靜態構造函數中定義依賴項屬性。若是您須要多行代碼來初始化依賴項屬性,
則此方法可能會頗有意義。 

若是要建立在 FrameworkElement 的派生類上存在的依賴項屬性,則能夠使用更專用的元數據類 FrameworkPropertyMetadata,
而不是 PropertyMetadata 基類。 
WPF 屬性系統提供了一個強大的方法,使得依賴項屬性的值由多種因素決定,從而實現了諸如實時屬性驗證、後期綁定以及向相關
屬性發出有關其餘屬性值發生更改的通知等功能 
本地屬性集在設置時具備最高優先級,動畫值和強制轉換除外。若是您在本地設置某個值,必定會但願該值能優先獲得應用,甚至希
望其優先級高於任何樣式或控件模板 
<Button > 
<Button.Style> 
<Style TargetType="{x:Type Button}"> 
<Setter Property="Background" Value="Green"/> 
<Style.Triggers> 
<Trigger Property="IsMouseOver" Value="True"> 
<Setter Property="Background" Value="Blue" /> 


 </Trigger> 
</Style.Triggers> 
</Style> 
</Button.Style> 
Click 
</Button> 
依賴項屬性設置優先級列表 

1. 屬性系統強制轉換。 有關強制轉換的詳細信息, 
2. 活動動畫或具備 Hold 行爲的動畫。 爲了得到任何實用效果,屬性的動畫必須優先於基(未動畫)值,即便該值是在本地設置
的狀況下也將如此。有關詳細信息, 
3. 本地值。 本地值能夠經過「包裝」屬性 (Property) 的便利性進行設置,這也至關於在 XAML 中設置屬性 (Attribute) 或屬性 
(Property) 元素,或者使用特定實例的屬性調用 SetValue API。若是您使用綁定或資源來設置本地值,則每一個值都按照直接設
置值的優先級順序來應用。 
4. TemplatedParent 模板屬性。 若是元素是做爲模板(ControlTemplate 或 DataTemplate)的一部分建立的,則具備 
TemplatedParent。有關什麼時候應用此原則的詳細信息,請參見本主題後面的 TemplatedParent。在模板中,按如下優先級順序
應用: 
a. 來自 TemplatedParent 模板的觸發器。 
b. TemplatedParent 模板中的屬性 (Property) 集。(一般經過 XAML 屬性 (Attribute) 進行設置。) 


5. 隱式樣式。 僅應用於 Style 屬性。Style 屬性是由任何樣式資源經過與其類型匹配的鍵來填充的。該樣式資源必須存在於頁面或
應用程序中;查找隱式樣式資源不會進入到主題中。 
6. 樣式觸發器。 來自頁面或應用程序上的樣式中的觸發器。(這些樣式能夠是顯式或隱式樣式,但不是來自優先級較低的默認樣
式。) 
7. 模板觸發器。 來自樣式中的模板或者直接應用的模板的任何觸發器。 
8. 樣式 Setter。 來自頁面或應用程序的樣式中的 Setter 的值。 
9. 默認(主題)樣式。 有關什麼時候應用此樣式以及主題樣式如何與主題樣式中的模板相關的詳細信息, 
a. 主題樣式中的活動觸發器。 
b. 主題樣式中的 Setter。 


10. 繼承。有幾個依賴項屬性從父元素向子元素繼承值,所以不須要在應用程序中的每一個元素上專門設置這些屬性。 
11. 來自依賴項屬性元數據的默認值。 任何給定的依賴項屬性都具備一個默認值,它由該特定屬性的屬性系統註冊來肯定。並且,繼
承依賴項屬性的派生類具備按照類型重寫該元數據(包括默認值)的選項。有關更多信息, 。由於繼承是在默認值以前檢查的,
因此對於繼承的屬性,父元素的默認值優先於子元素。 所以,若是任何地方都沒有設置可繼承的屬性,將使用在根元素或父元素
中指定的默認值,而不是子元素的默認值。 


WPF 附帶的每一個控件都有一個默認樣式,在默認樣式中,對於控件最重要的信息就是其控件模板, 控件經常在主題中將觸發器行爲定
義爲其默認樣式的一部分, 爲控件設置本地屬性可能會阻止觸發器從視覺或行爲上響應用戶驅動的事件。 

ClearValue 方法爲從在元素上設置的依賴項屬性中清除任何本地應用的值提供了一個有利的途徑 

屬性值繼承 


屬性值繼承是包容繼承 
父元素還能夠經過屬性值繼承來得到其值,所以系統有可能一直遞歸到頁面根元素。屬性值繼承不是屬性系統的默認行爲;屬性必須
用特定的元數據設置來創建,以便使該屬性可以對子元素啓動屬性值繼承。屬性值繼承則是關於屬性值如何基於元素樹中的父子關係
從一個元素繼承到另外一個元素。經過更改自定義屬性的元數據,還能夠使您本身的自定義屬性可繼承. 
附加屬性的典型方案是針對子元素設置屬性值,若是所討論的屬性是附加屬性 
屬性繼承經過遍歷元素樹來工做。此樹一般與邏輯樹平行。跨樹邊界繼承屬性值(屬性值繼承就能夠彌合邏輯樹中的這種間隙,並且仍
能夠傳遞所繼承的值) 
爲了糾正此問題,在類構造函數調用中,必須將集合依賴項屬性值重設爲惟一的實例 
當您定義本身的屬性並須要它們支持 Windows Presentation Foundation (WPF) 功能的諸多方面(包括樣式、數據綁定、繼承、
動畫和默認值)時,應將其實現爲依賴項屬性。 
設計依賴項屬性: 
public class MyStateControl : ButtonBase 
{ 
public MyStateControl() : base() { } 
public Boolean State 
{ 
get { return (Boolean)this.GetValue(StateProperty); } 
set { this.SetValue(StateProperty, value); } 
} 
public static readonly DependencyProperty StateProperty = DependencyProperty.Register( 
"State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(false)); 
} 
設計附加屬性: 
public static readonly DependencyProperty IsBubbleSourceProperty = DependencyProperty.RegisterAttached( 
"IsBubbleSource", 
typeof(Boolean), 
typeof(AquariumObject), 
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender) 
); 
public static void SetIsBubbleSource(UIElement element, Boolean value) 
{ 
element.SetValue(IsBubbleSourceProperty, value); 
} 
public static Boolean GetIsBubbleSource(UIElement element) 
{ 
return (Boolean)element.GetValue(IsBubbleSourceProperty); 
} 
能夠從功能或實現的角度來考慮路由事件。此處對這兩種定義均進行了說明,由於用戶當中有的認爲前者更有用,而有的則認爲後者
更有用。 
路由事件:功能定義:路由事件是一種能夠針對元素樹中的多個偵聽器(而不是僅針對引起該事件的對象)調用處理程序的事件。 

實現定義:路由事件是一個 CLR 事件,能夠由 RoutedEvent 類的實例提供支持並由 Windows Presentation Foundation (WPF) 事件系統
來處理。 

路由事件的頂級方案 

下面簡要概述了需運用路由事件的方案,以及爲何典型的 CLR 事件不適合這些方案: 


控件的撰寫和封裝:WPF 中的各個控件都有一個豐富的內容模型。例如,能夠將圖像放在 Button 的內部,這會有效地擴展按鈕的
可視化樹。可是,所添加的圖像不得中斷命中測試行爲(該行爲會使按鈕響應對圖像內容的 Click),即便用戶所單擊的像素在技術
上屬於該圖像也是如此 
單一處理程序附加點:在 Windows 窗體中,必須屢次附加同一個處理程序,才能處理多是從多個元素引起的事件。路由事件使您
能夠只附加該處理程序一次(像上例中那樣),並在必要時使用處理程序邏輯來肯定該事件源自何處。例如,這能夠是前面顯示的 
XAML 的處理程序: 
類處理:路由事件容許使用由類定義的靜態處理程序。這個類處理程序可以搶在任何附加的實例處理程序以前來處理事件。 

引用事件,而不反射:某些代碼和標記技術須要能標識特定事件的方法。路由事件建立 RoutedEvent 字段做爲標識符,以此提供不
須要靜態反射或運行時反射的可靠的事件標識技術。 
路由事件使用如下三個路由策略之一: 

. 冒泡:針對事件源調用事件處理程序。路由事件隨後會路由到後續的父元素,直到到達元素樹的根。大多數路由事件都使
用冒泡路由策略。冒泡路由事件一般用來報告來自不一樣控件或其餘 UI 元素的輸入或狀態變化。 
. 直接:只有源元素自己纔有機會調用處理程序以進行響應。這與 Windows 窗體用於事件的「路由」類似。可是,與標準 
CLR 事件不一樣的是,直接路由事件支持類處理(類處理將在下一節中介紹)並且能夠由 EventSetter 和 EventTrigger 
使用。 
. 隧道:最初將在元素樹的根處調用事件處理程序。隨後,路由事件將朝着路由事件的源節點元素(即引起路由事件的元素)
方向,沿路由線路傳播到後續的子元素。在合成控件的過程當中一般會使用或處理隧道路由事件,這樣,就能夠有意地禁止顯
示覆合部件中的事件,或者將其替換爲特定於整個控件的事件。在 WPF 中提供的輸入事件一般是以隧道/冒泡對實現的。
隧道事件有時又稱做 Preview 事件,這是由隧道/冒泡對所使用的命名約定決定的。 


若是您使用如下任一建議方案,路由事件的功能將獲得充分發揮:在公用根處定義公用處理程序、合成本身的控件或者定義您本身的
自定義控件類。路由事件還能夠用來經過元素樹進行通訊,由於事件的事件數據會永存到路由中的每一個元素中。一個元素能夠更改事
件數據中的某項內容,該更改將對於路由中的下一個元素可用。 

. 某些 WPF 樣式和模板功能(如 EventSetter 和 EventTrigger)要求所引用的事件是路由事件。前面提到的事件標識符方
案就是這樣的。 
. 路由事件支持類處理機制,類能夠憑藉該機制來指定靜態方法,這些靜態方法可以在任何已註冊的實例程序訪問路由事件以前,
處理這些路由事件。這在控件設計中很是有用,由於您的類能夠強制執行事件驅動的類行爲,以防它們在處理實例上的事件時
被意外禁止。 
. <Button Click="b1SetColor">button</Button> 
. 
. b1SetColor 是所實現的處理程序的名稱,該處理程序中包含用來處理 Click 事件的代碼。b1SetColor 必須具備與 
RoutedEventHandler 委託相同的簽名,該委託是 Click 事件的事件處理程序委託。全部路由事件處理程序委託的第一個參
數都指定要向其中添加事件處理程序的元素,第二個參數指定事件的數據。 
. entHandler 是基本的路由事件處理程序委託 
. 
. void MakeButton() 


 { 

. Button b2 = new Button(); 


 b2.AddHandler(Button.ClickEvent, new RoutedEventHandler(Onb2Click)); 
} 


 void MakeButton2() 
{ 
Button b2 = new Button(); 
b2.Click += new RoutedEventHandler(Onb2Click2); 
} 

「已處理」概念 

全部的路由事件都共享一個公用的事件數據基類 RoutedEventArgs。RoutedEventArgs 定義了一個採用布爾值的 Handled 屬性。
Handled 屬性的目的在於,容許路由中的任何事件處理程序經過將 Handled 的值設置爲 true 來將路由事件標記爲「已處理」。處理
程序在路由路徑上的某個元素處對共享事件數據進行處理以後,這些數據將再次報告給路由路徑上的每一個偵聽器。 

Handled 的值影響路由事件在沿路由線路向遠處傳播時的報告或處理方式。在路由事件的事件數據中,若是 Handled 爲 true,則
一般再也不爲該特定事件實例調用負責在其餘元素上偵聽該路由事件的處理程序。這條規則對如下兩類處理程序均適用:在 XAML 中附
加的處理程序;由語言特定的事件處理程序附加語法(如 += 或 Handles)添加的處理程序。對於最多見的處理程序方案,若是將 
Handled 設置爲 true,以此將事件標記爲「已處理」,則將「中止」隧道路由或冒泡路由,同時,類處理程序在某個路由點到處理的所
有事件的路由也將「中止」。 

可是,偵聽器仍能夠憑藉「handledEventsToo」機制來運行處理程序,以便在事件數據中的 Handled 爲 true 時響應路由事件。換
言之,將事件數據標記爲「已處理」並不會真的中止事件路由。您只能在代碼或 EventSetter 中使用 handledEventsToo 機制: 

在代碼中,不使用適用於通常 CLR 事件的特定於語言的事件語法,而是經過調用 WPF 方法 AddHandler(RoutedEvent, 
Delegate, Boolean) 來添加處理程序。使用此方法時,請將 handledEventsToo 的值指定爲 true。 

在 EventSetter 中,請將 HandledEventsToo 屬性設置爲 true。 
WPF 中的附加事件 
XAML 語言還定義了一個名爲「附加事件」的特殊類型的事件。使用附加事件,能夠將特定事件的處理程序添加到任意元素中。
正在處理該事件的元素沒必要定義或繼承附加事件,可能引起這個特定事件的對象和用來處理實例的目標也都沒必要將該事件定
義爲類成員或將其做爲類成員來「擁有」。 

WPF 輸入系統普遍地使用附加事件。可是,幾乎全部的附加事件都是經過基本元素轉發的。輸入事件隨後會顯示爲等效的、
做爲基本元素類成員的非附加路由事件。例如,經過針對該 UIElement 使用 MouseDown(而不是在 XAML 或代碼中處
理附加事件語法),能夠針對任何給定的 UIElement 更方便地處理基礎附加事件 Mouse..::.MouseDown。 

這兩個事件會共享同一個事件數據實例,由於用來引起冒泡事件的實現類中的 RaiseEvent 方法調用會偵聽隧道事件中的事
件數據並在新引起的事件中重用它。具備隧道事件處理程序的偵聽器首先得到將路由事件標記爲「已處理」的機會(先是類處
理程序,後是實例處理程序)。若是隧道路由中的某個元素將路由事件標記爲「已處理」,則會針對冒泡事件發送已經處理的
事件數據,並且將不調用爲等效的冒泡輸入事件附加的典型處理程序。已處理的冒泡事件看起來好像還沒有引起過。此處理行
爲對於控件合成很是有用,由於此時您可能但願全部基於命中測試的輸入事件或者全部基於焦點的輸入事件都由最終的控件
(而不是它的複合部件)報告。做爲可支持控件類的代碼的一部分,最後一個控件元素靠近合成鏈中的根,所以將有機會首
先對隧道事件進行類處理,或許還有機會將該路由事件「替換」爲更特定於控件的事件。 


事件路由示意圖 


1. 針對根元素處理 PreviewMouseDown(隧道)。 
2. 針對中間元素 1 處理 PreviewMouseDown(隧道)。 
3. 針對源元素 2 處理 PreviewMouseDown(隧道)。 
4. 針對源元素 2 處理 MouseDown(冒泡)。 
5. 針對中間元素 1 處理 MouseDown(冒泡)。 
6. 針對根元素處理 MouseDown(冒泡)。 


路由事件處理程序委託提供對如下兩個對象的引用:引起該事件的對象以及在其中調用處理程序的對象。在其中調用處理程序的對象
是由 sender 參數報告的對象。首先在其中引起事件的對象是由事件數據中的 Source 屬性報告的。路由事件仍能夠由同一個對象引
發和處理,在這種狀況下,sender 和 Source 是相同的(事件處理示例列表中的步驟 34 就是這樣的狀況)。 
一般,隧道事件和冒泡事件之間的共享事件數據模型以及先引起隧道事件後引起冒泡事件等概念並不是對於全部的路由事件都適用。該
行爲的實現取決於 WPF 輸入設備選擇引起和鏈接輸入事件對的具體方式。實現本身的輸入事件是一個高級方案,可是您也能夠選擇
針對本身的輸入事件遵循該模型。 
<StackPanel > 
<StackPanel.Resources > 
<Style TargetType="{x:Type Button}" > 
<EventSetter Event="Click" Handler="b1SetColor" /> 
</Style> 
</StackPanel.Resources> 
<Button >click me</Button> 
<Button Click="HandleThis" >buton make</Button> 
</StackPanel> 

另外一個將 WPF 的路由事件和動畫功能結合在一塊兒的專用語法是 EventTrigger。與 EventSetter 同樣,只有路由事件能夠用於 
EventTrigger。一般將 EventTrigger 聲明爲樣式的一部分,可是還能夠在頁面級元素上將 EventTrigger 聲明爲 Triggers 集合
的一部分或者在 ControlTemplate 中對其進行聲明。使用 EventTrigger,能夠指定當路由事件到達其路由中的某個元素(這個元
素針對該事件聲明瞭 EventTrigger)時將運行的 Storyboard。與只是處理事件而且會致使它啓動現有演示圖板相比,
EventTrigger 的好處在於,EventTrigger 對演示圖板及其運行時行爲提供更好的控制 


附加事件概述 
可擴展應用程序標記語言 (XAML) 定義了一個語言組件和稱爲「附加事件」的事件類型。附加事件的概念容許您針對特定事件爲任意元
素(而不是爲實際定義或繼承該事件的元素)添加處理程序。在這種狀況下,對象既不會引起該事件,目標處理實例也不會定義或「擁
有」該事件。 

附加事件具備一種 XAML 語法和編碼模式,後備代碼必須使用該語法和編碼模式才支持附加事件的使用, 在 WPF 中,附加事件由 
RoutedEvent 字段來支持,並在引起後經過元素樹進行路由。一般,附加事件的源(引起該事件的對象)是系統或服務源,因此運
行引起該事件的代碼的對象並非元素樹的直接組成部分。 

Microsoft .NET Framework 託管代碼中的全部對象都要經歷相似的一系列的生命階段,即創造、使用和析構, 在 WPF 中有四種與
生存期事件有關的主要類型的對象;即常規元素、窗口元素、導航宿主和應用程序對象。任何 WPF 框架級元素(從 
FrameworkElement 或 FrameworkContentElement 派生的那些對象)都有三種通用的生存期事件: Initialized、Loaded 和 
Unloaded。 

引起 Loaded 事件的機制不一樣於 Initialized。將逐個元素引起 Initialized 事件,而無需經過整個元素樹直接協調。相反,引起 
Loaded 事件是在整個元素樹內協調的結果(特別是邏輯樹)。當樹中全部元素都處於被視爲已加載狀態中時,將首先在根元素上引
發 Loaded 事件。而後在每一個子級元素上連續引起 Loaded 事件。 

構建於元素的通用生存期事件上的爲如下應用程序模型元素: Application、Window、Page、NavigationWindow 和 Frame。這
些元素使用與其特定用途關聯的附加事件擴展了通用生存期事件 
路由事件的處理程序能夠在事件數據內將事件標記爲已處理。處理事件將有效地縮短路由。類處理是一個編程概念,受路由事件支持。
類處理程序有機會在類級別使用處理程序處理特定路由事件,該處理程序在類的任何實例上的任何實例處理程序以前調用 
另外一個要考慮「已處理」問題的情形是,若是您的代碼以重要且相對完整的方式響應路由事件,則您一般應將路由事件標記爲已處理 

AddHandler(RoutedEvent, Delegate, Boolean)),在某些狀況下,控件自己會將某些路由事件標記爲已處理。已處理的路由事件
表明 WPF 控件做者這樣的決定:即響應路由事件的控件操做是重要的,或者做爲控件實現的一部分已完成,事件無需進一步處理。
一般,經過爲事件添加一個類處理程序,或重寫存在於基類上的虛擬類處理程序之一,能夠完成此操做 
隧道路由事件和冒泡路由事件在技術層面上是單獨的事件,可是它們有意共享相同的事件數據實例以實現此行爲 
隧道路由事件與冒泡路由事件之間的鏈接是由給定的任意 WPF 類引起本身的已聲明路由事件的方式的內部實現來完成的,對於成對
的輸入路由事件也是如此。可是除非這一類級實現存在,不然共享命名方案的隧道路由事件與冒泡路由事件之間將沒有鏈接:沒有上
述實現,它們將是兩個徹底獨立的路由事件,不會順次引起,也不會共享事件數據。 
路由事件的類處理主要是用於輸入事件和複合控件 
當您一般處理預覽事件時,應謹慎地在事件數據中將事件標記爲已處理。在引起預覽事件的元素(在事件數據中報告爲源的元素)之
外的任何元素上處理該事件都有這樣的後果:使得元素沒有機會處理源自於它的事件。有時這是但願的結果,尤爲當該元素存在於控
件的複合關係內時。 
特別是對於輸入事件而言,預覽事件還與對等的冒泡事件共享事件數據實例。若是您使用預覽事件類處理程序將輸入事件標記爲已處
理,將不會調用冒泡輸入事件類處理程序。或者,若是您使用預覽事件實例處理程序將事件標記爲已處理,則一般不會調用冒泡事件
的處理程序。 
一般使用預覽事件的一種情形是複合控件的輸入事件處理。 

RoutedPropertyChanged 事件 

某些事件使用顯式用於屬性更改事件的事件數據類型和委託。該事件數據類型是 RoutedPropertyChangedEventArgs<(Of 
<(T>)>),委託是 RoutedPropertyChangedEventHandler<(Of <(T>)>)。事件數據和委託都具備用於在您定義處理程序時指


定更改屬性的實際類型的泛型參數。事件數據包含兩個屬性,即 OldValue 和 NewValue,以後它們均做爲事件數據中的類型參數傳
遞。 

名稱中的「Routed」部分表示屬性更改事件註冊爲一個路由事件。路由屬性更改事件的好處是,若是子元素(控件的組成部分)的屬性
值更改,控件的頂級也能夠接收到屬性更改事件。例如,您可能建立一個合併 RangeBase 控件(如 Slider)的控件。若是滑塊部分
的 Value 屬性值更改,則您可能須要在父控件(而不是在該部分)處理此更改。 

RoutedPropertyChanged 事件 

某些事件使用顯式用於屬性更改事件的事件數據類型和委託。該事件數據類型是 RoutedPropertyChangedEventArgs<(Of 
<(T>)>),委託是 RoutedPropertyChangedEventHandler<(Of <(T>)>)。事件數據和委託都具備用於在您定義處理程序時指
定更改屬性的實際類型的泛型參數。事件數據包含兩個屬性,即 OldValue 和 NewValue,以後它們均做爲事件數據中的類型參數傳
遞。 

DependencyPropertyChanged 事件 

屬於屬性更改事件方案一部分的另外一對類型是 DependencyPropertyChangedEventArgs 和 
DependencyPropertyChangedEventHandler。這些屬性更改的事件不會路由;它們是標準的 CLR 事件。
DependencyPropertyChangedEventArgs 不是普通的事件數據報告類型,由於它不是派生自 EventArgs;
DependencyPropertyChangedEventArgs 是一個結構,而不是一個類。 
與屬性更改事件密切相關的一個概念是屬性觸發器。屬性觸發器是在樣式或模板內部建立的,使用它,您能夠建立基於分配有屬性觸
發器的屬性的值的條件行爲。屬性觸發器的屬性必須是一個依賴項屬性。屬性的主要方案是報告控件狀態,可能與實時 UI 具備因果
關係,並所以是一個屬性觸發器候選項。 

其中的一些屬性還具備專用屬性更改事件。例如,屬性 IsMouseCaptured 具備一個屬性更改事件 IsMouseCapturedChanged。
該屬性自己是隻讀的,其值由輸入系統調整,而且輸入系統對每次實時更改都引起 IsMouseCapturedChanged。 

爲了補償具備各類可能值的屬性觸發器的「if」條件,一般最好使用 Setter 將該屬性值設置爲默認值。這樣,當觸發器條件爲 true 時,
Trigger 包含的 setter 將具備優先權,而只要觸發器條件爲 false,則不在 Trigger 內部的 Setter 就具備優先權。 
屬性觸發器一般適合於一個或多個外觀屬性應基於同一元素的其餘屬性的狀態而更改的狀況。 
如何實現 WeakEvent 模式? 
實現 WeakEvent 模式由三個方面組成: 

. 從 WeakEventManager 類派生一個管理器。 
. 在任何想要註冊弱事件的偵聽器的類上實現 IWeakEventListener 接口,而不生成源的強引用。 
. 註冊偵聽器時,對於想要偵聽器使用該模式的事件,不要使用該事件的常規的 add 和 remove 訪問器,請在該事件的專
WPF) 中的元素以元素樹結構形式排列。父元素能夠參與處理最初由元素樹中的子元素引起的事件,這是因爲存在事件路由。
用 WeakEventManager 中改用「AddListener」和「RemoveListener」實現。 


下面的示例使用 XAML 屬性語法向公用的父元素(在本示例中爲 StackPanel)附加事件處理程序。本示例使用屬性語法向 
StackPanel 父元素附加事件處理程序,而不是爲每一個 Button 子元素都附加一個事件處理程序。這個事件處理模式演示瞭如何使用
事件路由技術來減小須要附加處理程序的元素數量。每一個 Button 的全部冒泡事件都經過父元素進行路由。 

若要使您的自定義事件支持事件路由,須要使用 RegisterRoutedEvent 方法註冊 RoutedEvent。本示例演示建立自定義路由事件
的基本原理。 
自定義路事件: public partial class mySimpleButton : Button 
{ 
//public static readonly DependencyProperty IsSpinningProperty = DependencyProperty.Register(); 


 public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent("Tap", 
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(mySimpleButton)); 
public event RoutedEventHandler Tap 
{ 
add { AddHandler(TapEvent, value); } 
remove { RemoveHandler(TapEvent, value); } 
} 
public void RaiseTapEvent() 
{ 
RoutedEventArgs neweEventArgs = new RoutedEventArgs(mySimpleButton.TapEvent); 
RaiseEvent(neweEventArgs); 
} 
protected override void OnClick() 
{ 
RaiseTapEvent(); 
} 
} 
WPF) 子系統爲輸入提供了一個全新的功能強大的 API。命令是 (WPF) 中的輸入機制,它提供的輸入處理比設備輸入具備更高
的語義級別,(WPF) 子系統提供了一個功能強大的 API,用於獲取來自各類設備(包括鼠標、鍵盤和手寫筆)的輸入。 

。許多輸入事件都有一對與其關聯的事件。 例如,鍵按下事件與 KeyDown 和 PreviewKeyDown 事件關聯。 這些事件之間
的差異是它們路由到目標元素的方式。 預覽事件在元素樹中從根元素到目標元素向下進行隧道操做。 冒泡事件從目標元素到根元素
向上進行冒泡操做。 

下面的示例使用 GetKeyStates 方法肯定 Key 是否處於按下狀態。 
if ((Keyboard.GetKeyStates(Key.Return) & KeyStates.Down) > 0) 
{ 
btnNone.Background = Brushes.Red; 
} 

下面的示例肯定鼠標上的 LeftButton 是否處於 Pressed 狀態。 
if (Mouse.LeftButton == MouseButtonState.Pressed) 
{ 
UpdateSampleResults("Left Button Pressed"); 
} 
路由事件使用三種路由機制之一:直接、冒泡和隧道。 在直接路由中,源元素是惟一獲得通知的元素,該事件不會路由到任何
其餘元素。可是,相對於標準 CLR 事件,直接路由事件仍提供了一些其餘僅對於路由事件才存在的功能。冒泡操做在元素樹中向上
進行,首先通知指明瞭事件來源的第一個元素,而後是父元素,等等。 隧道操做從元素樹的根開始,而後向下進行,以原始的源元素
結束。 

因爲輸入事件在事件路由中向上冒泡,所以無論哪一個元素具備鍵盤焦點,StackPanel 都將接收輸入。 TextBox 控件首先獲得
通知,而只有在 TextBox 未處理輸入時纔會調用 OnTextInputKeyDown 處理程序。若是使用 PreviewKeyDown 事件而不是 
KeyDown 事件,則將首先調用 OnTextInputKeyDown 處理程序。 
在此示例中,處理邏輯寫入了兩次,一次針對 Ctrl+O,另外一次針對按鈕的單擊事件。使用命令,而不是直接處理輸入事件,可
簡化此過程。 

爲了使元素可以獲取鍵盤焦點,Focusable 屬性和 IsVisible 屬性必須設置爲 true。 某些類(如 Panel)默認狀況下將 
Focusable 設置爲 false;所以,若是您但願該元素可以獲取焦點,則必須將此屬性設置爲 true。 


在 WPF 中,有兩個與焦點有關的主要概念:鍵盤焦點和邏輯焦點。WPF) ,資源,能夠經過一種簡單的方法來重用一般定義的對象和
值。 

確保在請求資源以前已在資源集合中對該資源進行了定義。若有必要,您能夠使用 DynamicResource 標記擴展在運行時引用
資源,這樣能夠繞過嚴格的資源引用建立順序,但應注意這種 DynamicResource 技術會對性能產生必定的負面影響 
<SolidColorBrush x:Key="MyBrush" Color="Gold"/> 
<Style TargetType="Border" x:Key="PageBackground"> 
<Setter Property="Background" Value="Blue"/> 
</Style> 
引用:<Style TargetType="TextBlock" x:Key="Label"> 
<Setter Property="DockPanel.Dock" Value="Right"/> 
<Setter Property="FontSize" Value="8"/> 
<Setter Property="Foreground" Value="{StaticResource MyBrush}"/> 
</Style> 
引用: 
<Style TargetType="Button" x:Key="GelButton" > 
<Setter Property="Margin" Value="1,2,1,2"/> 
<Setter Property="HorizontalAlignment" Value="Left"/> 
<Setter Property="Template"> 
<Setter.Value> 
...... 
</Setter.Value> 
</Setter> 
</Style> 
SystemFonts: 
<Button Margin="10, 10, 5, 5" Grid.Column="0" Grid.Row="3" 
FontSize="{x:Static SystemFonts.IconFontSize}" 
FontWeight="{x:Static SystemFonts.MessageFontWeight}" 
FontFamily="{x:Static SystemFonts.CaptionFontFamily}"> 
SystemFonts 
</Button> 

系統資源將許多系統度量做爲資源公開,以幫助開發人員建立與系統設置一致的可視元素。SystemFonts 是一個類,它包含系
統字體值以及綁定到這些值的系統字體資源。例如,CaptionFontFamily 和 CaptionFontFamilyKey。 
系統字體規格能夠用做靜態或動態資源。若是您但願字體規格在應用程序運行時自動更新,請使用動態資源;不然,請使用靜態
資源。 
引用DynamicResource: 
<Style x:Key="SimpleFont" TargetType="{x:Type Button}"> 
<Setter Property = "FontSize" Value= "{DynamicResource {x:Static SystemFonts.IconFontSizeKey}}"/> 
<Setter Property = "FontWeight" Value= "{DynamicResource {x:Static 
SystemFonts.MessageFontWeightKey}}"/> 
<Setter Property = "FontFamily" Value= "{DynamicResource {x:Static 
SystemFonts.CaptionFontFamilyKey}}"/> 
</Style> 

系統資源會將多個基於系統的設置做爲資源進行顯示,以幫助您建立與系統設置協調一致的視覺效果。SystemParameters 是一個
類,其中既包含系統參數值屬性,又包含綁定到這些值的資源鍵。例如,FullPrimaryScreenHeight 是 SystemParameters 屬性
值,FullPrimaryScreenHeightKey 是相應的資源鍵。 


在 XAML 中,能夠使用 SystemParameters 的成員做爲靜態屬性用法或動態資源引用(靜態屬性值爲資源鍵)。若是您但願基於
系統的值在應用程序運行時自動更新,請使用動態資源引用;不然請使用靜態引用。資源鍵的屬性名稱後面附有 Key 後綴。 
<Style x:Key="SimpleParam" TargetType="{x:Type Button}"> 
<Setter Property = "Height" Value= "{DynamicResource {x:Static 
SystemParameters.CaptionHeightKey}}"/> 
<Setter Property = "Width" Value= "{DynamicResource {x:Static 
SystemParameters.IconGridWidthKey}}"/> 
</Style> 
查找template 中資源: 
<Style TargetType="{x:Type Button}"> 
<Setter Property="Template"> 
<Setter.Value> 
<ControlTemplate TargetType="{x:Type Button}"> 
<Grid Margin="5" Name="grid"> 
<Ellipse Stroke="DarkBlue" StrokeThickness="2"> 
<Ellipse.Fill> 
<RadialGradientBrush Center="0.3,0.2" RadiusX="0.5" RadiusY="0.5"> 
<GradientStop Color="Azure" Offset="0.1" /> 
<GradientStop Color="CornflowerBlue" Offset="1.1" /> 
</RadialGradientBrush> 
</Ellipse.Fill> 
</Ellipse> 
<ContentPresenter Name="content" Margin="10" 
HorizontalAlignment="Center" VerticalAlignment="Center"/> 
</Grid> 
</ControlTemplate> 
</Setter.Value> 
</Setter> 
</Style> 
View Code
相關文章
相關標籤/搜索