У нас есть довольно большое бизнес-приложение WPF, и я работаю над переоснащением существующего отчета WPF FixedPage/FixedDocument.
Это несколько загруженная экосистема. У нас есть встроенный генератор форм с множеством различных элементов управления, которые вы можете использовать (подумайте, как мини-встроенная визуальная студия). Все это работает нормально. Вы заполняете форму на экране, а затем можете распечатать (в XPS) идентичную копию на стандартной бумаге 8,5x11.
В коде мы разбиваем этот отчет на вертикальные фрагменты. Скажем, каждый кусок будет иметь высоту в дюйм или два на распечатанном листе бумаги. Вот как мы обрабатываем пагинацию. Если следующий кусок слишком велик для страницы, мы делаем NewPage() и повторяем. Как я уже говорил, это работало нормально.
У WPF огромная кривая обучения, и я возвращался к старому коду и рефакторингу и с удовольствием работал с DataTemplates, строго типизированными ViewModels и универсальными ContentControls, чтобы уменьшить размер нашего кода. Генератор экранных форм по-прежнему работает, но отчет FixedDocument стал странным.
Возвращаясь к этим вертикальным срезам, мы печатаем пользовательские формы на бумаге как отдельные элементы управления Grid. Ничего фантастического. Каждая сетка (как я упоминал выше) может иметь высоту в дюйм или два и содержать любую случайную смесь флажков, радиокнопок, текстовых блоков и так далее.
Когда сетки содержали стандартные (стандартные) элементы управления MS WPF, я мог делать это целый день:
System.Windows.Controls.Grid g = .....
g.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
g.Arrange(new Rect(g.DesiredSize));
И верните правильные размеры, то есть 100 x 67.
Теперь иногда сетки имеют только один элемент управления — заголовок, если хотите (например, «Расписание на этот месяц»). Единственный дочерний элемент управления, добавленный в эту сетку, — это ContentControl.
ContentControl просто привязан к ViewModel:
<ContentControl Content="{Binding}" />
Затем в словаре ресурсов есть два DataTemplates, которые получают эту привязку. Здесь я покажу, что:
<UserControl.Resources>
<w:MarginConverter x:Key="boilerMargin" />
<DataTemplate DataType="{x:Type render:BoilerViewModel}">
<render:RtfViewer
Width="{Binding Path=Width}"
TextRTF="{Binding Path=Rtf}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type render:Qst2NodeViewModel}">
<ContentControl Content="{Binding Path=BoilerVm}">
<ContentControl.Margin>
<MultiBinding Converter="{StaticResource boilerMargin}">
<Binding Path="NodeCaptionVm.Height" />
<Binding Path="NodeLeft" />
</MultiBinding>
</ContentControl.Margin>
</ContentControl>
</DataTemplate>
</UserControl.Resources>
ContentControl выберет самый нижний шаблон данных. Затем этот шаблон, в свою очередь, будет использовать меньший из приведенных выше.
Причудливый преобразователь просто устанавливает маржу. Это может быть неудобно читать, но все это правильно отображается на экране в родительском пользовательском элементе управления. Это все правильный размер и обоснование и все такое.
Что касается печатного отчета (XPS), мне нужно создать эти элементы управления в коде и измерить их, чтобы увидеть, поместятся ли они на текущей странице FixedPage. Когда я перехожу к этому шагу: (в сетке, содержащей этот ContentControl)
g.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
g.Arrange(new Rect(g.DesiredSize));
Я получаю обратно 0,0 размер. Хотя должно быть, например, 730x27. Опять же, на экране, размещенном в UserControl, все это работает нормально. Просто попытка создать экземпляр и измерить его чисто в коде не удалась. Я подтвердил, что элемент управления добавлен в сетку, имеет свои строки и столбцы, добавлен в коллекцию Children и т. д.
Если я добавлю к этим двум операторам вызов UpdateLayout, как здесь, то это сработает:
g.UpdateLayout(); //this fixes it
g.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
g.Arrange(new Rect(g.DesiredSize));
Я читал, что UpdateLayout дорог и его следует избегать, и я бы предпочел не вызывать его в каждом разделе сетки, прежде чем добавлять его в свою FixedPage отчета FixedDocument. Итераций может быть несколько десятков, а то и сотен. И, опять же, если в сетке есть обычные элементы управления WPF, без каких-либо ContentControls и причудливого поиска и поиска шаблонов данных, измерение работает нормально без вызова UpdateLayout.
Любой совет? Спасибо!
Я просто не понимаю, почему возникла необходимость вызывать его, как только я начал использовать движок Xaml. Такое ощущение, что меня наказывают за использование расширенных функций.