Binding to RichTextBox Blocks property - c#

I would like to bind directly to the Blocks property of a RichTextBox in my Xaml. This is not possible as the Blocks property is read only. I can bind directly to an individual run:
<RichTextBox x:Name="MyRichTextBox" FontSize="36" Margin="10" Foreground="White">
<Paragraph>
<Run Text="{Binding MyObject.Text}" Foreground="Yellow"/>
<Run Text="{Binding MyObject.Text}" Foreground="Cyan"/>
</Paragraph>
</RichTextBox>
I would like to do something like:
<RichTextBox x:Name="MyRichTextBox" Blocks="{Binding MyObject.RichTextBlocks}" FontSize="36" Margin="10" Foreground="White"/>
Particularly as I don't know in advance how many blocks will be returned from the binding object.
Is the correct way to achieve this to create an Attached Behaviour for the RichTextBox with a RichTextBlocks property that when being set enumerates through the blocks and calls RichTextBox.Blocks.Add() for each one?
I am new to C#, .NET and XAML so please excuse the elementary question and a simply explained answer would be greatly appreciated.

With the pointers from #Nogard and the other post, I created my own class with a Dependency Property called RichText. Have posted here in case it is of use to anyone else.
public class MyRichTextBox : RichTextBox
{
public static readonly DependencyProperty RichTextProperty = DependencyProperty.Register("RichText", typeof(Paragraph), typeof(MyRichTextBox), new PropertyMetadata(null, RichTextPropertyChanged));
public Paragraph RichText
{
get
{
return (Paragraph)GetValue(RichTextProperty);
}
set
{
SetValue(RichTextProperty, value);
}
}
private static void RichTextPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
MyRichTextBox richTextBox = (MyRichTextBox)dependencyObject;
Paragraph paragraph = (Paragraph)dependencyPropertyChangedEventArgs.NewValue;
// Remove any existing content from the text box
richTextBox.Blocks.Clear();
// Add the paragraph to the text box
richTextBox.Blocks.Add(paragraph);
}
}
}
and added this to my xaml...
<sub:MyRichTextBox x:Name="MyRichTextOverlay" RichText="{Binding CurrentOverlay.RichTextParagraph}" VerticalAlignment="Top" FontSize="36" Margin="10" Foreground="White" HorizontalAlignment="Center" TextWrapping="Wrap" TextAlignment="Center"/>

Related

OnPropertyChange(null) makes hyperlink disappear

Code first (upper TextBox is simplified for purpose of this question) :
<TextBlock
Style="{StaticResource FieldNameStyle }"
TextWrapping ="Wrap" Height="33" FontSize="12"
Visibility="Visible"
TextAlignment="Center"
Foreground="#FFFFFF"
Opacity="0.5"
Text="{Binding UnderLineMsg}">
<Hyperlink Name="PrivacyNoticeLink2"
Command="{Binding OpenPrivacyNoticeCommand}">
<TextBlock
Visibility="Visible"
Name="privacyNoticeText2"
Text="{Binding PrivacyNoticeButtonLabel,FallbackValue='privacy notice' ,UpdateSourceTrigger=PropertyChanged}"/>
</Hyperlink>
</TextBlock>
this is what it looks after the window loads for the first time : Under line msg filler: link
one of the events in the window triggers a call to
OnPropertyChanged(null);
the method triggers a "refresh" in all the members in the window that are subscribed to it with :
UpdateSourceTrigger=PropertyChanged
once called the Hyperlink element disappears completely (verified using Snoop 2.8)
so after the call it will look like this:
Under line msg filler:
i have NO idea why this is happening. the current fix is replacing the general OnPropertyChanged call with many specific ones but that is not a realistic option in the long run.
EDIT :
Isolated the issue to a new project, note the issue still happens when its only a textblock within a textblock
simple XAML with a button that triggers OnPropertyChanged
<Grid>
<Button Click="Meh" Margin="171,37,153,199">
PRESS ME
</Button>
<TextBlock Name="WrapperText" Text= "{Binding randomNumber}">
<TextBlock Name="linkText" Text="{Binding randomNumStr }"></TextBlock>
</TextBlock>
</Grid>
Code behind:
public MainWindow()
{
DataContext = new Stuff();
InitializeComponent();
}
public void Meh(object sender, RoutedEventArgs e)
{
//MessageBox.Show(this, "BLA", "caption", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
//MessageBox.Show("FASDFASDFASDF");
(DataContext as Stuff).OnPropertyChanged(null);
//Msg.ShowMessageBox("BLA", "caption", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
}
the "view model"
public class Stuff : INotifyPropertyChanged
{
public Stuff()
{
rnd = new Random();
}
private Random rnd;
public int randomNumber => rnd.Next(1, 100);
public string randomNumStr => randomNumber.ToString()+"Text";
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
public virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Note the truly disgusting way that I trigger the property change. I know I should use Icommand in the 'Stuff' class but I wanted to isolate the problem quickly. In my original code, it's done properly.
Don't bind the Text property of a TextBlock that you are also adding a Hyperlink to. Raising the PropertyChanged event for the source property will then clear out the Hyperlink.
Instead of binding the Text property of the TextBlock itself, you could add a Run element to it:
<TextBlock
TextWrapping ="Wrap" Height="33" FontSize="12"
Visibility="Visible"
TextAlignment="Center"
Foreground="#FFFFFF"
Opacity="0.5">
<Run Text="{Binding UnderLineMsg, Mode=OneWay}" />
<Hyperlink Name="PrivacyNoticeLink2" Command="{Binding OpenPrivacyNoticeCommand}">
<TextBlock
Visibility="Visible"
Name="privacyNoticeText2"
Text="{Binding PrivacyNoticeButtonLabel,FallbackValue='privacy notice' ,UpdateSourceTrigger=PropertyChanged}"/>
</Hyperlink>
</TextBlock>

Textbox is becoming readonly in flyout, UWP store app

In button flyout I am using one usercontrol inside that I have textbox. when running the app the textbox is appearing as readonly, don't know why I am getting this issue. nowhere I am setting readonly.
<TextBox Margin="2" Height="32"
MaxHeight="60"
TextWrapping="Wrap"
HorizontalAlignment="Stretch"
TextAlignment="Left"
Text="ramesh"
Style="{x:Null}"/>
Figure out the issue it's because of anniversary update.
https://blogs.msdn.microsoft.com/wsdevsol/2016/09/14/combobox-from-an-appbarbutton-loses-mouse-input-on-1607/
I created attached property for solution given in above link. Below is the attached property
public class CompatExtensions
{
public static bool GetAllowFocusOnInteraction(DependencyObject obj)
{
return (bool)obj.GetValue(AllowFocusOnInteractionProperty);
}
public static void SetAllowFocusOnInteraction(DependencyObject obj, bool value)
{
obj.SetValue(AllowFocusOnInteractionProperty, value);
}
// Using a DependencyProperty as the backing store for AllowFocusOnInteraction.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty AllowFocusOnInteractionProperty =
DependencyProperty.RegisterAttached("AllowFocusOnInteraction",
typeof(bool),
typeof(CompatExtensions),
new PropertyMetadata(0, AllowFocusOnInteractionChanged));
private static bool allowFocusOnInteractionAvailable =
Windows.Foundation.Metadata.ApiInformation.IsPropertyPresent(
"Windows.UI.Xaml.FrameworkElement",
"AllowFocusOnInteraction");
private static void AllowFocusOnInteractionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (allowFocusOnInteractionAvailable)
{
var element = d as FrameworkElement;
if (element != null)
{
element.AllowFocusOnInteraction = (bool)e.NewValue;
}
}
}
}
And an example of it used:
<AppBarButton local:CompatExtensions.AllowFocusOnInteraction="True" Icon="Setting">
<AppBarButton.Flyout>
<Flyout>
<StackPanel Orientation="Vertical" >
<ComboBox>
<ComboBoxItem Content="Red" IsSelected="True" />
<ComboBoxItem Content="Green" />
<ComboBoxItem Content="Blue"/>
</ComboBox>
</StackPanel>
</Flyout>
</AppBarButton.Flyout>
</AppBarButton>
Difficult to be sure about any answer given how the few details have been provided but I once saw something similar due to sizing of the TextBox. The UWP text box has a "delete" button (a small cross) at the end of the box to delete the current content. When the TextBox was sized vertically, the delete button scaled to take up the entirety of the TextBox thereby making it look read only.
If you're facing a similar issue, try setting AcceptsReturn="True" and InputScope="Text" on the TextBox.

