Problems with RichTextBox.Selection and TextSelection.ApplyPropertyValue - c#

I'm trying to create a text editor in WPF using a RichTextBox.
My problem is with changing the font size of my text. My code works as intended in every case, except when the cursor is inside a word. In this case it should not change the font size of anything, except the font size of the text to come if the user writes anything. The problem is that for some reason TextSelection.ApplyPropertyValue(RichTextBox.FontSizeProperty, value) changes the font size of the whole word when the cursor is inside a word.
This is my eventHandler:
private void fontSizeBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox comboBox = (ComboBox)sender;
string value = (string)comboBox.SelectedValue;
if (comboBox.IsDropDownOpen)
{
TextSelection text = textBoxMain.Selection;
richTextBox.Focus();
text.ApplyPropertyValue(RichTextBox.FontSizeProperty, value);
}
}
And I cannot use something like adding !text.Text.IsEmpty inside the if statement, because I still need to be able to change the font size for text to be written.
I have found similar questions on StackOverFlow, but none with an actual working answer.
Edit: Added XAML
<Window x:Class="MathEdit.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:MathEdit"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.CommandBindings>
<CommandBinding Command="Open" Executed="OpenCommandBinding_Executed"></CommandBinding>
<CommandBinding Command="Save" Executed="SaveCommandBinding_Executed"></CommandBinding>
<CommandBinding Command="SaveAs" Executed="SaveAsCommandBinding_Executed"></CommandBinding>
</Window.CommandBindings>
<Window.InputBindings>
<KeyBinding Key="O" Modifiers="Control" Command="Open"></KeyBinding>
<KeyBinding Key="S" Modifiers="Control" Command="Save"></KeyBinding>
<KeyBinding Key="S" Modifiers="Control+Alt" Command="SaveAs"></KeyBinding>
</Window.InputBindings>
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem Header="_New" InputGestureText="Ctrl+N" />
<MenuItem Header="_Open" InputGestureText="Ctrl+O" Command="Open"/>
<MenuItem Header="_Save" InputGestureText="Ctrl+S" Command="Save"/>
<MenuItem Header="_Save As" InputGestureText="Ctrl+Alt+S" Command="SaveAs"/>
<Separator />
<MenuItem Header="_Exit" InputGestureText="Alt+F4" />
</MenuItem>
<MenuItem Header="_Tools">
<MenuItem Header="_Check if toby = on" IsCheckable="false" IsChecked="True" Click="MenuItem_Click" />
<MenuItem Header="_Settings" Click="MenuItem_Click_2" IsCheckable="True" />
<MenuItem Header="_Add formula" x:Name="menuItemAdd" Click="MenuItem_Add_Click" />
</MenuItem>
<ComboBox x:Name="fontSizeBox" Width="40" SelectedValuePath="Content" SelectionChanged="fontSizeBox_SelectionChanged" SelectedIndex="2">
<ComboBoxItem Content="5"/>
<ComboBoxItem Content="12"/>
<ComboBoxItem Content="16"/>
<ComboBoxItem Content="20"/>
</ComboBox>
</Menu>
<Grid x:Name="gridParent">
<RichTextBox x:Name="richTextBox" AcceptsReturn="True" SelectionChanged="textBoxMain_SelectionChanged" />
</Grid>
</DockPanel>

This simple application of a property value to a no-text selection is not possible. Look at what you get when you apply it to a real selection: Say you have a text "hello brave new world". The document inside the RTB looks like this (simplified)
<FlowDocument>
<Paragraph>
<Run>hello brave new world</Run>
</Paragraph>
</FlowDocument>
When you select "brave" and change the font size (or anything else), the document changes to
<FlowDocument>
<Paragraph>
<Run>hello </Run>
<Run FontSize="20">brave</Run>
<Run> new world</Run>
</Paragraph>
</FlowDocument>
An attribute has to be applied to some text; there is no way to change "nothing", as you requested. If you want to achieve what to me looks closest to your requirement, you will have to split the document yourself - when the selection is empty - and create an empty run with the desired font size. So the above example would look like this (assuming, the caret is positioned before "brave"):
<FlowDocument>
<Paragraph>
<Run>hello </Run>
<Run FontSize="20"></Run>
<Run>brave new world</Run>
</Paragraph>
</FlowDocument>
As this leaves endless possibilities to end up with an infinite number of "empty" Runs, I would advise to revise your requirement.

Related

CheckBoxes "stealing" bitmaps from each other [duplicate]

