WPF FlowDocument will not fill Viewbox container - c#

I have a WPF usercontrol that I want to load with dynamic content in code using a FlowDocument: textblocks, bulleted lists, etc. Content is loaded as expected. And the box sizes looks fine in designer, but in runtime the FlowDocument insists on clipping the result and only show part of the width.
My assumption was that the Viewbox would fill the grid cell and the inner FlowDocument to fill up all available space.
I tried to set the PageWidth/PageHeigth of the FlowDocument in code but with no result.
Any help appreciated.
<Grid x:Name="grdDisplay">
<Grid.RowDefinitions>
<RowDefinition Height="22*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Viewbox x:Name="viewboxContent" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Left" VerticalAlignment="Top" StretchDirection="DownOnly">
<FlowDocumentScrollViewer VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden">
<FlowDocument x:Name="flowDocument">
</FlowDocument>
</FlowDocumentScrollViewer>
</Viewbox>
<Viewbox Grid.Row="1" Grid.Column="0" VerticalAlignment="Bottom" HorizontalAlignment="Left" StretchDirection="DownOnly">
<TextBlock x:Name="txtBottom" />
</Viewbox>
</Grid>

I finally got it to work!!
The problem lies in the fact that Viewbox scaling is based on the design time size of the child object. Even if the actual size of the internal child objects are bigger, they will still report their design time size also to the ActualWidth and ActualHeight properties. This is kind of confusing.
The workaround is to create the whole content including the FlowDocumentScrollViewer and its FlowDocument in runtime. They will then be based on the current available space and will fill the content as expected.
In my case the purpose was to make a simple markdown parser that will generate dynamic layout. This is now implemented and working fine :)

Related

WPF - GridRows Spacing Confusion

I have an Expander control, and the grid inside will have a ListBox with a Label on top of it saying 'Video Sources'. I am attempting to use Grid Row Definitions to achieve this. My issue however is that the grid rows separate everything evenly. I want the label to be directly on top of the ListBox. Removing the definitons causes the ListBox to fill up the entire grid including covering up the Label (which makes no sense to me as the label is on top).
My current code is below:
<Expander HorizontalAlignment="Left" Height="434" Header="Expander" ExpandDirection="Left" Margin="651,8,0,8">
<Grid Background="#FF252525" ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Label Content="Video Sources" Grid.Row="0"/>
<ListBox Grid.Row="1" d:ItemsSource="{d:SampleData}">
</ListBox>
</Grid>
</Expander>
The code produces this result. You can see there are even gaps between each control. I want the video sources label right above the listbox:
It would be nice if you could set the column name like in a ListView, however as far as I am aware that is not possible. I don't think it's worth using a ListView for something that will only have a single column, either
You have to set the rows height ; to auto (ie: minimal value) and * (ie: remaining space).
Also only two rows definition are needed.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Grid.Row="0"
Content="Video Sources" />
<ListBox Grid.Row="1"
ItemsSource="{d:SampleData}"
VerticalAlignement="Top" />
</Grid>

XAML How to arrange an element after a centered one

I want to create a button that will display some text together with a status icon inside. The text should be centered and the status icon should go right next to the text. Something like this:
I didn't find a way how to center only the text and then positioning the icon after the centered text. Right now I am using a Grid solution which centers the text and aligns the icon to the right.
<Button VerticalContentAligment="Stretch" HorizontalContentAligment="Stretch">
<Grid>
<TextBlock VerticalAligment="Center" HorizontalAligment="Center"/>
<Image VerticalAligment="Center" HorizontalAligment="Right"/>
</Grid>
</Button>
It's not what I want but it's working for the button's size I have at the moment.
I know that this can be accomplished using some binding magic but it seems too simple for this. It will be great if you can point me to a solution without binding magic but I will grateful even for one with it.
A Grid with 3 columns should work. When the left and right columns have equal width, the middle one is automatically centered.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1" VerticalAlignment="Center"/>
<Image Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Left"/>
</Grid>

Left HorizontalAlignment on a Grid

