简体   繁体   中英

How to add a XAML Resource (Grid with ItemsControls) to a FlowDocument in Code-Behind multiple times

I am working on a WPF Window that presents the results of some technical calculations inside a FlowDocumentViewer.

Problem: The FlowDocument and all its content is created from Code-Behind because every calculation differs a bit in terms of headers, shown results, and lines. I use different BlockUIContainers which hold a Resource Grid with some ItemsControls to show the results in a formatted order and add it to a Section and then to the Blocks of the Document, but only the last Block is shown inside the Reader. I don't understand why after using FlowDoc.Blocks.Add(section) multiple times, only the last Block is shown.

I have created a Resource inside the XAML Code to fill it from Code Behind with the results. I need the output to look like this, eg:


      M,ed                                =       70 kNm
      Q,ed                                =       25 kN
      N,ed                                =       30 kN
      ...
   

To achieve formatting like this, I created a grid with three columns, each containing an ItemsControl with a DataTemplate TextBlock, which has a Binding to a List<> of results.

 <Window x:Class="FaceplateInput.Output" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:FaceplateInput" mc:Ignorable="d" x:Name="OutputWnd" WindowStartupLocation="CenterScreen" Title="Output" Height="1000" Width="700"> <Window.Resources> <Grid x:Key ="TestGrid" x:Name="Grid"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="*"/> </Grid.RowDefinitions> <ItemsControl x:Name="ItemsLeft" Grid.Column="0"> <ItemsControl.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}"/> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> <ItemsControl x:Name="ItemsMid" Grid.Column="1"> <ItemsControl.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}"/> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> <ItemsControl x:Name="ItemsRight" Grid.Column="2"> <ItemsControl.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}"/> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid> </Window.Resources>... <FlowDocumentScrollViewer x:Name="DocReader" Grid.Column="1" Grid.Row="2" Margin="20" MaxWidth="700"> </FlowDocumentScrollViewer> </Grid> </Window>

I want to create the FlowDocument in Code-Behind to have control over formatting, especially because the document is created from a single string that is returned by the calculating class.
I build the following test-method to see how FlowDocuments work. The result is that only the added paragraphs and the last section are shown in the output window. Code-Behind:

namespace FaceplateInput
{
    public partial class Output: Window
    {
        public string PathToImage;
        public string PathToBackground;
        public string PathToTXTFile;
        public Output()
        {
            InitializeComponent();
            CreateFlowDocument();
        }

        private void CreateFlowDocument()
        {
            FlowDocument FlowDoc = new FlowDocument();
            
            List<string> leftStr = new List<string>();
            List<string> midStr = new List<string>();
            List<string> rightStr = new List<string>();
            //for Testing purposes, i create some weired data to fill the Lists
            for (int i = 0; i < 10; i++)
            {
                leftStr.Add($"LeftLine {i + 1}");
                midStr.Add("=");
                rightStr.Add($"RightLine {i + 1}");
            }

            //Creating a new Container with Resource Grid as UIElement:
            BlockUIContainer cont = new BlockUIContainer((UIElement)this.FindResource("TestGrid"));
            Grid child = (Grid)cont.Child;
            //setting the sources for the ItemsControl:
            ItemsControl items1 = (ItemsControl)child.Children[0];
            items1.ItemsSource = leftStr;
            ItemsControl items2 = (ItemsControl)child.Children[1];
            items2.ItemsSource = midStr;
            ItemsControl items3 = (ItemsControl)child.Children[2];
            items3.ItemsSource = rightStr;
            
            //adding section holding BlockUIContainer to Document  
            Section section = new Section();
            section.Blocks.Add(cont);
            FlowDoc.Blocks.Add(section);
        
            //disconnecting UIContainer from parent to avoid Exception
            section.RemoveChild(cont.Child);
            cont.Child = null;
            
            //another "testline" to see where it puts it in the document
            FlowDoc.Blocks.Add(new Paragraph(new Run("TestString 1\n")));
            
            //all the stuff above again to test
            leftStr.Clear();
            midStr.Clear();
            rightStr.Clear();
            leftStr.Add("______");
            midStr.Add("_____________________");
            rightStr.Add("_____________________");
            for (int i = 0; i < 20; i++)
            {
                leftStr.Add($"Left: {(double)i * 324 / 10}\n");
                midStr.Add("=\n");
                rightStr.Add($"{(double)i * 13 / 2}\n");
            }

            BlockUIContainer cont1 = new BlockUIContainer((UIElement)this.FindResource("TestGrid"));
            Grid child1 = (Grid)cont1.Child;
            items1 = (ItemsControl)child1.Children[0];
            items1.ItemsSource = leftStr;
            items2 = (ItemsControl)child1.Children[1];
            items2.ItemsSource = midStr;
            items3 = (ItemsControl)child1.Children[2];
            items3.ItemsSource = rightStr;

            Section section1 = new Section();
            section1.Blocks.Add(cont1);
            FlowDoc.Blocks.Add(new Paragraph(new Run("TestString 2\n")));
            FlowDoc.Blocks.Add(section1);

            FlowDoc.Blocks.Add(new Paragraph(new Run("TestString 3\n")));
            
            DocReader.Document = FlowDoc;
        }
        //...
    }
}

I'd appreciate any tips on how to get this working, and maybe how to improve the code itself, or any ideas for completely different approaches. Greets and thx, Crawliiee

You are setting the first container to null, so that first section is empty. Remove this line:

cont.Child = null;

An alternative could be to use a ListView with GroupView as in the example

https://docs.microsoft.com/en-us/dotnet/api/system.windows.controls.listview?view=netframework-4.7.2&f1url=%3FappId%3DDev16IDEF1%26l%3DEN-US%26k%3Dk(System.Windows.Controls.ListView);k(TargetFrameworkMoniker-.NETFramework,Version%253Dv4.7.2);k(DevLang-csharp)%26rd%3Dtrue

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM