Ivan Krivyakov's Blog

Premature optimization is the root of all evil

August 31, 2009

Fancy Borders

I was trying to draw a “raised” border around my control, something resembling a 3D window border in Windows. I.e. something like white on the outside, gray in the middle, black on the inside (or something like that). I ran into quite a few difficulties. My discoveries so far:

  • If you use a gradient brush as the border background, it will not think they way you expect: see Charles Petzold’s post on gradient brushes
  • While you can make things like <Rectangle> or <Ellipse> to have a width of “Auto” (which might mean “fill the whole parent”), corresponding geometries like <EllipseGeometry>must have absolute widths.
  • I could not find a way to create in XAML a geometry that would be stretchable with the parent control, even using control templates. I am sure it is possible in code.
  • I probably spent too much time trying to draw a fancy border around my control. :)

August 27, 2009

Dependency Properties

Dependency properties are a special feature of WPF that is similar in principal to regular .NET properties, with at least two functional distinctions:

  • They support change notifications
  • They support special handling for animations, retaining the “original” value

The problem with dependency properties is that they are not directly supported by C# language, and the syntax is quite verbose. Here’s how a typical dependency property is defined in a class that ultimately derives from DependencyObject:
[read more...]

August 20, 2009

Custom Meshes

As I already mentioned, typing mesh positions by hand is not scalable, even for the simplest things like a cube. Meshes must be generated either by a tool or by code. Ideally, I would like to write something like this:

<GeometryModel3D>
<GeometryModel3D.Geometry>
<my:Sphere Center=”1 2 -1″ Radius=”0.9″>
</GeometryModel3D.Geometry>
</GeometryModel3D>

Unfortunately, this is not possible. Geometry property expects MeshGeometry3D, which is a sealed class. So, deriving a Sphere from MeshGeometry3D is not an option. Surprisingly, very little on the subject can be found on the net. It appears that there are two practical ways to refer to a generated mesh (as Charles Petzold describes in this post):

There are basically two ways to create classes that generate MeshGeometry3D objects and which can be used in XAML:

Method 1: Write a class that exposes a public property (named Geometry, for example) of type MeshGeometry3D. In XAML, reference that class in a Resource section. In your markup, create a GeometryModel3D as usual, and define a binding between the Geometry property of that GeometryModel3D and the Geometry property of the resource.

Method 2: Write a class that derives from ModelVisual3D. This is the only class in the Systems.Windows.Media.Media3D namespace that is neither sealed nor abstract! This class creates its own MeshGeometry3D and GeometryModel3D objects. The class must also define its own public Material and BackMaterial properties, and transfer the values of those properties to its internal GeometryModel3D. It’s messy, but you can then instantiate this class directly in XAML as a child of a Viewport3D.

This seems a little surprising. After all, if WPF does not provide its own primitives, it should have made creating custom primitives easy, no?

Lights, Camera, Model!

These are three main components of a WPF 3D scene. In fact, it should be more like “Lights, Camera, GeometryModel3D”, but “GeometryModel3D” looks ugly and messes up the rhythm of the title.

All 3D objects in WPF live inside a Viewport3D object. This is a two-dimensional window (typically, a rectangle, but it can be made to take any shape) that shows 3D stuff inside itself.

Each viewport has a camera. This defines a point (and angle, etc.) from which we look at things. There is only one camera per viewport – you cannot look at things from different points of view simultaneously. Besides the camera, viewport may contain one or more ModelVisual3D objects. Each ModelVisual3D has a Content property that is of type Model3D.

Model3D is a base class in a typical composite pattern. It may be a light, a “body” (GeometryModel3D), or a group of lights and bodies (Model3dGroup).

wpf_3dmodel

Now, each body (GeometryModel3D) further needs a mesh (MeshGeometry3D), i.e. a list of vertices, and material that defines what the body is made of and what color it has.

These are the basic elements of a 3D world setup, and unfortunately they are all required. Without a camera you cannot see, without bodies there is nothing to see, and without light everything is pitch black. Without mesh or material, bodies are invisible. But if you set it all up, a new brave 3D world suddenly jumps into action.

August 14, 2009

The Rotating Cube

Rotating Cube ScreenshotContinuing to explore WPF, I wandered into the world of 3D. I decided to start small: let’s display a rotating cube. I built a little sample that is available here (17KB zip file) and the screenshot is to your right.

There is an excellent CodeProject sample by Prasad02 that shows a rotating cube, but it is much more involved. I took this sample as an inspiration and built my own from scratch, periodically taking a peek at Prasad’s work.

I also left my cube open (only 5 planes instead of 6) to explore the shading effects on the interior. The sample is entirely in XAML – I did not have to write a single line of C#. Some would say “I did not have to write a single line of code”, but here we risk to plunge into a long debate on what exactly is “code”. It’s a very interesting subject, but I will discuss it some other time :)

