It has to be further styled, although that's the basic idea. Notice that there is no ContentPresenter here, as the look of our Button will be set using only its Background property.

2. Images Are Defined Entirely in XAML

First, in WPF, it is possible to paint using VisualBrush. Such brush can be set as Background of controls. The Visual of the VisualBrush can be a lot of objects. In our case, the simplest solution is to use Canvas with some vector image that will be defined in XAML.

Getting such a definition is easier than you think, although it requires a workaround or two.

If you already have a vector image, save it as SVG. If you have a raster image, it can be converted to SVG using for example some online converter (e.g., Google "convert png to svg") or you can do it using Inkscape's Path/Trace Bitmap.

In this example, we will be using an icon that is already in SVG format, although I modified it a little in Inkscape to standardize the sizes and add some background (file attached). Basically, I added a square white background, grouped the objects, changed the unit in Document Properties to inches, then set document width and height to 1 and resized the group of objects to fit the document. This way, my image is 1x1 inch, which will cause the resulting image to be 96x96 px (because of my screen dpi) after it is printed (Inkscape/File/Print) on Microsoft XPS Document Writer, without annoying fraction digits.

Why print it? It turns out that the "regular" SVG format is a little different than the syntax used in XAML. Instead of manually converting it, we can use Microsoft XPS Document Writer to print it. Then, rename the resulting XPS file to ZIP, and extract the file in \Documents\1\Pages\1.fpage. After that, change the extension of FPAGE file to XML (or open it with a text editor). Inside, you will have a pretty, XAML-compatible definition of your image. The (almost) last thing you'll have to do is replace the FixedPage tag with Canvas.

Since our Canvas and geometries will have bound properties, they won't be able to freeze anyway, so setting PresentationOptions:Freeze="True" won't do a thing. On the other hand, we have to set x:Shared="False" so we can use our Canvas multiple times in our application.

3. Images Can Be Set Easily and Dynamically by Key & 4. Images Have Bindable Properties Such as Foreground, Background, Opacity, Fill Color, Etc.

This can be done using Attached Properties. Let's define a class VisBg:

publicclass VisBg: DependencyObject

It will have 5 properties that will be used to set visual properties of the image: ResourceKey, Foreground, Background, Opacity and Fill. It will also have one property that will expose the resulting VisualBrush, called BrushValue. Then, there will be a private property that will hold the data to which our image (Canvas) will be bound: BrushData.

VisualBackgroundData is a class that is used to get the correct resource and set DataContext of Canvas. For better readability of resource keys and to avoid repetition, the Canvas is declared in the ResourceDictionaries with Key starting with "Canvas_", and while searching for the resource, this header is added by default.

The VisualBackgroundData class also holds the reference to the source FrameworkElement for which it was instantiated. It is used to find resource defined for this element. Application.TryFindResource(key) can be used too, without the source reference, but that will force you to always import all of the resources at the application level, in the App.xaml file.

In VisualBackgroundData class, when the Key changes, the application searches for the appropriate Canvas, and if it is found, it is set as the Visual for the VisualBrush that will later be used to manage backgrounds for controls.

The rest of the attached properties (ResourceKey, Foreground, Background, Opacity, Fill and BrushValue) have PropertyChangedCallback method defined. In those methods, the private attached property BrushData that holds the data is instantiated if necessary, and the appropriate property in that instance is set.

Now, let's return to the XAML definition of our image. The Brushes used in the Canvas and its Opacity property:

(Notice the brackets around a Path to the attached property in Binding.)

Now, let's define a style, where the Foreground and Fill colors of the image (not the button itself!) change when the button is pressed. Since we do not use the actual Foreground and BorderBrush properties of the Button, we can utilize those too (Button.Foreground for pressedCanvasImage.Fill, and Button.BorderBrush for pressedCanvasImage.Foreground). This way, our Yellow-Black-Red button can change to Yellow-Violet-White when pressed:

Now, let's also make it so the image's Background will change to its Fill color when mouse is over the button. When we are doing that, why not add an Opacity animation too that starts when the Button is pressed?

Points of Interest

You can set SnapsToDevicePixels and UseLayoutRounding for Canvas to "True", then the image will be crisper, but it will sometimes cause the parts of geometry to move strangely when resized. It is especially visible for smaller sizes, where one path moving 1 pixel away from another in the image makes a noticeable difference. I guess it depends on the image and if you'll want it to be resizable at runtime. It depends on what you can stomach better: blurry or not entirely accurate.

While Borders and Buttons defined and styled in the above manner show in the designer in Visual Studio 2010 .NET Framework 4.0, they do not in Visual Studio 2015 Community .NET Framework 4.5.2. The application runs without problems though and there are no warnings whatsoever. If someone has any idea as to why it is so and is willing to share, I would be grateful.