TimeGIS
InfoVista.NET
首页             技术文章             专业软件             管理软件             开源软件             知识分享             网站地图            

 程序设计

      C++
      C#
      Web
      GIS

 软件工程

      软件框架
      设计模式
      建模仿真

 计算机科学

      HelloWorld
      Jolt图书奖
      开源获奖
      图灵奖


.NET Shape Library: A Sample Designer1

参考:http://windowsclient.net/articles/shapedesigner.aspx

.NET Shape Library: A Sample Designer

Get the samples for this article. Unzip the folder and double-click either the VB or C# VS.NET solution.

The Shape Library provides a comprehensive example of writing a designer in the .NET Framework. It starts with a very simple set of runtime vector drawing components: the shape library, and adds design-time support for these components. The design-time code provides many examples of the rich features available to .NET Framework designers.

Compiling and running the sample

The sample is written as a Visual Studio solution containing two C# projects. One project is the ShapeLibrary itself and the other is a sample project to test the library. Load up the solution and build it. Once it is built you can experiment with the test project. Open MyShape.cs to manipulate the shape designer. To add additional shapes, you must first add the shape components to the toolbox. To do this, right click on the toolbox and choose Customize. Then click on the .NET Framework Components tab and click the Browse button. Navigate to the bin\debug directory of the ShapeLibrary project and choose ShapeLibrary.dll. This will select the components in the dialog; just click on the check box to check them all and they will appear on the current tab of the toolbox. Now you can put them on the shape designer's design surface.

To view your shape's changes on a form, you can open the TestForm.cs file. This file has an instance of your shape on it. If you have made changes to the shape, rebuilding your test project will update the form. Pressing F5 will execute the sample form, showing your shape.

Runtime components

The shape library's runtime components consist of six total objects:

Shape

Shape is the base class for all shapes. Shape is abstract and doesn't define any properties because shapes can have various characteristics. For example, Shape does not provide any sort of bounding box because for some shapes, like lines, this property does not make sense. All shapes can be drawn onto a graphics object, however, so Shape provides a single abstract method named Draw. Shape derives from System.ComponentModel.Component. The reason for this will be covered in a later section.

Line

The Line class is derived from Shape. It implements the Draw method to draw a line. It also adds several properties to Shape that help to define the line. The StartPoint and EndPoint properties describe the starting and ending points of the line, and the Color property determines what color the line will be.

Ellipse

The Ellipse class is derived from Shape. It implements the Draw method to draw an ellipse. The Ellipse object also defines its own properties that are used to define the characteristics of the ellipse. Ellipse offers a Bounds property which describes the bounding rectangle for the ellipse, a Color property which defines the color of the line used to draw the ellipse, and a Fill property. The Fill property uses a custom data type called ShapeFill, which provides a few ways that the ellipse's interior can be filled.

ShapeContainer

The ShapeContainer class is also derived from Shape. ShapeContainer does no drawing of its own, but it provides a collection of shapes. When ShapeContainer is asked to draw, it iterates through its collection of shapes, asking each shape to draw.

ShapeFill

The ShapeFill object derives from object and provides a few ways to fill objects. ShapeFill contains a Brush object that is used to fill areas with a pattern or color, and also provides a few static pre-built instances of itself so developers can pick a ShapeFill at design time from a drop-down list.

WindowsFormsAdapter

Shapes have been designed only to use classes from System.Drawing. This allows a shape object to be used on both Windows Forms applications and ASP.Net applications. A common use will be to have a shape draw itself on a Windows Forms control. This requires a small amount of "glue" code to connect a control's paint event to the shape's Draw method. Instead of requiring the user to write this code, we provide a special adapter object. The adapter has a method called Connect that accepts a control and a shape. The adapter then connects the control's paint event to the shape's draw method. This generally wouldn't be much of a help to the user, but in our case we have also provided some additional design-time code to automatically create adapters when shapes are placed on Windows Forms objects.

The Base Class: Component vs. Object

Why does Shape derive from System.ComponentModel.Componet while ShapeFill and WindowsFormsAdapter do not? This is a requirement of the .NET Framework designer architecture. The designer architecture works with any type of object, but designers (described later) can only be attached to objects that implement System.ComponentModel.IComponent. Objects that implement IComponent can also be added to a container object that implements System.ComponentModel.IContainer. This container is responsible for controlling the lifetime of the component objects it contains. At design time, Visual Studio provides a container that implements this interface. Any component added to this container automatically participates in code generation and gets its own member variable in the user's code.

Note that it is possible to use the .NET Framework designer architecture even if your objects are not components, but there needs to be at least one object that is a component. For example, if we were to expand our shape library into a full vector drawing package, we would probably find that emitting a member variable for each shape would not be terribly efficient. Instead, it would probably make sense for ShapeContainer to implement IComponent while individual shapes did not. That requires more work on the component vendor's part because less design-time logic is handled automatically, but it is possible and may be an appropriate choice for when the number of objects in the design surface becomes huge.

