Page 4 of 5
The connection to the Image control
Now we have everything needed to make it all work apart from the answer to one simple question.
When you assign a bitmap source to something like an Image control e.g.
TestBitmapSource TBS =
new TestBitmapSource(500, 100);
image1.Source = TBS;
Then how do the bits in the BitmapSource get from there to the Image control?
You might think that there was some deep and special connection between the two but no. What happens is that one of the CopyPixels methods are used to transfer the bits into a suitable data structure. In this case it is the version of the method that copies the specified rectangle of pixels to an array, that is:
public override void CopyPixels(
Notice that the bits are transferred between the two objects using a managed data structure and you might think that this would be inefficient. However if you recall a BitmapSource is regarded as Frozen so this transfer occurs once and once only when the assignment is made.
So to complete the customisation we have at least to provide an implemenation of this CopyPixels method and we can once again pass on the job to the encapsulated BitmapSource:
public override void CopyPixels(
int stride, int offset)
Now if you try out the custom class it all works and you can see the test card grid pattern displayed in the Image control.
It is also important to realise that the destination doesn't call the source's CopyPixels method just once to transfer the entire bit map n one operation. It transfers the data in stride wide chunks. That is CopyPixels is called once for each row of the bitmap using the sourceRect parameter to specify each row in turn.
So the very minimum you have to implement to make a custom BitmapSource do some useful work is:
one Freezable method:
and one method
Of course to provide a full implementation you have to at least consider implementing the remaining properties, events and methods. In this case most of the implementation simply comes down to calling the encapsulated BitmapSource's methods that do the same job. This is tedious but not difficult and it is typical of the sort of thing you have to do when implementing inheritance based on aggregation.
A more subtle problem is the fact that your class has some methods that you might not think it actually needs. This is usually insignificant as an abstract class usually doesn't pass on to derived classes any implementation of methods but in this case it does. For example, the new TestBitmapSource inherits a complete static Create Method from BitmapSource - so much for an abstract class. What do you think
returns? Logically the answer should be a TestBitmapSource. In fact it returns a CachedBitmap object just like the BitmapSource create method. In fact all of the classes derived from BitmapSource return a CachedBitmap from their inherited Create method. From an object-oriented point of view this is slightly odd. Notice however that there is no way that you can remove the Create method and the principle that a derived class can always be used in place of a base class means that you shouldn't even try. However, it would be more logical if the Create method returned an object of the derived type. If you think that this is correct then you can override the Create method - but this isn't what is done in the rest of the bitmap classes.
It would have been better if BitmapSource had not been declared abstract and then there would be no need for the Create method.