#wpf #richtextbox #flowdocument
#wpf #richtextbox #потоковый документ
Вопрос:
У меня есть пользовательский класс WPF, подкласс RickTextBox, поэтому я могу добавлять встроенные дисплеи для пользовательских объектов данных. Иногда мне нужно все очистить и перестроить документ из списка классов. Однако, когда я это делаю, я получаю странную ошибку:
doc.Blocks.Clear(); // <-- Error at this line
--------------------------------------------------------------
Additional information: Cannot serialize a generic type System.Collections.ObjectModel.ObservableCollection [System.Collections.ObjectModel.ObservableCollection ...
Я действительно понятия не имею, что может быть причиной этого. Я не выполняю никакой явной сериализации, поэтому что-то внутри WPF пытается. Кто-нибудь знает, что происходит?
К вашему сведению, вот настраиваемое встроенное отображение для моего объекта (не волнуйтесь, работа с кодом в конечном итоге перейдет на XAML). Я сузил проблему до CellVMs
, что является IEnumerable<IEnumerable<ReportTableCellViewModel>>
public class ReportTableRun : InlineUIContainer
{
public ReportTableRun() : this(null)
{
}
public ReportTableRun(ReportTableViewModel table, DataTemplate cellTemplate=null) : base()
{
mTable = table;
DynamicGrid grid = new DynamicGrid();
grid.DefaultRowHeight = new GridLength(50);
grid.DefaultColumnWidth = new GridLength(50);
Binding bind0 = new Binding("CellVMs");
bind0.Mode = BindingMode.OneWay;
bind0.Source = mTable;
BindingOperations.SetBinding(grid, DynamicGrid.ItemsSourceProperty, bind0);
Binding bind3 = new Binding("RowSharedGroupNames");
bind3.Mode = BindingMode.OneWay;
bind3.Source = mTable;
BindingOperations.SetBinding(grid, DynamicGrid.RowSharedSizeGroupNamesProperty, bind3);
Binding bind4 = new Binding("ColumnSharedGroupNames");
bind4.Mode = BindingMode.OneWay;
bind4.Source = mTable;
BindingOperations.SetBinding(grid, DynamicGrid.ColumnSharedSizeGroupNamesProperty, bind4);
grid.ItemTemplate = cellTemplate;
this.Child = grid;
}
private ReportTableViewModel mTable;
}
и вот код для перестроения документа.
private void BuildDocument()
{
FlowDocument doc = Document;
IEnumerable<IReportComponentViewModel> components = ReportTemplateViewModel.ComponentVMs;
List<Paragraph> parList = new List<Paragraph>();
Paragraph par = new Paragraph();
parList.Add(par);
foreach(IReportComponentViewModel c in components)
{
if (c is ReportTableViewModel)
par.Inlines.Add(new ReportTableRun(c as ReportTableViewModel, ReportTableCellTemplate));
}
doc.Blocks.Clear();
foreach(Paragraph p in parList)
doc.Blocks.Add(p);
}
Комментарии:
1. У вас есть пример проекта, который я могу воспроизвести?
2. Возможно, я мог бы попытаться собрать что-нибудь вместе, когда вернусь к работе на следующей неделе. В итоге я решил обойти проблему, не используя ObservableCollection непосредственно в моих классах. Я явно создал отдельную специализированную ObservableCollection, используя шаблон, например, класс MyObservableCollection : ObservableCollection<MyObject>
Ответ №1:
У меня была та же проблема, не удается очистить BlockCollection
, потому что используйте потоковый документ в обоих местах: печать и предварительный просмотр. Run
‘s и другие элементы atom должны использоваться один раз (с одним родителем).
Итак, это решается путем копирования встроенных элементов перед вторым добавлением:
Inline CopyInline(Inline iln)
{
string strType;
Inline ilnRes;
strType = (string)SpIntConst.GetLastElement(iln.GetType().ToString().Split('.'));
switch (strType)
{
case "Run":
ilnRes = new Run(((Run)iln).Text) { Style = iln.Style };
break;
case "Bold":
Inline newInl;
Bold boldSend;
boldSend = iln as Bold;
newInl = CopyInline(boldSend.Inlines.ElementAt(0));
ilnRes = new Bold(newInl);
break;
case "InlineUIContainer":
ilnRes = new InlineUIContainer(_gGraph.GraphImage);
break;
default:
ilnRes = null;
break;
}
return ilnRes;
}
Paragraph CopyParagraph(Paragraph par)
{
Paragraph parRes = new Paragraph();
foreach (Inline iln in par.Inlines)
parRes.Inlines.Add(CopyInline(iln));
return parRes;
}
TableCell CopyTableCell(TableCell tblCell)
{
TableCell tblCellRes = new TableCell() { Style = tblCell.Style };
return tblCellRes;
}
TableRow CopyTableRow(TableRow tr)
{
TableRow trRes = new TableRow();
foreach (TableCell tblCell in tr.Cells)
trRes.Cells.Add(CopyTableCell(tblCell));
return trRes;
}
TableRowGroup CopyTableRowGroup(TableRowGroup trg)
{
TableRowGroup trgRes = new TableRowGroup();
foreach (TableRow tr in trg.Rows)
trgRes.Rows.Add(CopyTableRow(tr));
return trgRes;
}
Table CopyTable(Table tbl)
{
Table tblRes = new Table();
for (int i = 0; i < tbl.Columns.Count; i )
tblRes.Columns.Add(new TableColumn() { Width = tbl.Columns[i].Width });
foreach (TableRowGroup trg in tbl.RowGroups)
tblRes.RowGroups.Add(CopyTableRowGroup(trg));
return tblRes;
}
Block CopyBlock(Block bl)
{
string type;
Block blRes;
type = (string)SpIntConst.GetLastElement(bl.GetType().ToString().Split('.'));
switch (type)
{
case "Paragraph":
Paragraph par = bl as Paragraph;
blRes = CopyParagraph(par);
break;
case "Table":
Table tbl = bl as Table;
blRes = CopyTable(tbl);
break;
default:
blRes = null;
break;
}
return blRes;
}
FlowDocument CopyDoc(FlowDocument fd)
{
FlowDocument fdRes = new FlowDocument()
{
PageWidth = fd.PageWidth,
PageHeight = fd.PageHeight,
PagePadding = fd.PagePadding
};
foreach (Block bl in fd.Blocks)
fdRes.Blocks.Add(CopyBlock(bl));
return fdRes;
}
private void btnPreview_Click(object sender, RoutedEventArgs e)
{
SetParams();
PrintPreview pp = new PrintPreview(CopyDoc(_doc), _mw.CurrHelpPath);
...
}