implementing bold button for wpf richtextbox... selection disappears after applying bold - c#

Let's say I have RichTextBox from the Windows WPF library, and I'm trying to make it so I can select text in the richtextbox and click a bold button. as soon as I click the bold button the selected text should remain except what's in the section turns bold, and when i click the bold button again the bold should disappear and the selection should remain unchanged.
Now for what I observe, I click block and the selection disappears. Here's my code to show tht:
<Window x:Class="WpfApp2.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:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<DockPanel>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
<Button Name="Bold" Content="Bold" Click="Bold_Click"/>
</StackPanel>
<RichTextBox Name="RichTextBox1" />
</DockPanel>
</Window>
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
namespace WpfApp2 {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
RichTextBox1.CaretPosition.InsertTextInRun("Select this text with a mouse click-and-drag, "
+ "then click bold button, and observe that "
+ "selection highlight disappears");
}
private bool SelectionIsBold() {
var weight = RichTextBox1.Selection.GetPropertyValue(FontWeightProperty);
var unset = DependencyProperty.UnsetValue;
var SelectionIsBold = weight != unset && (FontWeight)weight == FontWeights.Bold;
return SelectionIsBold;
}
private void Bold_Click(object sender, RoutedEventArgs e) {
if (RichTextBox1.Selection.IsEmpty) return;
var textRange = RichTextBox1.Selection;
var len = RichTextBox1.Selection.Text.Length;
var startpos = RichTextBox1.CaretPosition.GetPositionAtOffset(0);
var endpos = RichTextBox1.CaretPosition.GetPositionAtOffset(len); //GetPoint(start, len);
if (SelectionIsBold())
RichTextBox1.Selection.ApplyPropertyValue(Inline.FontWeightProperty, FontWeights.Normal);
else
RichTextBox1.Selection.ApplyPropertyValue(Inline.FontWeightProperty, FontWeights.Bold);
// ==>> DOESN"T WORK: Attempt to Reselect Text After ApplyPropertyValue because it unhighlights the selection...
RichTextBox1.SelectionBrush = new SolidColorBrush(Colors.Red);
RichTextBox1.Selection.Select(startpos, endpos);
}
} // class
} // namespace

So the issue is not that the selection is being removed. When the user clicks on the button the RichTextBox loses focus so the selection highlight appears to go away. You don't need to worry about re-calculating the selection, just call Focus().
private void Bold_Click(object sender, RoutedEventArgs e)
{
if (RichTextBox1.Selection.IsEmpty) return;
if (SelectionIsBold())
RichTextBox1.Selection.ApplyPropertyValue(Inline.FontWeightProperty, FontWeights.Normal);
else
RichTextBox1.Selection.ApplyPropertyValue(Inline.FontWeightProperty, FontWeights.Bold);
// The selection brush obviously isn't required but i kept it to make it obvious it is working.
RichTextBox1.SelectionBrush = new SolidColorBrush(Colors.Red);
RichTextBox1.Focus(); // Return focus to the Text Box after the button click
}

Related

RichTextBox becomes ReadOnly when binding it to content from inside FlowDocumentReader