WPF Insert variable in TextBlock

Is it possible to bind "AMOUNT" with it's value (i.e. update with corresponding global variable) using only XAML? If not, what i have write to replace AMOUNT with my var before showing the page?
http://i.imgur.com/SDrV0rs.png
<TextBlock Height="231" Canvas.Left="120" TextWrapping="Wrap" Canvas.Top="459" Width="840"
FontFamily="Neo Sans Pro" FontSize="48"
Foreground="#FF006CB7"
VerticalAlignment="Top" HorizontalAlignment="Left" TextAlignment="Center">
<Run Text="Для перечисления "/>
<Run FontWeight="Bold" Text="AMOUNT"/>
<Run Text=" рублей в помощь детям с помощью банковской карты, пожалуйста, следуйте инструкции:"/>
</TextBlock>
What you need is a binding to a variable in your code-behind.
Text="{Binding AMOUNT}"
If this is - as you describe - a "global variable", you can bind like so:
Text="{x:Static wpfApplication1:Globals.Amount}"
The global variable definition could look like this:
public class Globals
{
public static string Amount = "5000";
}
Note that the Text property of your text box requires a string.
Using MVVM; in very broad strokes :
Create a class with a string property: e.g.
public class MyViewModel
{
public string Amount { get { return "..."; } }
}
Assign an instance of the class above to the DataContext of the view.
var viewModel = new MyViewModel();
view.DataContext = viewModel;
Using a binding expression in the XAML
...TextBlock Text="{Binding Amount}"... />

PropertyChangedCallback not firing when respective property bound to ScrollBar

I have a rather simple UserControl which I would like to extend with the DependencyProperty. The relevant code of the control is as follows:
public partial class CompassControl : UserControl
{
public static readonly DependencyProperty AngleProperty =
DependencyProperty.Register("Angle", typeof(Double), typeof(CompassControl),
new FrameworkPropertyMetadata( 0.0, FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnAngleChanged)));
private static void OnAngleChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
((CompassControl)target).SetImageAngle((Double)e.NewValue);
}
public CompassControl( )
{
InitializeComponent( );
}
public Double Angle
{
get { return (Double)GetValue(AngleProperty); }
set { SetValue(AngleProperty, value); }
}
This control is being used on a simple form; the relevant XAML as follows:
<DockPanel DockPanel.Dock="Bottom">
<DockPanel>
<TextBlock DockPanel.Dock="Left"
TextAlignment="Center" FontWeight="Bold" FontSize="12"
Padding="0,4,0,0" HorizontalAlignment="Left"
Height="22" Width="60" Margin="10,0,0,0"
Text="{Binding ElementName=scrollBarAngle, Path=Value}">
</TextBlock>
<ScrollBar DockPanel.Dock="Left" Name="scrollBarAngle" Orientation="Horizontal"
Height="22" Margin="10,0"
Maximum="360.0" Minimum="0.0" SmallChange="1.0" Value="0.0"
ValueChanged="scrollBarAngle_ValueChanged" />
</DockPanel>
</DockPanel>
<ctl:CompassControl DockPanel.Dock="Top" Name="compassControl"
Margin="5" Width="Auto" Height="Auto"
Angle="{Binding ElementName=scrollBarAngle, Path=Value}"
/>
</DockPanel>
The "Text" property of the TextBox and the "Angle" property of my control are bound to the "Value" property of the ScrollBar using the following binding:
"{Binding ElementName=scrollBarAngle, Path=Value}"
When I scroll the ScrollBar, the Text field is updated as expected, but the Angle does not change - the OnAngleChanged callback is not being called!
However if I directly change the Angle property in the ScrollBar's ValueChanged event everything works fine - the property got changed and the respective callback fired:
private void scrollBarAngle_ValueChanged( object sender, RoutedPropertyChangedEventArgs<double> e )
{
compassControl.Angle = e.NewValue;
}
Please help resolve this issue!
Thank you,
--Alex
My apologies - the problem was not in the code, but in the environment! I had several copies of VS2013 open, the project was open in two of them. Anyway, after reading Clemens comment indicating that my problem is not reproducible, I closed all instances of VS, then started the fresh instance, opened the project - and everything worked fine!
Thank you!