The cube is comprised of five planes of different colors and a gray interior. You are looking almost directly at the vertex, but I moved the vertex slightly off the line of sight, to make the picture more 3D. I had a hard time making interior shading look natural. I am afraid I did not succeed much. I think the problem is in the way WPF handles light sources – more about it in a separate article I guess. I also could not help but notice that the borders between the planes are quite coarse – WPF does not care to smooth the edges and the border looks uneven (i.e. suffers from pixelization), in any resolution. I guess smaller triangles and/or nicer textures would take care of that.

Also, you definitely need a tool to draw your bodies. Spelling out coordinates by hand is simply not an option. Unless you are a 3D prodigy, anything more complicated that a cube ought to make you slightly insane.

Note that the rotation make look weird, because we don’t really rotate the cube, we rotate the whole space together with the light sources, so the shading of the facets does not change. This is not what normally happens in real life. In real life while the body rotates, light sources remain static, and the facets change the color with rotation. One day I may come back to this sample to implement real rotation.

Note, that Silverlight (as of version 3) does not support 3D, so this sample cannot be converted to a Silverlight app.

August 13, 2009

WPF: Pack URIs: what’s the point of all the commas?

WPF uses “pack” URI schema to point to resources. An absolute pack URI looks like this:

pack://application:,,,/ResourceFile.xaml

The schema is described in this MSDN article and they claim the URIs are compatible with RFC 2396.

This is all fine and nice, but who invented these commas? They say it was supposed to be “application:///”, but you cannot put that many slashes in a valid URI, so they had to encode it. Why bother with the slashes at all?

pack://authority/path

would be a perfectly valid URI. E.g.

pack://application/ResourceFile.xaml

Whenever I see several commas in a row, I feel there are some missing parameters that could have been inserted there – thank you, VB. So, all these commas in URIs takes some getting used to.

Fortunately, relative URIs don’t require them, you can just say ResourceFile.xaml and it would work fine.

August 12, 2009

Expression Blend: Setting Background

brushes
Setting window background in Expression Blend proved to be quite confusing. Of course, seasoned users would say it is beyond obvious and I am just an ignorant novice. This may be true, but in my opinion this is exactly how good apps differ from bad apps. Good apps make it easy for ignorant novices by following established practices and by not violating “the rule of least astonishment”. Of course, different people may be astonished by different things, but nevertheless there is some established common philosophy of Windows apps.

Astonishment #1: there is no “Properties” in the right click menu. I naturally assumed that “background” would be one of the properties, and I was right. However, I had a hard time finding the properties window. Usually you right-click on the object you want to edit, choose “Properties” from the right click menu and you’re there. Not in Expression Blend. There is no even View→Properties in the menu. There is Window→Properties, but who looks in the Window menu? :) It turns out “Properties” is one of the tabs alongside “Project” and “Resources”, but it seems so obvious to the authors of multiple tutorials, they don’t even mention that. Or maybe I was just missing it.
[read more...]

August 11, 2009

Expression Blend

Expression blend looks like an interior of a coffin. It is outright depressing. “Expression light” theme is better – it looks like an interior of a coffin on a bright sunny day.

WPF: Selecting a Background Image

As many people have pointed out, WPF in its current form is far from point-and-click experience. This is how you set background image on a windows form (not WPF):

  1. Select the form
  2. Right click on properties
  3. Find BackgroundImage property
  4. Click “Import”, find your file on disk
  5. You’re done

In WPF you need to add the file to the project, and then you have to write by hand something like this:

<Window.Background>
  <ImageBrush ImageSource="/Images/sahara.jpg" Stretch="Fill" />
</Window.Background>

This is way too much writing. I would tolerate something like <Window BackgroundImage="/Images/sahara.jpg">, but this brush mambo-jumbo just does not feel right. Too much noise. Yes, I know it fits nicely in the conceptual model et cetera, but it is still too much typing for such a simple task.

WPF: Elliptical Button

Screenshot of an elliptical button
Creating an elliptical button proved to be not as easy as some claimed. I would hardly describe it is “a snap”. <Button Shape="Ellipse"/> would be a snap, but the reality is nowhere near it.

From the other hand, not being a WPF expert, I managed to complete it within an hour with some googling, which means it is not that hard. Doing the same in Windows Forms would definitely give me much more headache. Full source code can be downloaded here (15K ZIP file).

The easiest way is to fake it: create a rectangular button and clip it to an ellipse, but this is cheating. The right way is to change the button control template. This post shows how to create a round button, but it is not pressible. This post demonstrates how to add a nice border with the “sink in” effect when the button is pressed. The result is still a little bit rough, but it works.

Everything is done in XAML, not a single line of code, but there is quite a few lines of it. I will provide it here in its entirety, just so you can feel the size of it:

[read more...]