WPF TileBrush and DrawingContext.DrawRectangle -- relative to upper-left of rectangle?
When using DrawingContext.DrawRectangle with a TileBrush, I noticed that the top-left of the rectangle is not the top-left of the underlying image. This occurs with both a DrawingBrush and an ImageBrush with the source as a DrawingImage. Is there a way to force DrawRectangle
to always snap the underlying brush to the top-left corner? Here's a picture:
The two rectangles are using the same brush but are at different points on the screen. As you can see, the brush's origin is different (it seems the brush is continuous over a much larger area). Here's a minimal repro:
private static readonly Brush _brush;
static CustomControl()
{
Uri uri = new Uri(@"p开发者_Go百科ack://application:,,,/WpfApplication1;component/image.png", UriKind.Absolute);
BitmapImage img = new BitmapImage(uri);
Rect rect = new Rect(0, 0, img.Width, img.Height);
ImageDrawing drawing = new ImageDrawing(img, rect);
_brush = new DrawingBrush
{
Drawing = drawing,
Viewport = rect,
ViewportUnits = BrushMappingMode.Absolute,
TileMode = TileMode.Tile
};
_brush.Freeze();
}
protected override void OnRender(DrawingContext dc)
{
dc.DrawRectangle(_brush, null, new Rect(70, 70, 100, 150));
dc.DrawRectangle(_brush, null, new Rect(200, 200, 80, 120));
}
* In this case an ImageBrush would give the correct results, but in my program, I'm dealing with a DrawingBrush with a custom GeometryDrawing.
I've tried using a pen instead of a brush, changing the TileMode, setting the stretch to Uniform, changing AlignmentX and AlignmentY... nothing seems to work. The origin of the brush is never set to the origin of the rectangle.
The solution I used was to render it at (0,0) with a TranslateTransform. So instead of...
dc.DrawRectangle(_brush, null, new Rect(70, 70, 100, 150));
You can use:
TranslateTransform transform = new TranslateTransform(70, 70);
transform.Freeze();
dc.PushTransform(transform);
dc.DrawRectangle(_brush, null, new Rect(0, 0, 100, 150));
dc.Pop();
This isn't ideal, since you have to allocate a new TranslateTransform
(and therefore another underlying DUCE resource) every time you draw a new rectangle, but it performs well enough in my tests.
IMPORTANT NOTE: You may still encounter some strange (but subtle) distortions on re-rendering, such as when scrolling. These might be caused by the subpixel rendering system. You can either play around with GuidelineSets or just round the start point's X and Y to the nearest integer value.
From my experience, tiling brushes is very dependent on the relation between Viewbox
, ViewboxUnits
, Viewport
and ViewportUnits
properties of the brush.
Both properties for the units should be set to Absolute
; the Viewport
and Viewbox
should be the same size (99% of the cases).
To elaborate, this is not something "official" that I've read, it's only from (a lot of) observation: the Viewbox
is the size considered for rendering the drawing. If it's not set, the Data
of the path will determine the Viewbox
size. On the other hand, Viewport
is the size considered while tiling the brush.
Setting the Viewbox
to smaller size than Viewport
will introduce spacing between each of the 'tiles' of the brush. Setting the Viewbox
larger than Viewport
will cause the 'tiles' to overlap (logically, on the screen the 'tile' will seem to be cut).
精彩评论