I'm trying to use HorizontalAlignment="Left" in the following situation:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Grid HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Background="Gray" Text="Small Text" TextWrapping="Wrap"/>
<TextBlock Grid.Column="1" Background="White" Text="This is a very large amount of text" TextWrapping="Wrap"/>
<TextBlock Grid.Column="2" Background="Gray" Text="Medium amount of text" TextWrapping="Wrap"/>
</Grid>
</Window>
My goal is to be able to resize the window, and have the three TextBlocks resize themselves proportionally. This works, but the grid is putting some blank space to the right of the final column, and as I try to resize towards the final column, the columns start to shrink. I want this shrinking behavior, but I don't want it to start until there is no more white space to the right of the rightmost column.
I can't use a UniformGrid as the text lengths can vary, and no other built-in WPF control that I've seen has the ability to resize all children when the parent size changes. I've looked into creating a custom panel, but that seems to be more trouble than it's worth. I feel like something much more simple can be done here.
Any suggestions or ideas are appreciated.
You'll have to build your own custom panel, and handle the case where the AvailableWidth is less then the panel's children DesiredWidth
Panel layout in WPF (Grid is a Panel) is a 2 step process, in the first pass the Panel iterates over its children and provides them with the panel's AvailableWidth. The children respond to this by computing their DesiredWidth.
In the second pass the Panel arranges the children according to their DesiredWidth. In this second pass you have access to the width that all the children (in your case, TextBlocks) require. If it is less than the panel's available width you can compute a percentage to give each one so that they appear to shrink uniformly.
Here's a resource that shows how you can create your own custom panel
What about this?
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>

ScrollViewer slow performance with DataGrid

I have the following scenario:
<ScrollViewer>
<Grid>
<!--many other controls-->
<DataGrid />
</Grid>
</ScrollViewer>
Now, when I bind DataGrid to large amount of data (around 10.000 rows) I am having very slow perfomance. In fact, i get OutOfmemory exception (and I have 8 GB memory)! I read somewhere that this is because ScrollViewer overrides DataGrid virtualisation (or something like that), but I don't know how to prevent that. If I remove the ScrollViewer, problem solved! The data loads in less than a second.
I want to keep the ScrollViewer (because of other controls) and have good performance. Is that possible? If not, is there any other solution-workaround?
A common workaround to these sorts of problems is to add an invisible "sizing element" in the same Row as the DataGrid, then you can bind DataGrid.Height to the ActualHeight of the sizing element. This way, your DataGrid will always consume the Height of the RowDefinition. Example
<ScrollViewer>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Button Content="Some Control.." />
<Rectangle Name="sizingElement"
Grid.Row="1"
Fill="Transparent"
Margin="1"/>
<DataGrid Grid.Row="1"
Height="{Binding ElementName=sizingElement,
Path=ActualHeight, FallbackValue=1}">
<!--...-->
</DataGrid>
<Button Content="Some more controls etc.." Grid.Row="2"/>
</Grid>
</ScrollViewer>
The outer ScrollViewer effectively gives the DataGrid as much space as it likes, that way its height becomes huge, showing all rows at once. Just restrict the DataGrid by explicitly setting a height on it for example.

Handling Different Font Sizes Due To Display Properties in a WPF App?

My group is building an editor-type app in WPF. One thing we noticed is that on my WinXP machine, running with the "windows classic style" theme, the text on buttons is fits fine. However on my friend's machine, who's running with the "windows xp style" theme, the font size is bigger so text on buttons get clipped at the bottom.
Is there a way to handle this nicely, like automatically resizing controls to fit the text?
I hesitate to manually resize the button to fit his layout as anyone else can have totally different settings through the Display Properties and Accessibility Options.
Thanks!
A WPF button will automatically resize to fit the content that it has been given, however it will only do this when it is inside a container that does not enforce size and its size has not been set manually. To prove this mess around with the font size in the following code snippet:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button
Grid.Column="1"
Grid.Row="1"
FontSize="24"
Content="QWERTY"/>
</Grid>
I guess that your buttons haven't resized because you have constrained them. To fix this you need to decide how you want them to resize (which can be very complicated when elements would overlap if they just grew blindly) and if none of the supplied panel types perform the growth behaviour that you are looking for then you may need to write your own that does.
Have you hardcoded element sizes using Width and Height properties? In WPF the recommended way to do this is to use the several layout containers.
The following is an example of a grid which lays two buttons at the bottom and a textbox at the top.
<Grid>
<Grid.RowDefinitions>
<!-- TextBox row with unspecified height. -->
<RowDefinition Height="*"/>
<!-- Button row with automated height so it resizes to
fit the content -->
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- Textbox on first row. -->
<TextBox Margin="3" Name="textBox1" Grid.Row="0" AcceptsReturn="True" />
<!-- StackPanel which lays the two buttons at the bottom horizontally.
RightToLeft is specified so that the first button appears on right.
-->
<StackPanel Grid.Row="1" HorizontalAlignment="Right"
Orientation="Horizontal" FlowDirection="RightToLeft">
<!-- The buttons. Only padding and margin are hardcoded so these
can resize to the contents -->
<Button Padding="3" Margin="3">OK</Button>
<Button Padding="3" Margin="3">Cancel</Button>
</StackPanel>
</Grid>

Categories