I have a MenuItem like below
<MenuItem Header="Edit">
<MenuItem Header="Copy Direct Link" Icon="{StaticResource CopyIcon}" Command="{Binding CopyImageCommand}" />
<MenuItem Header="Copy Image Data" Icon="{StaticResource CopyIcon}" Command="{Binding CopyImageDataCommand}" />
<MenuItem Header="Paste" Icon="{StaticResource PasteIcon}" Command="{Binding PasteImageCommand}" />
</MenuItem>
Notice the 1st 2 items use the same icon, I get something like below
I tried removing the 2nd item,
<MenuItem Header="Edit">
<MenuItem Header="Copy Direct Link" InputGestureText="Ctrl+C" Icon="{StaticResource CopyIcon}" Command="{Binding CopyImageCommand}" />
<!--<MenuItem Header="Copy Image Data" InputGestureText="Ctrl+Alt+C" Icon="{StaticResource CopyIcon}" Command="{Binding CopyImageDataCommand}" />-->
<MenuItem Header="Paste" InputGestureText="Ctrl+P" Icon="{StaticResource PasteIcon}" Command="{Binding PasteImageCommand}" />
</MenuItem>
then I got something like
How can I reuse Icons?
See this question
An Image can only have one parent so it will be moved from the first MenuItem to the second. You can add the x:Shared attribute like this
<Window.Resources>
<Image x:Key="CopyIcon" x:Shared="False" Source="..." />
</Window.Resources>
From msdn
x:Shared Attribute
When set to false, modifies WPF
resource-retrieval behavior so that
requests for the attributed resource
create a new instance for each request
instead of sharing the same instance
for all requests.
You're most likely declaring CopyIcon as Image type in your resource, something like this:
<Window.Resources>
<Image x:Key="CopyIcon" Source="yourcopyicon.ico"/>
</Window.Resources>
So, the root cause of the problem is, Image is a visual element, since it derives from FrameworkElement (which is a visual element), and a visual element cannot have more than one parent at the same time. That is why the first MenuItem is not showing the icon, since the second MenuItem reset the parent of CopyIcon, making itself parent of the CopyIcon.
Hope this explanation is helpful to you. Now follow what Meleak has said in his response. :-)
Try the following:
<MenuItem Header=“Paste“ >
<MenuItem.Icon><Image Height=“16“ Width=“16“ Source=“paste.jpg“ /></MenuItem.Icon>
</MenuItem>

The ability to propagate default values down the element tree doesn't work

I have a strange problem in my simple application window when trying to set font size for all controls on it. Some controls inherit the font size from parent window, another (Menu, StatusBar) don't.
I expected that setting the FontSize property value for the window will propagate down the element tree. But for some controls it does not work.
Why? Is there any explanation for this? Is there any error in my code?
NOTE: There is no code behind.
MainWindow.xaml
<Window x:Class="WpfApp.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:WpfApp"
mc:Ignorable="d"
Title="MainWindow" Height="340" Width="300"
FontSize="24" >
<StackPanel>
<Label Content="Hello! " />
<Menu DockPanel.Dock="Top" Margin="10">
<MenuItem Header="File"/>
<MenuItem Header="Edit"/>
<MenuItem Header="View"/>
<MenuItem Header="Help"/>
</Menu>
<ListBox Margin="10">
<ListBoxItem>Chapter 1</ListBoxItem>
<ListBoxItem>Chapter 2</ListBoxItem>
</ListBox>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Margin="5" Padding="5" Content="Help"/>
<Button Margin="5" Padding="5" Content="OK" />
</StackPanel>
<StatusBar Margin="10">
<Label>Status Bar</Label>
<Separator/>
<Label>Zoom</Label>
<ComboBox SelectedIndex="0">
<ComboBoxItem>100%</ComboBoxItem>
<ComboBoxItem>75%</ComboBoxItem>
<ComboBoxItem>50%</ComboBoxItem>
<ComboBoxItem>25%</ComboBoxItem>
</ComboBox>
</StatusBar>
</StackPanel>
</Window>
On the picture below the menu and the status bar are not inherit the
font size:
Some WPF controls like Menu and StatusBar explicitly set the FontSize property in their default styles.
Menu.xaml
<Style x:Key="{x:Type Menu}" TargetType="{x:Type Menu}">
...
<Setter Property="FontSize"
Value="{DynamicResource {x:Static SystemFonts.MenuFontSizeKey}}"/>
...
</Style>
This is why inheritance is breaking.
If a property is set explicitly it will not inherit a value.
The only way around this is to override the default styles.

WPF Menu Item check mark is blacked in Windows 10