Question: In the btnRevert_Click(...) event of the following code, how can we make the RichTextBox editable?
In my WPF .NET5 app, the btnTest_Click(...) event successfully displays the contents of a RichTextBox into a FlowDocumentReader (fdReader). Then the btnRevert_Click(...) event successfully takes the content back from fdReader and displays it back into RichTextBox, but it makes the RichTextBox ReadOnly.
I tested the scenario by entering simple text "This is a test" inside RichTextBox, and noted that the above back and forth scenario works fine except that the btnRevert_Click(...) event makes the text "This is a test" ReadOnly.
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"/>
<Button x:Name="btnRevert" Content="Revert" Click="btnRevert_Click"/>
</ToolBar>
<RichTextBox Name="rtbTest" AcceptsTab="True"></RichTextBox>
<FlowDocumentReader x:Name="fdReader" Visibility="Collapsed">
<FlowDocument>
<Paragraph>Text inside paragraph.</Paragraph>
</FlowDocument>
</FlowDocumentReader>
</DockPanel>
</Grid>
</Window>
MainWindow.xaml.cs:
private void btnTest_Click(object sender, RoutedEventArgs e)
{
FlowDocument RTBflowDoc = rtbTest.Document;
rtbTest.Visibility = Visibility.Collapsed;
rtbTest.Document = new FlowDocument();
fdReader.Document = RTBflowDoc;
fdReader.Visibility = Visibility.Visible;
}
private void btnRevert_Click(object sender, RoutedEventArgs e)
{
FlowDocument fdReaderFlowDoc = fdReader.Document;
fdReader.Document = new FlowDocument();
rtbTest.IsReadOnly = false;
rtbTest.Document = fdReaderFlowDoc;
rtbTest.Visibility = Visibility.Visible;
}
The FlowDocumentReader provides a control to view a flow content (doesn't support edit).
The following lines in btnRevert_Click() method, from question above, just set rtbTest.Document to fdReader.Document:
FlowDocument fdReaderFlowDoc = fdReader.Document;
fdReader.Document = new FlowDocument();
rtbTest.Document = fdReaderFlowDoc;
But because of the FlowDocument in the FlowDocumentReader control doesn't support editing, the easiest way to obtain document from the FlowDocumentReader control for editing is to clone it like below:
private void btnRevert_Click(object sender, RoutedEventArgs e)
{
var range = new TextRange(fdReader.Document.ContentStart, fdReader.Document.ContentEnd);
if (!range.IsEmpty)
{
using (var stream = new MemoryStream())
{
range.Save(stream, DataFormats.XamlPackage);
var copyto = new TextRange(rtbTest.Document.ContentEnd, rtbTest.Document.ContentEnd);
copyto.Load(stream, DataFormats.XamlPackage);
}
}
rtbTest.Visibility = Visibility.Visible;
fdReader.Visibility = Visibility.Collapsed;
}

How do I position a window in WPF in an exact location?

I'm trying to display a window that aligns with an existing component. In this example I want to align it to a button. When i click the button I would like the window to position itself so that it's bottom is just above the button, and it's width is the same as the button. The left of the window should be the same as the left of the button.
To achieve this I use the following xaml:
<Window x:Class="WindowPositioningTest.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:WindowPositioningTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Name="MyButton" Content="Click me to see window!" Width="300" Height="50" Click="Button_Click"/>
</Grid>
The onclick function looks like this:
private void Button_Click(object sender, RoutedEventArgs e)
{
var window = new Window();
var myButtonLocation = MyButton.PointToScreen(new Point(0, 0));
window.Width = MyButton.ActualWidth;
window.Height = 300;
window.Left = myButtonLocation.X;
window.Top = myButtonLocation.Y - window.Height;
window.Show();
}
When I click the button a window is displayed like in the picture below.
My question is: why is the window not as wide as the button and why is it not in the right position? It's almost as if there's an invisible frame around the window.
Try this:
private void Button_Click(object sender, RoutedEventArgs e)
{
var window = new Window();
var myButtonLocation = MyButton.PointToScreen(new Point(0, 0));
window.Width = MyButton.ActualWidth + 16;
window.Height = 300;
window.Left = myButtonLocation.X - 8;
window.Top = myButtonLocation.Y - window.Height;
window.Show();
}
It happens beacuse of window border. As you know, window is a composite element. I think when you set Width, you set a width of the working space, not the width of the whole window.

ListBoxItems selected color differs when programatically selecting the item

As you can see in the image below, the highlight/selected color differs when clicking with the mouse vs programatically selecting an item. When clicking an item, the selected color is a transparent blue. When programatically selecting an item, the selected color is grey?
How can I make it so when programatically selecting an item, it also is the transparent blue color that the system uses by default (when clicking with the mouse) ?
Thank you
Here is the simple code behind:
using System.Windows;
using System.Windows.Controls;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
for (int i = 0; i < 10; i++)
{
this.ListBoxTest.Items.Add(new ListBoxItem {Content = "Test " + i});
}
this.ListBoxTest.SelectedItem = this.ListBoxTest.Items[2];
}
}
}
The XAML
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox Name="ListBoxTest"></ListBox>
</Grid>
</Window>
When programatically selected (grey selection color)
When selected via mouse click (light blue color)
All you need to do is set the focus to the listbox first
ListBoxTest.Focus();
Then you can select the item you want.

