How to efficiently stream text to screen in WPF? - wpf

I want to stream a bunch of text to display the status/progress of a long running task (such as the output window in Visual Studio).
Currently I have something like this XAML:
<ScrollViewer Canvas.Left="12" Canvas.Top="12" Height="129" Name="scrollViewer1" Width="678">
<TextBlock Name="text" TextWrapping="Wrap"></TextBlock>
</ScrollViewer>
and this code behind:
private void Update(string content)
{
text.Text += content + "\n";
scrollViewer1.ScrollToBottom();
}
After a while, it gets really slow.
Is there a recommended way of doing this type of thing? Am I using the right kinds of controls?
Thanks!

At a minimum, you'll want to use a readonly TextBox and use the AppendText() method to append text.
Of course, you're still not immune from performance problems if you have sufficient volumes of text. That being the case, you might need to look into virtualization (both data and UI) solution.

Related

Large TextBlock works much faster when placed inside a ViewBox

Accidentialy found a strange behaviour. A large TextBlock, containing 100k lines, is rendered very slow, resizing and scrolling it takes about a second. But if this TextBlock is placed within a ViewBox with Stretch="None" it is scrolled and resized quite fast.
Something in background is definitely changed with a ViewBox, but what and why?
Window contents
<Grid>
<ScrollViewer>
<Viewbox Stretch="None">
<TextBlock x:Name="TextContainer" HorizontalAlignment="Left" TextAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Center"/>
</Viewbox>
</ScrollViewer>
</Grid>
Code behind, just creating some text
public MainWindow()
{
InitializeComponent();
StringBuilder sb = new StringBuilder();
for(int i = 1; i < 100000; i++)
{
sb.AppendLine($"Line #{i} ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ");
}
this.TextContainer.Text = sb.ToString();
}
Presence of a ScrollView have no effect it is still slow without a ViewBox and fast with it. If I change TextBlock to a TextBox with the same content performance becomes fast in all cases. So it is TextBox specific.
Little update: Clarification. I do not need to display large text amounts with a TextBlock, TextBox or [insert whatever you want]. All I want, is to understand, why it behaves like this? Perhaps this knowledge will help me to find solutions to later quiestions, avoid some troubles or just quench my thirst for knowledge. While I appreciate advices on performance optimizations, it does not answer the question.
When you use a ViewBox, the layout and the rendering is done only once, then, it's only an image that is clipped by the ScrollViewer.
Without the ViewBox, the layout is recalculated at each change (scroll, resize, ...). Which causes a slowdown. Maybe it's possible do disable some virtualization but I'm not sure.

WPF take screenshot of application including hidden content

Under the category "limitations of the technology":
I have received the requirement to have a screenshot button in my application that will take a screenshot and launch a printer dialog. Fair enough. My code achieves that. I simply take my window, and use a RenderTargetBitmap to render the window.
However, the requirement now states that it should include all content that is hidden behind scrollbars. Meaning, that in the screenshot the application should look "stretched" in order to eliminate scrollbars, and show all data. For instance in case there is a large list or datagrid, all the data should be visible.
Keeping in mind that WPF might be virtualizing and not rendering things that are not in view, is there any way I can achieve this requirement? Is there a possibility of rendering the visual tree to a seperate infinite space and taking a screenshot there? Something else?
In response to comments:
The screenshot button is on an outer shell that only holds the menu. Inside this shell any of 800+ views can be hosted. These views could contain datagrids, lists, large forms consisting of textboxes... anything. There is no way to tell what is 'inside' without walking the visual tree.
The functionality requested is similar to printing a webpage in your browser to PDF. It will also give you the entire DOM instead of just what you see in the limited view of the browser.
XAML:
<Grid>
<Button
x:Name="btnPrint"
Width="50"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Click="BtnPrint_Click"
Content="Print" />
<ScrollViewer Height="500" HorizontalAlignment="Center">
<Grid x:Name="toPrint">
<!--your code goes here-->
</Grid>
</ScrollViewer>
</Grid>
C#:
private void BtnPrint_Click(object sender, RoutedEventArgs e)
{
var pdialog = new PrintDialog();
if (pdialog.ShowDialog() == true)
{
System.Windows.Size pageSize = new System.Windows.Size { Height = pdialog.PrintableAreaHeight, Width = pdialog.PrintableAreaWidth };
toPrint.Measure(pageSize);
toPrint.UpdateLayout();
pdialog.PrintVisual(toPrint, "Print");
}
}

WPF text control with colors only

