I have a UWP Desktop application with Text to Speech capabilities. In it, I have a TextBox to contain the text that will be executed by the Speech Synthesizer. During execution, the application selects the currently executed phrase. However, since the text is larger than the TextBox, I need to scroll the text so that the executed and selected phrase is visible to the user. How to do this? Any help is most welcome.
UWP/C# How to scroll text from a TextBox to selected text?
I'm afraid you can't scroll to specific position wihtin UWP TextBox. It looks does not contains ScrollTo method. However, you could get TextBox's internal ScrollViewer then call ChangeView method to scroll to your wanted position.
For example.
public void ScrollToSp(TextBox control, string text)
{
var grid = (Grid)VisualTreeHelper.GetChild(control, 0);
var position = control.Text.Contains(text);
if (control.Text.Contains(text))
{
var index = control.Text.IndexOf(text);
var rect = control.GetRectFromCharacterIndex(index, true);
for (var i = 0; i <= VisualTreeHelper.GetChildrenCount(grid) - 1; i++)
{
object obj = VisualTreeHelper.GetChild(grid, i);
if (!(obj is ScrollViewer)) continue;
((ScrollViewer)obj).ChangeView(0.0f, rect.Top, 1.0f);
break;
}
}
}
Related
I have simple Win-form GUI in C# which display the text in red or Green depended upon the value received. The RichText display the text correctly as long as the i do not minimize the GUI. When the GUI is minimized, the text shown in Text window is in black color (only data that was processed when GUI was minimized). when the GUI is maximized the text color for the data shown correctly again.
Please let me know what is wrong here.
Here is my code:
LogMessageWindow.Find(message);
LogMessageWindow.SelectionColor = Color.Red; /// if message&2==0 set color to Red otherwise set color to green
LogMessageWindow.SuspendLayout();
LogMessageWindow.Focus();
LogMessageWindow.AppendText(message + ".\n");
LogMessageWindow.ScrollToCaret();*
In your code you have:
LogMessageWindow.Find(message);
This line is useless: you are Appending a new chunk of Text. Searching for it before appending it won't do much (maybe locate an identical string. Then what?).
LogMessageWindow.SuspendLayout();
SuspendLayout() can be useful if you're adding/appending a large selection of lines of text in batch. When you're finished, you should ResumeLayout(). Doesn't seem to be needed here.
LogMessageWindow.Focus();
Moving the Focus on the RTB control doesn't accomplish anything special. And if the container Form is minimized... Since you're adding text in a procedure, the focus is not needed.
A couple of things you can do.
Using a method, pass a reference to the RichTextBox that is used for this task, the color to use and the text to be appended. Here the new text color is defined as Color? color, so if you pass null, the control ForeColor is used.
RTBAppendWithColor(LogMessageWindow,
((message & 2) == 0) ? Color.Red : Color.Green,
message.ToString() + "\n");
private void RTBAppendWithColor(RichTextBox rtb, Color? color, string AppendedText)
{
int sLenght = AppendedText.Length;
rtb.AppendText(AppendedText);
rtb.Select(rtb.Text.Length - sLenght, sLenght);
if (color != null)
rtb.SelectionColor = (Color)color;
rtb.ScrollToCaret();
}
Using an Extension.
Create a static Class with a static Method that references a RichTextBox object. This method will be a new method of any RichTextBox you create.
LogMessageWindow.AppendWithColor(((message & 2) == 0) ? Color.Red : Color.Green,
message.ToString() + "\n");
public static class RTBExtensions
{
public static void AppendWithColor(this RichTextBox rtb, Color? color, string AppendedText)
{
int sLenght = AppendedText.Length;
rtb.AppendText(AppendedText);
rtb.Select(rtb.Text.Length - sLenght, sLenght);
if (color != null)
rtb.SelectionColor = (Color)color;
rtb.ScrollToCaret();
}
}
If you using FrameWork 3.5, the selection text will probably remain selected even after ScrollToCaret() is called. If it looks ugly, add:
rtb.SelectionStart = rtb.Text.Length;
before rtb.ScrollToCaret().
Thanks al for yur valuable feedback. i was able to get this done by suing this code.
LogMessageWindow.SelectionStart = LogMessageWindow.TextLength; LogMessageWindow.SelectionLength = 0; LogMessageWindow.SelectionColor = Color.Red; LogMessageWindow.SuspendLayout();LogMessageWindow.AppendText(message + ".\n"); LogMessageWindow.ScrollToCaret(); ` LogMessageWindow.ResumeLayout()
I'm trying to create a chat application like msn. When i do "textBox.Text = textBox.Text+text" it updates the textbox and the text i got selected is no longer selected. In MSN you can have selected text and still recieve messages in different colors etc.. How do they do it? I figure its something like push messages, maybe they create a new textbox under another textbox? Any clues?
I hope you guys know what i'm talking about here. I just want my text to behave like MSN used to do, not update the whole textbox, just push a new message under the current message etc.
If I understand your question, you just want text to stay selected when you append messages to a RichTextBox?
int selectionStart = textBox.SelectionStart;
int selectionLength = textBox.SelectionLength;
int carat = textBox.TextLength;
textBox.Text += Environment.NewLine;
textBox.Text += newText;
//optional styling code for newly appended text
textBox.Select(carat, newText.Length);
textBox.SelectionColor = //value;
//etc.
//reapply original selection
if(selectionStart >= 0 && selectionLength > 0)
{
textBox.Select(selectionStart, selectionLength);
}
I have an application that has a nice flyout animation when the users selects an item in the listbox. When the user clicks the back button, a flyin animation is used. You can see the example here:
I am using the same animation code as this sample. everything works fine until the clicked item is partially off the bottom or top of the screen. The flyout animation will happen properly but when the user returns to the list, the listbox auto scrolls the selected item up (or down) to be fully in view. The flyin animation however returns the text to the original (before auto scroll happened) location. You may need to download the working sample to fully see what I am on about.
The question I have is - is there a way to disable this auto-scroll action. The built in apps like messaging and email don't scroll a partially visible item into view when selected.
Thanks
I don't know how to disable auto-scroll action, but I have a quick fix for that code:
In class ItemFlyInAndOutAnimations
add a field
private FrameworkElement _element; //let's consider the line is 266
in public void ItemFlyIn() make changes:
public void ItemFlyIn()
{
if (_popupCanvas.Children.Count != 2)
return;
_popup.IsOpen = true;
_backgroundMask.Opacity = 0.0;
Image animatedImage = _popupCanvas.Children[1] as Image;
var sb = new Storyboard();
var rootFame = Application.Current.RootVisual as FrameworkElement; //new line
var targetElementPosition = _element.GetRelativePosition(rootFame); //new line
// animate the X position
var db = CreateDoubleAnimation(targetElementPosition.X - 100, targetElementPosition.X,
new SineEase(),
_targetElementClone, Canvas.LeftProperty, _flyInSpeed); //reference changed!
sb.Children.Add(db);
// animate the Y position
db = CreateDoubleAnimation(targetElementPosition.Y - 50, targetElementPosition.Y,
new SineEase(),
_targetElementClone, Canvas.TopProperty, _flyInSpeed); //reference changed!
sb.Children.Add(db);
//other code is the same
in public void ItemFlyOut(FrameworkElement element, Action action)
after this line
_targetElementPosition = element.GetRelativePosition(rootElement);
add this one:
_element = element;
What have I done:
In this code I save the reference to animated UI element and update it's position on back animation.
You'd better to test this code, but it seems to be ok.
I want to implement something that programmatically changes the background of the text when provided with a documentline.(Something that looks very similar to a block selection of a text. I'm going to be using this for debug breakpoints of an IDE I'm designing). I don't want to have to use selection as it causes the textbox to scroll.
I think I need to make use of DocumentColorizingTransformer but I'm not 100% sure how to go about this.
public class ColorizeAvalonEdit : ICSharpCode.AvalonEdit.Rendering.DocumentColorizingTransformer
{
protected override void ColorizeLine(ICSharpCode.AvalonEdit.Document.DocumentLine line)
{
int lineStartOffset = line.Offset;
string text = CurrentContext.Document.GetText(line);
int start = 0;
int index;
if (line.LineNumber == LogicSimViewCodeWPFCtrl.currentLine)
{
while ((index = text.IndexOf(text, start)) >= 0)
{
base.ChangeLinePart(
lineStartOffset + index, // startOffset
lineStartOffset + index + text.Length, // endOffset
(VisualLineElement element) =>
{
element.TextRunProperties.SetBackgroundBrush(Brushes.Red);
});
start = index + 1; // search for next occurrence
}
}
}
}
currentLine is the portion that will be highlighted.
The above code does work properly.. only problem is if the currentLine ever changes while I am viewing that line, it doesn't highlight the updated line until I scroll to another portion of the document (hiding the updated line), and come back to the updated line.
Also, how do I make the line numbers start from zero?
Since it was their creation, I peeked at SharpDevelop's source and how they did it.
They defined a bookmark type (BreakpointBookmark) and added bookmark to the line.
bookmark itself sets the color of the line in CreateMarker method. It is strange that it is not possible to configure colors of the break-point in SharpDevelop.
Hope it helps.
protected override ITextMarker CreateMarker(ITextMarkerService markerService)
{
IDocumentLine line = this.Document.GetLine(this.LineNumber);
ITextMarker marker = markerService.Create(line.Offset, line.Length);
marker.BackgroundColor = Color.FromRgb(180, 38, 38);
marker.ForegroundColor = Colors.White;
return marker;
}
I found the answer
TxtEditCodeViewer.TextArea.TextView.Redraw();
Isn't this a duplicate of this question?
However it looks like you should call InvalidateArrange() on the editor or InvalidateVisual() on each changed visual.
Basically I have a tablelayoutpanel , it its currently being used for POS System.
When I call SetColumnSpan on a button control , the tablelayoutpanel adds an extra row, and messes up my screen layout.
Has anybody come across this before ?
Each free space in the panel is assigned a blank button, when the screen is in edit mode , they can add/edit and delete buttons.
Below is the code to apply button changes.
Edit cleaned up code a bit
void button_MouseUp(object sender, EventArgs e)
{
try
{
TableLayoutPanelCellPosition pos = tableLayoutPanel1.GetCellPosition((Control) sender);
POSButton productButton = GetProductButton(sender);
tableLayoutPanel1.SuspendLayout();
if (productButton == null)
{
DeleteButton(sender, pos);
return;
}
productButton.Dock = DockStyle.Fill;
EditModeHookButton(productButton);
tableLayoutPanel1.Controls.Remove((Control) sender);
tableLayoutPanel1.Controls.Add(productButton, pos.Column, pos.Row);
if (productButton.TableRowSpan > 0)
tableLayoutPanel1.SetRowSpan(productButton, productButton.TableRowSpan);
if (productButton.TableColumnSpan > 0)
tableLayoutPanel1.SetColumnSpan(productButton, productButton.TableColumnSpan);
buttonManager.Save(tableLayoutPanel1);
tableLayoutPanel1.ResumeLayout();
}
catch(OperationCanceledException)
{
}
}
Here is the button Manager function that serializes the button layout.
public void Save(ScreenTabkeLayoutPanel panel)
{
List<ButtonSaveInfo> buttons = new List<ButtonSaveInfo>();
foreach (Control control in panel.Controls)
{
TableLayoutPanelCellPosition pos = panel.GetCellPosition(control);
ButtonSaveInfo info;
if (control is POSButton)
info = ((POSButton)control).ConvertToButtonInfo(pos);
else
info = control.ConvertToButtonInfo(pos);
buttons.Add(info);
}
AppDataSerializer.SaveBinary(buttons,buttonPath);
}
Here is the code that loads/populates the screen with the buttons
private void LoadButtonsFromFile(ScreenTabkeLayoutPanel panel)
{
List<ButtonSaveInfo> buttons = AppDataSerializer.LoadBinary<List<ButtonSaveInfo>>(buttonPath);
panel.SuspendLayout();
foreach (ButtonSaveInfo info in buttons)
{
switch (info.ButtonType)
{
case (int) ButtonType.PRODUCT:
POSButton productButton = info.ConvertToPosButton();
wireButtonEvents(productButton);
panel.Controls.Add(productButton, info.ColumnIndex, info.RowIndex);
if (productButton.TableRowSpan > 0)
panel.SetRowSpan(productButton, productButton.TableRowSpan);
if (productButton.TableColumnSpan > 0)
panel.SetColumnSpan(productButton, productButton.TableColumnSpan);
break;
default:
Control control = BuildBlankButton();
wireButtonEvents(control);
panel.Controls.Add(control, info.ColumnIndex, info.RowIndex);
break;
}
}
FillEmptySpacesWillBlankButtons(panel);
panel.ResumeLayout();
}
Thanks in advanced.
Make sure you don't have a control in a spanned cell.
If you set column span to 2 on cell 0,0 and put a control in 1,0 this will confuse the layout engine. Since you specified in your question that you added blank buttons to all cells, this might be what is happening here.
Make sure you remove any control from a cell you are planning to span over.
Also, there are some situation in which the table layout just gives up, especially if you span cells with auto sizing.
Are you setting the RowSpan to a value greater than the number of rows in the table? This might cause an extra row to be rendered. Other than that you will need to provide more information/code for us to figure it out :)