Я думаю, что ScrollViewer предполагает, что источник будет в (0, 0) - в конце концов, нет никакого способа сказать это иначе; Canvas - единственная панель, которая знает об указанном и ненулевом происхождении.
Простым вариантом может быть забыть о ScrollViewer и реализовать свои собственные функции панорамирования - возможно, с помощью перетаскивания, или, возможно, с помощью большой кнопки «прокрутка влево» с левой стороны вида, кнопки «прокрутка вправо» справа и т. д. - и реализовать его с точки зрения преобразования содержимого.
Но если вам нужно придерживаться классической метафоры полосы прокрутки, я думаю, что вы можете создать свою собственную панель (или переопределить ArrangeOverride тоже, что равноценно) и сместить все позиции - сделать их на основе нуля. Поэтому, если у вас есть элемент в (20, -20), а другой в (0, 5), вам нужно будет сместить все вниз на 20, чтобы он поместился в пространстве с нулями; и когда вы раскладываете своих детей, первый будет в (20, 0), а второй в (0, 25).
Но сейчас прокрутка странная. Если пользователь прокручивается до самого дна и перетаскивает что-то за нижний край, вид остается на месте; то же самое с правом. Но если они прокручиваются до самого верха и перетаскивают что-то за верхний край, внезапно все прыгает вниз, потому что VerticalOffset ScrollViewer был нулевым и все еще нулевым, но ноль означает что-то другое из-за смещения вашего макета.
Возможно, вы сможете обойти странность прокрутки, связав HorizontalOffset и VerticalOffset. Если ваша ViewModel автоматически исправила эти свойства (и запустила PropertyChanged ) всякий раз, когда элемент управления перемещается «отрицательно», то вы могли бы сохранить прокрутку представления до того же логического содержимого, даже если вы сейчас говорите Движок макета WPF, что все где-то еще. То есть, ваша ViewModel будет отслеживать позицию логической прокрутки (ноль, прежде чем перетаскивать отрицательный элемент), и будет сообщать позицию прокрутки на основе нуля в ScrollViewer (поэтому после перетаскивания вы должны сказать ScrollViewer, что его VerticalOffset теперь 20 ).