I need a text control where the user is able to edit text and where some parts of the text can have different colors based on the text. Basically, imagine Visual Studio source file editor or any other source file editor which colors the source code. What WPF control is that? None of the three options in WPF I am aware of are not suitable:
TextBox doesn't allow colors
TextBlock doesn't allow users to edit the text
RichTextBox allows too much - I just want colors.
Maybe RichTextBox can have fixed other text formattings (i.e. fonts, bold, italic)? Any thoughts?
Here is a (very) rough example keeping stick with TextBox and TextBlock: just for fun, but worthwhile...
Here is the XAML...
<Grid>
<TextBlock
x:Name="Tx1"
HorizontalAlignment="{Binding Path=HorizontalAlignment, ElementName=Tb1}"
VerticalAlignment="{Binding Path=VerticalAlignment, ElementName=Tb1}"
Margin="{Binding Path=Margin, ElementName=Tb1}"
FontSize="{Binding Path=FontSize, ElementName=Tb1}"
/>
<TextBox
x:Name="Tb1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Margin="100,0"
FontSize="24"
Background="Transparent"
Foreground="Transparent"
TextChanged="Tb1_TextChanged"
/>
</Grid>
...and here is some code...
private void Tb1_TextChanged(object sender, TextChangedEventArgs e)
{
var inlines = this.Tx1.Inlines;
inlines.Clear();
foreach (char ch in this.Tb1.Text)
{
if (Char.IsDigit(ch))
{
var run = new Run(ch.ToString());
run.Foreground = Brushes.Blue;
inlines.Add(run);
}
else if (Char.IsLetter(ch))
{
var run = new Run(ch.ToString());
run.Foreground = Brushes.Red;
inlines.Add(run);
}
else
{
var run = new Run(ch.ToString());
run.Foreground = Brushes.LimeGreen;
inlines.Add(run);
}
}
}
The trick is using a transparent TextBox over a TextBlock, which can be colored by collecting many different Run elements.
I think your best choice is looking for 3rd Party controls, as suggested by Erno.
For example: wpfsyntax
WPF Syntax Highlight Textbox is very simple control for editing source code. Contains line numbering, world highlighting, indenting (tab, shift+tab....) and more. Test application contains parser for language IronPython and Boo. Syntax Highlight Textbox is developed in C#.
Or: Fast colored textblox
For one of my projects, I have felt the need of a text editor with syntax highlighting. At first, I used a component inherited from RichTextBox, but while using it for a large amount of text I found out that RichTextBox highlights very slowly a large number of colored fragments (from 200 and more). When such highlighting has to be made in a dynamic way, it causes a serious problem.
Therefore I created my own text component which uses neither Windows TextBox nor RichTextBox.
This Feature is not in any of the text controls. May be you can write your own custom control by inheriting RichTextBox.

Disable ScrollViewer VerticalScrollBarVisible if content fits

I am currently writing my very first Windows Phone (8) App which also is my very first Xaml Application. So it is likely I just did not find the solution for my problem on my own, because I don't know which words to feed google. I tried, but found nothing useful. I found that one, but it does not help:
How to disable "scroll compression" in ScrollViewer
Here is the important part of my XAML:
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel VerticalAlignment="Top">
<TextBlock x:Name="InfoText" TextWrapping="Wrap" VerticalAlignment="Top" Text="VersionInfoText"/>
</StackPanel>
</ScrollViewer>
I will programmatically change the content of my TextBlock InfoText. The text might be short enough to fit in completely, or it might be rather long. That is why I embedded it into a ScrollViewer. (By the way, there will be further Controls added to the StackPanel later.)
The ScrollViewer produces these "overbounce" effects if it cannot scroll any further. That is nice if the text is large, but when there is nothing to scroll I don't want this effect to be visilbe.
I tried VerticelScrollBarVisibility="Disable", which successfully disables the effect. Now my question:
Can I automatically (by XAML-Magic) switch between Auto and Disable depending on the Height of my StackPanel and the Hight of my ScrollViewer?
I was hoping Auto would do the trick, but it does not (tested in the VS2013 Emulator WVGA).
In VS2013 setting VerticalScrollBarVisibility="Auto" worked for me.
Try adding this attribute to your ScrollViewer
VerticalScrollMode="Auto"
Also try disabling the HorizontalScrollMode and HorizontalScrollBarVisiblity attributes.
Let me know if this doesn't work. I will then have to make a sample app to see if I can make that work for you. Right now I am just guessing. Try it.
You could dynamically set the SetVerticalScrollBarVisibility to Disabled depends on your InfoText length in your cs code...
if(InfoText.Length() >n)
{
ScrollViewer.SetVerticalScrollBarVisibility(scrollViewer, ScrollBarVisibility.Auto);
}
else
{
ScrollViewer.SetVerticalScrollBarVisibility(scrollViewer, ScrollBarVisibility.Disabled);
}
You can check if TextBlock height is greater than the height of the ScrollViewer.
In xaml:
<ScrollViewer x:Name="TestScrollViewer">
<TextBlock x:Name="InfoText"
Text="Information"
TextWrapping="Wrap"
VerticalAlignment="Top" />
</ScrollViewer>
In cs:
public MainPage()
{
InitializeComponent();
Loaded += (sender, args) =>
{
TestScrollViewer.IsEnabled = InfoText.ActualHeight > TestScrollViewer.ActualHeight;
// OR
TestScrollViewer.VerticalScrollBarVisibility = InfoText.ActualHeight > TestScrollViewer.ActualHeight
? ScrollBarVisibility.Visible
: ScrollBarVisibility.Disabled;
};
}

WPF Windows to Tabs

I have an already made WPF application which is Windowed based and now I wish to implement tabbed controls instead of each of those separate windows. What would be the easiest and fastest way to reuse?
Thanks
Typically when I want to convert, I take the contents of each Window (which is usually wrapped in a Grid) and I convert them to a UserControl. You can basically move the entire Xaml and code-behind almost as-is, with only minor tweaks.
You then replace your Window contents with the UserControl, and you can reuse the same UserControl in a tab, or anywhere else.
Or you could convert you Windows to Pages and create frames in the tabs and put the page in the frame. It might be easier convert you Windows to Pages (and it might not) - depends on your Windows. I like the UserControl answer. Just putting another possilble option out there.
<TabItem>
<TabItem.Header>
<TextBlock Style="{StaticResource TabItemHeader}">DocTxt</TextBlock>
</TabItem.Header>
<Frame Source="PageViewDocText.xaml" BorderThickness="0" Margin="0"/>
</TabItem>
Transfort all your windows into UserControl and build your new window with your TabControl.

Resources