C# WPF Can't click button after defining margin

I have created a button in my WPF Application using the following code:
Button EditButton = new Button();
EditButton.Margin = new System.Windows.Thickness(Location[0], Location[1], 0, 0);
EditButton.Height = double.Parse("20");
EditButton.Width = double.Parse("20");
EditButton.Cursor = System.Windows.Input.Cursors.Hand;
EditButton.Content = "TEST!";
EditButton.Click += new System.Windows.RoutedEventHandler(Edit_Click);
Grid.Children.Add(EditButton);
Location[1] += 17;
The button works perfectly when I have not defined EditButton.Margin but as soon as I define it I can't click it and the cursor does not change. I have searched the internet around for an answer and none of them seemed to work. Thanks in advance.
If you cannot click the control you have created, then it is generally being caused by other control being on top of it.
I would suggest altering your code slightly and move on from that point:
var stackPanel = new StackPanel();
var button = new Button();
button.Content = "Your Button";
button.Click += new System.Windows.RoutedEventHandler(Edit_Click);
stackpanel.Children.Add(button);
I suggest using StackPanel as it automatically arrange your control and thus prevents it from overlapping you can start from this point on see whether issue was caused by grid or some other component.
Button will stretch by default to its content, so will StackPanel.
Not sure what 'Location' is in your code, and I assume 'Grid' is the name of the grid. The below works.
public MainWindow()
{
InitializeComponent();
Button EditButton = new Button();
EditButton.Margin = new System.Windows.Thickness(10, 10, 0, 0);
EditButton.Height = double.Parse("20");
EditButton.Width = double.Parse("20");
EditButton.Cursor = System.Windows.Input.Cursors.Hand;
EditButton.Content = "TEST!";
EditButton.Click += new System.Windows.RoutedEventHandler(Edit_Click);
Grid.Children.Add(EditButton);
// Location[1] += 17;
}
private void Edit_Click(object sender, RoutedEventArgs e)
{
throw new NotImplementedException();
}
XAML -
<Window x:Class="WpfApplication6.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">
<Grid x:Name="Grid">
</Grid>
</Window>
It looks like you want to do this programmatically, but if you define it in XAML, you could set the button's Panel.ZIndex property to some high number to bring it to the front:
<Button Content="TEST!" Panel.ZIndex="1000" Height="20" Width="20" Cursor="Hand" Click="Edit_Click" />
Hope that helps somebody...

WPF Fire event for dynamically added control in stack panel

I am adding adding a radio button to stack panel for each item in List and that happens without issue. (it adds 4 or 6 radio buttons as those would be count of items in my list).
foreach (var nearestgage in nearestgages)
{
StackPanel.Children.Add(new RadioButton { Content = nearestgage.GageSize.ToString(), Margin = new Thickness(1, 1, 1, 1) });
}
Now what i want to do is when any of these dynamically created radio buttons are selected at run time.
I want to fire a event.
So what i am thinking is i will have to attach a handler to my radio button click . I tried a few methods but not able to that. I have another radio button in the grid which does not belong to this group as well. Please suggest what would be the ideal way to do this.
Here's a little sample, adding the event to the radio button when it's generated.
XAML:
<Window x:Class="WpfApplication1.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">
<StackPanel Name="StackPanel">
</StackPanel>
</Window>
C#
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
for (int i = 1; i <= 10; i++)
{
RadioButton rb = new RadioButton();
rb.Content = "Item " + i.ToString();
rb.Click += rb_Click;
StackPanel.Children.Add(rb);
}
}
void rb_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show((sender as RadioButton).Content.ToString());
}
}
To deal with keeping the radio button actions separate, set the group name:
rb.GroupName = "Dynamic";

Categories