Действительно интересная проблема - вот что я понял: эффект, который вы испытываете, кажется, определяется концепцией / поведением Viewport
TileBrush
(см. Viewbox
тоже для полной картины). Очевидно, на неявную ограничивающую рамку FrameworkElement
(т. Е. Canvas в вашем случае) влияют / расширяются элементы, незаметно торчащие из границ, то есть размеры окна расширяются, но система координат окна не масштабируется, скорее расширяется также в направлении за пределы.
Возможно, будет проще проиллюстрировать это графически, но из-за нехватки времени я сначала предложу решение и объясню шаги, которые я предпринял на данный момент, чтобы начать работу:
Решение:
<Border Background="LightBlue" Width="198" Height="198">
<Border.OpacityMask>
<DrawingBrush Stretch="None" AlignmentX="Center" AlignmentY="Center"
Viewport="-10,0,222,202" ViewportUnits="Absolute">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="#30000000">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="-10,0,220,200" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="Black">...</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Border.OpacityMask>
<Canvas x:Name="myGrid">...</Canvas>
</Border>
Обратите внимание, что я скорректировал единицы измерения на +/- 2 пикселя здесь и там для точности пикселей, не зная, где происходит смещение, но я думаю, что это может быть проигнорировано в целях примера и разрешено позже, если потребуется.
Объяснение
Чтобы упростить иллюстрацию, сначала нужно сделать явными все связанные подразумеваемые / автоматические свойства.
Внутренняя граница получает автоматические размеры 198 от внешней границы (заполнение 240 - 20 - 2 пикселя, выведенных экспериментом; не знаю их происхождение, но игнорируется прямо сейчас), то есть если вы укажете это следующим образом, ничего не должно изменение, при использовании других значений выдает графические изменения:
<Border Background="LightBlue" Width="198" Height="198">...</Border>
Далее подразумевается значение по умолчанию Viewport
и ViewportUnits
примерно так:
<DrawingBrush Stretch="None" AlignmentX="Left" AlignmentY="Top"
Viewport="0,0,1,1" ViewportUnits="RelativeToBoundingBox">...</DrawingBrush>
Вы устанавливаете размер DrawingBrush, переопределяя Stretch
с None
, сохраняя положение и размер базовой плитки по умолчанию и относительно ее ограничительной рамки . Кроме того, вы (по понятным причинам) переопределяете AlignmentX
/ AlignmentY
, которые определяют размещение внутри базовой плитки, которая находится внутри ее ограничительной рамки. Сброс их к значениям по умолчанию Center
уже говорит: маска сместится соответственно, означая, что должна быть меньше, чем ограничивающая рамка , иначе их не будет в центре.
Это можно сделать, изменив ViewportUnits
на Absolute
, что не даст графику вообще, пока единицы не будут правильно отрегулированы; опять же, экспериментально следующие явные значения совпадают с автоматическими, а использование других значений приводит к графическим изменениям:
<DrawingBrush Stretch="None" AlignmentX="Center" AlignmentY="Center"
Viewport="0,0,202,202" ViewportUnits="Absolute">...</DrawingBrush>
Теперь маска непрозрачности уже правильно совмещается с элементом управления . Очевидно, что осталась одна проблема, поскольку маска теперь обрезает линию, что неудивительно, учитывая ее размер и отсутствие какого-либо эффекта Stretch
. Регулировка его размера и положения соответственно решает это:
<RectangleGeometry Rect="-10,0,220,200" />
и
<DrawingBrush Stretch="None" AlignmentX="Center" AlignmentY="Center"
Viewport="-10,0,222,202" ViewportUnits="Absolute">...</DrawingBrush>
Наконец, маска непрозрачности соответствует контрольным границам по желанию!
Дополнение:
Требуемые смещения, определенные с помощью дедукции и эксперимента в приведенном выше объяснении, могут быть получены во время выполнения с помощью VisualTreeHelper Class
:
Rect descendantBounds = VisualTreeHelper.GetDescendantBounds(myGrid);
В зависимости от состава и потребностей вашего визуального элемента вам может потребоваться указать LayoutInformation Class
и создать объединение обоих, чтобы получить всеобъемлющий ограничивающий прямоугольник:
Rect descendantBounds = VisualTreeHelper.GetDescendantBounds(myGrid);
Rect layoutSlot = LayoutInformation.GetLayoutSlot(myGrid);
Rect boundingBox = descendantBounds;
boundingBox.Union(layoutSlot);
См. Следующие ссылки для получения дополнительной информации по обеим темам: