Following btnTest_Click(...) event in my WPF .NET5 app successfully displays the content of a RichTextBox into a FlowDocumentReader. But, as shown in the images below, the different page viewing modes of the FlowDocumentReader create excessive amounts of whitespace on lines. Question: Why it is happening, what I may be missing here, and how can we resolve the issue?
MainWindow.xaml
<Window x:Class="Wpf_RTBFlowDocTest.MainWindow"
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:Wpf_RTBFlowDocTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<DockPanel Name="mainPanel">
<ToolBar Name="mainToolBar" Height="30" DockPanel.Dock="Top">
<Button x:Name="btnTest" Content="Test" Click="btnTest_Click"/>
</ToolBar>
<RichTextBox Name="rtbTest" AcceptsTab="True" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"/>
<FlowDocumentReader x:Name="fdReader" ScrollViewer.VerticalScrollBarVisibility="Auto" IsScrollViewEnabled="True">
<FlowDocument IsOptimalParagraphEnabled="True" IsHyphenationEnabled="True" TextAlignment="Left"></FlowDocument>
</FlowDocumentReader>
</DockPanel>
</Grid>
</Window>
MainWindow.xaml.cs
private void btnTest_Click(object sender, RoutedEventArgs e)
{
var range = new TextRange(rtbTest.Document.ContentStart, rtbTest.Document.ContentEnd);
if (!range.IsEmpty)
{
if(fdReader.Document.Blocks.Count > 0)
fdReader.Document.Blocks.Clear();
using (var stream = new MemoryStream())
{
range.Save(stream, DataFormats.XamlPackage);
var copyto = new TextRange(fdReader.Document.ContentEnd, fdReader.Document.ContentEnd);
copyto.Load(stream, DataFormats.XamlPackage);
}
}
rtbTest.Visibility = Visibility.Collapsed;
fdReader.Visibility = Visibility.Visible;
}
Original display of the app before clicking the Test button:
Single pageview display after the above code ran:
Single Scroll pageview display after above code ran:
Multiple pageview display after the above code ran:
That's exactly as expected: your source text file is including lots of NewLine characters for line feed inside the sentences and spaces used for lines content alignment:
In a flow document, the content adapts itself to fit the container, but NewLine characters inside sentences prevented the FlowDocument control to format the text correctly.
Therefore, it's necessary to make some source text processing before loading it to the FlowDocument control.
Related
I am making a video game, where each grid serves as game room. Each grid\room has a large number of objects like things that can be used, images and sound files. Until now, they all were stored in one large file. But now, I was told that this approach wastes a lot of resources.
My plan is, to store xaml code of every such grid as a separate file, then load relevant file at run time with usual code.
For now xaml looks about like this:
<Window
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"
mc:Ignorable="d" x:Name="wdwMain" x:Class="RealityIncognita.MainWindow"
Height="900" Width="1600" ResizeMode="NoResize" WindowState="Maximized"
Cursor="Cross" WindowStyle="None" Loaded="wdwMain_Loaded">
<Viewbox Stretch="Fill">
<Grid x:Name="areaContainer" HorizontalAlignment="Left" Height="900"
VerticalAlignment="Top" Width="1600">
<Grid x:Name="areaMain">
<Grid.Background>
<ImageBrush
ImageSource="Resources/Images/Interface/main_interface.jpg"/>
</Grid.Background>
<Grid x:Name="areaShowers" HorizontalAlignment="Left" Height="700"
Margin="1653,790,-1561,-590" VerticalAlignment="Top" Width="1508"
IsVisibleChanged="areaShowers_IsVisibleChanged">
Here's example - areaShowers is a grid for relevant room. Until now, it was stored in the main file, like all other grids, and when I needed, I just altered its Margin to put it upon "areaMain" - also a grid.
Now though, I want to put each room into a file, then load it when I need it, and remove it from memory when I don't.
For example I'll create an empty grid "areaGeneric" and add it and it alone to original xaml.
So, I want something like this. Can't provide any earlier attempt, because I don't really know how it can be done.
Grid new_grid = new Grid;
new_grid = load from file (areaRoom.xaml); (file is among the project's
resources)
areaGeneric = new_grid;
Can I load a grid xaml at run-time, then switch grids in main code?
Thank you,
Evgenie
A really simple solution would just be to house each of your rooms inside a user control and then place the control into your container grid when you need to change rooms. Here's the rough idea:
public partial class MainWindow : Window
{
UserControl _currentRoom;
public MainWindow()
{
InitializeComponent();
}
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
this.areaContainer.Children.Clear();
_currentRoom = null;
if (e.LeftButton == MouseButtonState.Pressed)
_currentRoom = new Room1();
if(e.RightButton == MouseButtonState.Pressed)
_currentRoom = new Room2();
this.areaContainer.Children.Add(_currentRoom);
base.OnPreviewMouseDown(e);
}
}
A "Room":
<UserControl x:Class="Test.Room1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Button Height="100" Width="100">
Hello world!
</Button>
</UserControl>
As for cleaning up your resources - once the user control is removed from the visual tree (assuming you aren't holding a reference) the GC should dispose of the user control. If you wanted to clear your resources more quickly you could implement a dispose method on your rooms and then call that before you change areas.
I have text boxes that are getting URL inside, when you put the URL (long) in it, I want it to go down one row in order to see the last character of the URL.How can I achieve it instead
of changing the width size?
The TextBlock class features the TextBlock.TextTrimming Property, which enables users to add an ellipsis (...) at the end of text that is too long to be displayed in the TextBlock. If your TextBox is not being used for text input, then you can simply use a TextBlock control instead.
If you really need to use a TextBox, then unfortunately that has no such property. One alternative is to use a custom TextBox that does have this property. You can find an example of that in the WPF TextBox With Ellipsis page on CodeProject.
UPDATE >>>
As you have not shown any code, nobody can tell you what you did wrong. Either way, this is a simple issue that I'm sure that you can fix yourself. Add this to a different view somewhere else:
<TextBlock Text="123456789012345678901234567890123456789012345678901234567890"
Width="100" TextTrimming="WordEllipsis" />
Now you should be able to see the ellipsis at the end of the TextBlock. That's how simple it is. If you example is not working, then you have made it not work by adding something else.
Try scrolling the text box to the beginning of the text when focus lost (not sure how to do that with data binding):
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
(sender as TextBox).ScrollToHome();
}
You can also create a Behavior to avoid direct event handling:
Add reference to System.Windows.Interactivity (installed with Expression Blend).
Add a Behavior class:
using System.Windows.Controls;
using System.Windows.Interactivity;
namespace WpfApplication2
{
public class AutoScrollToHomeBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
AssociatedObject.LostFocus += (tb, args) =>
{
(tb as TextBox).ScrollToHome();
};
}
}
}
Attach a Behavior to your text box:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:e="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel Orientation="Vertical">
<TextBox HorizontalAlignment="Left" Height="23" Width="120">
<e:Interaction.Behaviors>
<local:AutoScrollToHomeBehavior />
</e:Interaction.Behaviors>
</TextBox>
<TextBox HorizontalAlignment="Left" Height="23" Width="120">
<e:Interaction.Behaviors>
<local:AutoScrollToHomeBehavior />
</e:Interaction.Behaviors>
</TextBox>
</StackPanel>
</Grid>
</Window>
I have a canvas in a grid, i want to keep my canvas on that grid, because its the first window, that opens in my program.
In my MainWindow.xaml, i have a ContentPage, that always changes its content, the startup content is the authenticationPage. In this page i have a Canvas that shows my skeletal tracking, and is used for making a gesture. This gestureCanvas is on my authenticationPage. The code behind this gestureCanvas is on my MainWindow.xaml.cs.
I need to link the gestureCanvas with my MainWindow.xaml.cs, because the code is behind MainWindow, and it's going to be used there, because it's an Kinect application.
How to link these ?
partial class MainWindow
{
void LoadCircleGestureDetector()
{
using (Stream recordStream = File.Open(circleKBPath, FileMode.OpenOrCreate))
{
circleGestureRecognizer.TraceTo(gesturesCanvas, Colors.Red);
}
}
}
This is my authenticationPage
<UserControl
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:smartHome2011"
xmlns:MyUserControl="clr-namespace:MyUserControl;assembly=MyUserControl"
mc:Ignorable="d"
x:Class="smartHome2011.AuthenticationPage"
x:Name="UserControl"
d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot">
<Grid x:Name="kinectGrid" HorizontalAlignment="Left">
<Viewbox Margin="204,220,430,220">
<Grid ClipToBounds="True" Margin="204,220,430,220">
**<Canvas x:Name="gesturesCanvas" />**
<Canvas x:Name="kinectCanvas"></Canvas>
</Grid>
</Viewbox>
</Grid>
</Grid>
at your code behind MainWindow you can try following
var gesturesCanvas = YourContentPage.FindName("gesturesCanvas") as Canvas;
if (gesturesCanvas != null) {
// do something
}
hope this helps
My goal is to attach a new image control while the application is running.
img = new System.Windows.Controls.Image();
img.Margin = new Thickness(200, 10, 0, 0);
img.Width = 32;
img.Height = 32;
img.Source = etc;
I've tried
this.AddChild(img);// says must be a single element
this.AddLogicalChild(img);// does nothing
this.AddVisualChild(img);// does nothing
It was never this difficult to add a element with forms.
How can I simply attach this new element to the main window (not another control) so that it will show up.
Solved it, I named the grid main, and from there I was able to access the children attribute and the add function
main.children.add(img);
<Window x:Class="Crysis_Menu.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded" AllowsTransparency="False" Background="White" Foreground="{x:Null}" WindowStyle="SingleBorderWindow">
<Grid Name="main">
<Button Content="Run" Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="btnRun" VerticalAlignment="Top" Width="151" Click="btnRun_Click" />
<TextBox Height="259" HorizontalAlignment="Left" Margin="12,40,0,0" Name="tbStatus" VerticalAlignment="Top" Width="151" />
</Grid>
</Window>
You should have only one root element under window. Adding the image using this.AddChilda adds the image as child of window, but you probably have some other child defined(Grid for example). Give a name to this child (Grid in the example) and then in the code behind add the image to the Grid
Example :
<Window>
<Grid x:Name="RootGrid">
</Grid>
</Window>
Then in the code behind use
RootGrid.Children.Add(img);
What is this in your case? You can try this.Content = image; or this.Children.Add(image);
If your this is indeed a Window, you should know that Window can have only a single child, which you put into Content. If you want several items in Window, usually you put some appropriate container (for example, Grid or StackPanel) as Window's content, and add children to it.
Vlad got the solution. I used it :
var grid = this.Content as Grid;
// or any controls
Label lblMessage = new Label
{
Content = "I am a label",
Margin = new Thickness(86, 269, 0, 0)
};
grid.Children.Add(lblMessage);
I have a data bound XAML control like this:
<UserControl>
<TextBlock Text="{Binding Text}" />
</UserControl>
and want it to render as a 'drawing' like this:
<UserControl>
<TextBlock Text="Actual text value" />
</UserControl>
Does anyone know how to extract the drawing from an arbitrary control?
UPDATE:
This question does not seem to be clear. So I try to explain a bit more.
The input is a XAML control with databinding. Now I want to convert this to plain XAML without any databinding. The output can be written to disk and displayed by any application which understands XAML, without binding to anything.
So the solution should look something like this:
FrameworkElementinput = (FrameworkElement)XamlReader.Read(inputFile);
input.DataContext = dataObject;
FrameworkElement output = ConvertToNative(input);
XamlWriter.Write(outputFile, output);
I'm looking for an implementation of 'ConvertToNative'
If you're gonna do this for a UserControls, make sure to remove the x:Class attribute first since you'll get a XamlParseException otherwise.
You can load the Xaml file with XamlReader.Load and when you save it with XamlWriter.Save, the Bindings are translated to their actual value. There seems to be some trouble getting the Bindings to update though so I worked around this by subscribing to the Loaded event and add it to a container in the UI, and in the event handler remove if from the container and then save it. This can probably be worked around in a better way though..
private void SomeMethod()
{
CreateXamlWithBindingValues("UserControl1.xaml", "UserControl1_Saved.xaml");
}
private void CreateXamlWithBindingValues(string sourcePath, string savePath)
{
StreamReader streamReader = new StreamReader(sourcePath);
StringReader stringReader = new StringReader(streamReader.ReadToEnd());
XmlReader xmlReader = XmlReader.Create(stringReader);
FrameworkElement loadedObject = (FrameworkElement)XamlReader.Load(xmlReader);
loadedObject.DataContext = UserControlViewModel;
RoutedEventHandler routedEventHandler = null;
routedEventHandler = new RoutedEventHandler(delegate
{
loadedObject.Loaded -= routedEventHandler;
grid1.Children.Remove(loadedObject);
string savedObject = XamlWriter.Save(loadedObject);
StreamWriter streamWriter = new StreamWriter(savePath);
streamWriter.Write(savedObject);
streamWriter.Close();
});
loadedObject.Loaded += routedEventHandler;
grid1.Children.Add(loadedObject);
}
Xaml before Save
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding Text}"/>
</Grid>
</UserControl>
Xaml after Save As you can see, there's no linebreaks but the Text value is the value produced from the binding
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"><Grid><TextBlock Text="Actual Text Value" /></Grid></UserControl>
Xaml before Save
<Grid Name="grid1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Button Content="{Binding MyContent}"/>
</Grid>
Xaml after Save
<Grid Name="grid1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"><Button>Actual Content</Button></Grid>