I created a small menu bar with some checked menu items. It is blacked out in Windows 10 but displayed fine in Windows 7
XAML
<Window x:Class="CheckMenuTickinWin10.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:CheckMenuTickinWin10"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<ToolBar>
<Menu>
<MenuItem Header="menu">
<MenuItem Header="Sub" IsCheckable="True" IsChecked="True"/>
<MenuItem Header="Sub" IsCheckable="True" IsChecked="False"/>
<MenuItem Header="Sub" IsCheckable="True" IsChecked="True"/>
</MenuItem>
</Menu>
</ToolBar>
</StackPanel>
</Window>
What should I do in Windows 10 to make the check mark visible?
This background is defined in the default ControlTemplate of the menu item. A template defines the look of a control, if you don't define a template for the control, it will pick up the default template.
The good news is that you are free to edit the ControlTemplate to customize the control as whatever you like. The not so good news is that you have to be comfortable with a bunch of XAML code (hundreds of lines for a single template).
First find the "default" ControlTemplate of the MenuItem in Visual Studio.
In the VS designer, right click the SubMenuItem, and choose "Edit Template" -> "Edit a Copy" from the dropdown menu. This will automatically copy the default template of the SubMenuItem to a style name "MenuItemStyle1" as defined in the window's Resource dictionary.
Remove the black background from the template.
Expand MenuItemStyle1, find the following line that defines the black border.
<Border x:Name="GlyphPanel" BorderBrush="#80DADADA" BorderThickness="1" Background="#FF212121" Height="20" Margin="0,1" Visibility="Hidden" Width="20">
Change the Background color from "#FF212121" to "#FFEEF5FD", and save the change. (#FFEEF5FD is the hightlight color of the border, it is also the color of the light background of the menu, you can find this color in the default template.)
Then apply this new template to all 3 menu items.
<MenuItem Header="menu">
<MenuItem Header="Sub" IsCheckable="True" IsChecked="True" Style="{DynamicResource MenuItemStyle1}" />
<MenuItem Header="Sub" IsCheckable="True" IsChecked="False" Style="{DynamicResource MenuItemStyle1}" />
<MenuItem Header="Sub" IsCheckable="True" IsChecked="True" Style="{DynamicResource MenuItemStyle1}" />
</MenuItem>

How to add Combobox as ContextMenu Item for Label Control. WPF app

I want to add combobox as ContextMenu item for some label control in wpf application in my code behind. How can I do it? I searched a lot over web but nothing comes productive.
The following code is just a proof of concept on how you could build your ContextMenu. It will give you a ComboBox as the content of a MenuItem.
<Label Content="label with context menu">
<Label.ContextMenu>
<ContextMenu>
<MenuItem Header="menu 1">
<ComboBox>
<ComboBoxItem Content="combo 1" IsSelected="True" />
<ComboBoxItem Content="combo 2" />
<ComboBoxItem Content="combo 3" />
</ComboBox>
</MenuItem>
</ContextMenu>
</Label.ContextMenu>
</Label>
Another alternative...this allows the ComboBox to appear directly when you do the Right-Click. Copy and paste this into KAXAML to see it working.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Label Content="Some Label">
<Label.ContextMenu>
<ContextMenu>
<ContextMenu.Template>
<ControlTemplate>
<ComboBox SelectedIndex="0">
<ComboBoxItem>One</ComboBoxItem>
<ComboBoxItem>Two</ComboBoxItem>
<ComboBoxItem>Three</ComboBoxItem>
</ComboBox>
</ControlTemplate>
</ContextMenu.Template>
</ContextMenu>
</Label.ContextMenu>
</Label>
</Grid>
</Page>
I got the solution, we can do this is folowing way:
ContextMenu contextmenu = new ContextMenu();
ComboBox CmbColorMenu = new ComboBox();
CmbColorMenu.ItemsSource = FontColors;// FontColors is list<objects>
CmbColorMenu.DisplayMemberPath = "Text";
contextmenu.Items.Add(CmbColorMenu);

ContextMenu in WPF is too wide

I've got a problem with my ContextMenu in WPF. The menu is far too wide- it's the width of the items I put on it, plus about fifty-a hundred pixels. So when you open the menu, instead of being a clean list of options or buttons, there's loads of greyspace on each side. How can I fix this?
Edit: Here's my XAML for the menu:
<ContextMenu Padding="0">
<Button Content="Close Tab" Height="23" Name="closetabbutton" Width="75" Margin="0,0,0,0" Click="closetabbutton_Click" />
<TextBox Height="23" Name="renamebox" Width="75" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ClipToBounds="True" TextChanged="renamebox_TextChanged" />
<Button Content="Close Menu" Height="23" Name="closemenubutton" Width="75" Margin="0,0,0,0" Click="closemenubutton_Click" />
</ContextMenu>
The space is reserved for icons on the left, and input gesture text (e.g. Ctrl+C) on the right. This is by design.
If you wish to change this, you'll have to create your own ContextMenu style. Here's an example of how to do this:
http://www.dev102.com/2008/06/20/how-to-create-a-wpf-custom-context-menu/
Update
Further to my question comment, MenuItems would normally be used where you have used buttons. For example:
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Name="mnuClose" Header="Close tab" InputGestureText="Ctrl+C" />
<MenuItem Name="mnuRename">
<MenuItem.Header>
<TextBox Name="txtRename" Width="100" />
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</Grid.ContextMenu>

Categories

Resources