An introduction to design-time

The .NET Framework was written with design-time support in mind. What is design-time support? It's the ability to connect several components together without writing code. The .NET Framework was written to support very rich design-time capabilities while allowing this design-time specific code to reside in a separate assembly so it does not contribute to the size of the runtime library.

Designers

Each object that implements the System.ComponentModel.IComponent interface can have an additional designer object associated with it. This object, called a "designer", contains the additional code needed to manipulate the object at design time. A designer may perform a very simple task, such as hiding or changing properties on the component that should not be available at design time, or it may work with other designers to provide a visual editor for a group of components, such as a form or web page designer. A designer must implement the interface System.ComponentModel.Design.IDesigner.

There may be many designers created for a visual editor. One of these designers must be the "master" that provides an interface that the user can interact with. This designer is called the "root designer", and the component it is bound to is called the "root component". In the Windows Forms designer, for example, the class System.Windows.Forms.Form is typically the root component, while in ASP.Net it is System.Web.UI.Page.

In addition these designers, there is an object that is responsible for maintaining instances of all designers and components, and for loading and saving data. This object implements an interface called System.ComponentModel.Design.IDesignerHost, and is usually just called a "host" or "designer host". Visual Studio provides an instance of this object for each visual editor.

As we delve more into the shape library's design time support we will examine how its designers work in detail.

Persistence

Persistence relates to the loading and saving of data. The .NET Framework designer architecture allows data to be persisted in any format. Visual Studio implements two common formats: code generation and resource files.

Code generation is the default way that components save their data. Because all components in the .NET Framework are designed to work through command line compilers and standard editors, they cannot rely solely on special features in Visual Studio. Therefore, having Visual Studio generate source code in response to changes to a visual editor is a convenient way to provide rich design-time support while minimizing the number of ways to implement a feature. In addition, code generation acts as a great learning tool for developers. Visual Studio provides no "magic"; developers can see first-hand how everything fits together.

In addition to code generation, Visual Studio also provides a way to save language-specific data and other resources that cannot be easily represented as code (like bitmaps or audio files, for example).

As we will see in some of the design time code contained in the shape library, you have a great deal of control over how resource and code generation support are handled for your component.

Shape library's design time experience

The following walk-through demonstrates the user model for interacting with the shape library in Visual Studio. The intent is that a user creates a class that derives from ShapeContainer and uses this class to visually composite several shapes together. She then compiles the project and adds the newly created class to a Windows Forms form class. The shapes then draw directly to the form.

Step 1: Creating a new ShapeContainer class

Visual Studio determines what designer to load based on the class that your source file is inheriting from. We start off by creating an empty class file, and then we change it to inherit from Microsoft.Samples.ShapeLibrary.ShapeContainer. This will enable the view designer button. Clicking on this button shows the empty ShapeContainer designer, ready for shapes:

Notice that the designer has placed a disabled "MyShape" item on the toolbox. This item refers to the current shape we're creating; we'll use it later when we want to place this shape on windows form.

Step 2: Adding shapes to the container

Next, the user uses the ellipse and line toolbox items to create shapes. Shapes can be directly manipulated on the shape container design surface. The designer provides support for dragging and sizing shapes. Also, the property browser shows the properties for each shape:

Step 3: Add the shape to a form

Next, the user compiles the project to make the MyShape class available to other objects, and opens an empty form. This enables the MyShape item on the toolbox, and disables the ellipse and line shapes. The user drags the MyShape toolbox item into the form. This creates an instance of MyShape in the form's component tray (because MyShape is not a control). But something interesting happens: MyShape is drawn on the background of the form. ShapeContainer's designer is performing this drawing, and we'll see how this is accomplished in a later section:

Step 4: Run the application

Now the user presses F5 to run the application. This displays the form, and the form is drawing the shape just like we see at design time. This is exactly what the user expects, but it does take some work to make this happen because the shape container's designer had to generate the code that binds the form's paint event to the shape container's draw method:

Design time components

So, how does it all work? There are seven objects that make up the design-time components for the shape library. All of these objects are contained under the Design sub-directory and have the namespace "Microsoft.Samples.ShapeLibrary.Design". At the heart of these objects is an object called the root designer. This designer is associated with the ShapeContainer class through a metadata attribute:

 

 


共3页  第1页  首页  上一页  下一页  尾页


友情链接 尚禹水利环境 | 我行我速 | SharpDevelop | CSLA.NET | CodePlex | 开源中国社区 | 流体中文网 | 水资讯网 | 上帝之眼 FindSim.NET |
本网页由快手工具软件自动生成,感兴趣者请联系我们。