Finding a control inside another control in WPF

Since there is no link button in WPF I created a link button using hyperlink and text block controls.
There are 3 controls:
<TextBlock Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Left" >
<Hyperlink Name="hyplnkIsActiveMarkets" Click="hyplnkIsActive_Click" Foreground="Blue" >
<TextBlock Name="txtblkIsActiveMarkets" Text="Active" />
</Hyperlink>
</TextBlock>
<TextBlock Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left">
<Hyperlink Name="hyplnkIsActiveBudgets" Click="hyplnkIsActive_Click" Foreground="Blue" >
<TextBlock Name="txtblkIsActiveBudgets" Text="Active" />
</Hyperlink>
</TextBlock>
<TextBlock Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Left">
<Hyperlink Name="hyplnkIsActivePrograms" Click="hyplnkIsActive_Click" Foreground="Blue" >
<TextBlock Name="txtblkIsActivePrograms" Text="Active" />
</Hyperlink>
</TextBlock>
All the link buttons calls same click method
private void hyplnkIsActive_Click(object sender, RoutedEventArgs e)
{
Hyperlink objHyperlink = (Hyperlink)sender;
TextBlock objTextBlock = new TextBlock();
if (objHyperlink == hyplnkIsActiveMarkets)
{
objTextBlock = txtblkIsActiveMarkets;
}
else if (objHyperlink == hyplnkIsActiveBudgets)
{
objTextBlock = txtblkIsActiveBudgets;
}
else if (objHyperlink == hyplnkIsActivePrograms)
{
objTextBlock = txtblkIsActivePrograms;
}
if (objTextBlock.Text == "Active")
ChangeHyperLinkStatus(objHyperlink, objTextBlock, Status.Inactive);
else ChangeHyperLinkStatus(objHyperlink, objTextBlock, Status.Active);
}
In the click method I check for the text block inside the hyper link individually using if condition.
Is there any easier way to do this? That's basically finding control inside a control?
UPDATE: you can not use VisualTreeHelper.GetParent(...) to get the parent of your hyperlink as you have mentioned hyperlink is not visual. corrected the answer.
See code below.
private void hyplnkIsActive_Click(object sender, RoutedEventArgs e)
{
Hyperlink objHyperlink = (Hyperlink)sender;
TextBlock objTextBlock = (TextBlock)LogicalTreeHelper.GetChildren(objHyperlink)[0];
// This will give logical tree first child of objHyperlink
if (objTextBlock.Text == "Active")
ChangeHyperLinkStatus(objHyperlink, objTextBlock, Status.Inactive);
else
ChangeHyperLinkStatus(objHyperlink, objTextBlock, Status.Active);
}
See this article about logical tree on MSDN
I think you're going into the wrong direction by relaying your execution logic on controls and not on data.
You can, for example, bind a ICommand or RelayCommand to the buttons, or just subscribe different events, or define a custom DataTemplate where on mouse down the clicked control is assignable to some ModelView property.
Doing in way you do, you create tough coupling between UI and your execution logic.
In this case easier use WindowsForm then WPF.
I got it finally . Thanks to Maheep fa his help
TextBlock objTextBlock = (TextBlock)LogicalTreeHelper.GetChildren(objHyperlink).Cast<System.Windows.Documents.InlineUIContainer>().FirstOrDefault().Child;

Categories