Dynamic custom content in RichTextBox - c#

I wish to display a text + hyperlinks in a RichTextBox from the code-behind or the binded via the Xaml if there is the possibility.
For the moment, I have a string variable with a Url (that I'd like very much to make clickable) binded to a TextBlock. I'd like to basically replace:
<TextBlock Text="{Binding myTextWithUrl}" />
by (in a richTB: )
<Run Text="partOfTextNonUrl" /><Hyperlink NavigateUri="theUrl" TargetName="whatever" />
Here is how it is presented:
I have an ItemsControl templated with a custom object
<ItemsControl ItemsSource="{Binding FeedResults}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" >
<my:SearchResultItem />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And this custom control presents the binded data in 3 TextBlocks as presented above: title, date, and the text containing text + urls.
I have already a method that extracts the urls from the string, I just don't know how to use it. I can generate dynamically Run() and Hyperlink(), and add them to the paragraph, but how to bind ?
Or any other solution ? You'd make my day!!
Thanks, Sylvain

OK. So apparently inline Hyperlinks aren't even allowed in Silverlight. But you can make your own!
http://csharperimage.jeremylikness.com/2009/11/inline-hyperlinks-in-silverlight-3.html
Not easy - at least not as easy at it should be. But it should get the job done.
Once you have the ability to add these runs with hyperlinks, the way I'd approach it is this. Create a user control with a single TextBlock (txtContent). Set the DataContext="{Binding myTextWithUrl}". Then in the code behind:
public TextWithUrlUserControl()
{
InitializeComponent();
this.Loaded += (s, e) =>
{
foreach(var inline in ParseText(DataContext as string))
txtContent.Inlines.Add(inline);
};
}
IEnumerable<Inline> ParseText(string text)
{
// return list of Runs and Runs with hyperlinks using your URL parsing
// for demo purposes, just hardcoding it here:
return new List<Inline>
{
new Run{Text="This text has a "},
new Run{Text="URL", RunExtender.NavigateUrl="http://www.google.com/"},
new Run{Text="in it!"}
};
}
Hope this is helpful.

I would do something like this. Create a ValueConverter which will take your text (with the URL in it). Then in your TextBlock, create the Run and Hyperlink - bind both to the text, both using the ValueConverter, but with a different parameter to the ValueConverter.
The ValueConverter:
public class MyCustomValueConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(parameter.ToString()== "URL")
{
// return the URL part of the string
}
else
{
// return the non-URL portion of the string
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then your XAML looks like this:
<Run Text="{Binding myTextWithUrl, Converter={StaticResource valueConverter}}"></Run><Hyperlink NavigateUri="{Binding myTextWithUrl, Converter={StaticResource valueConverter}, ConverterParameter=URL}"></Hyperlink>

Related

FFImageLoading has wrong behavior when resource is not found (issue or PEBKAC ?)

I use FFImageLoading to show SVG images in a ListView like this :
1) I have an XML file with categories and items, I parse it, I have an ObservableCollection, each Category is an ObservableCollection.
2) I have a custom TintedSvgCachedImage class which inherits from SvgCachedImage (just adding some tint color on a bindable property) ; I bind the Source property to a a Category.Label property, and use a converter to return an SvgImageSource.
3) If the corresponding embedded resource is not found, I catch the Exception and I return another image.
This works very well when the image is found. When not, I face 2 issues :
1) If it is the last Category in the ListView, no Exception is thrown and the images is just "empty", nothing is displayed, without any error
2) If it is not the last Category, an Exception is thrown as expected, but the replacing image is not the one I wanted to load !
My XAML file :
<ListView.GroupHeaderTemplate>
<DataTemplate x:DataType="models:Category">
<ViewCell Height="50">
<StackLayout Orientation="Horizontal">
<ffsvgimg:TintedSvgCachedImage Source="{Binding Label, Converter={StaticResource CategoryNameToSvgImageResource}}"
TintColor="Accent" />
<Label Text="{Binding Name, Converter={StaticResource StringCaseConverter}, ConverterParameter=U}"
Padding="15, 0" VerticalOptions="CenterAndExpand"
FontSize="12" FontAttributes="Bold" Opacity="0.75" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
And the converter :
// Convert Category Name (or Label) to SVG image resource to be displayed in ListView
public class CategoryNameToSvgImageResourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.GetType() != typeof(string))
throw new FormatException("CategoryNameToSvgImageResource: argument value is not of type String.");
try
{
return SvgImageSource.FromResource("CardioCALC.Resources." + value.ToString() + ".svg");
}
catch (Exception)
{
return SvgImageSource.FromResource("CardioCALC.Resources.HeartFailure.svg");
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
Does the problem come from me ? Missing something ?
Or do I have to open an issue on GitHub ? (I did not find any, but I did not search a lot as I am not sure the problem is not my bad...)
Thanks,
Olivier

How to handle ArgumentException thrown by generated XAML-code in UWP App

i've got several Textboxes in my UWP app which are bound to float properties (two-way). I use compiled bindings. At this time there is no own "code-behind" for these textboxes. Now i got the problem that the app crashes on simple mistypings (for instance if the user types letters instead of numbers). I wonder how i could handle these exceptions without modifying the generated code.
<TextBox Text="{x:Bind ViewModel.QtyGoodEntered, Mode=TwoWay}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="1" FontSize="36" x:Name="QtyGoodTextBox"/>
The App crashes when trying to convert the letters to float.
case 33: // Views\ProdFeedbackView.xaml line 191
this.obj33 = (global::Windows.UI.Xaml.Controls.TextBox)target;
(this.obj33).LostFocus += (global::System.Object sender, global::Windows.UI.Xaml.RoutedEventArgs e) =>
{
if (this.initialized)
{
// Update Two Way binding
this.dataRoot.ViewModel.QtyGoodEntered = (global::System.Double) global::Windows.UI.Xaml.Markup.XamlBindingHelper.ConvertValue(typeof(global::System.Double), this.obj33.Text);
}
Regards
Nils
ok i got following options.
Using converter (thanks to Florian Moser)
TextBoxMask Property (thanks to Marian Dolinsky)
Catching UnhandledException
Due to lack of time i stick with the converter solution but i think the TextBoxMask Property is worth a try. Catching the UnhandledException is the worst solution in my opinion.
This is what my converter looks like. In case of an exception the content defaults to 0.0. Thats all i needed.
public class StringToDoubleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
try
{
return System.Convert.ToString((double)value);
}
catch
{
return "";
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
try
{
return System.Convert.ToDouble((string)value);
}
catch
{
return 0.0;
}
}
}
And thats the XAML part
<TextBox Text="{x:Bind ViewModel.QtyGoodEntered, Mode=TwoWay, Converter={StaticResource StringToDoubleConverter}}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="1" FontSize="36" x:Name="QtyGoodTextBox"/>

Windows Phone update/refresh binding

I have a textblock in my listbox called "feedTitle" which I want to change the forground color of. I use Foreground="{Binding Converter={StaticResource NewsTextColorConverter}}" for the binding of the forground color. Now the strange problem is that, if I choose a color in the listpicker("Lys" or "Dark" value) it runs the IValueConverter Convert method, but it dont show the color in the GUI, only if I restart my whole app it shows the color I chosen. It's like it only set the color of the forground of the textblock once.
MainPage.xaml
<ListBox Grid.Row="1" Name="feedListBox" ScrollViewer.VerticalScrollBarVisibility="Auto" SelectionChanged="feedListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel VerticalAlignment="Top">
<TextBlock TextDecorations="Underline" FontSize="24" Name="feedTitle" TextWrapping="Wrap" Margin="12,0,0,0" Foreground="{Binding Converter={StaticResource NewsTextColorConverter}}" Text="{Binding Title.Text, Converter={StaticResource RssTextTrimmer}}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And in my app file:
App.xaml
<Application.Resources>
<converter:NewsTextColorConverter xmlns:converter="clr-namespace:NordjyskeRss" x:Key="NewsTextColorConverter" />
</Application.Resources>
I use a listpicker where a user select the value "Mørk" or "Lys" and then I want the textblock forground color to update its forground color. I call the Convert method and pass null as arguments, it seems to run the method fine:
MainPage.cs
private void lpkThemes_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Make sure we don't handle the event during initiation.
if (e.RemovedItems != null && e.RemovedItems.Count > 0)
{
if (this.lpkThemes.SelectedItem != null)
{
settings[THEMES_SETTING_KEY] = lpkThemes.SelectedItem.ToString();
if (lpkThemes.SelectedItem.ToString() == "Mørk")
{
n.Convert(null, null, null, null);
}
else
{
n.Convert(null, null, null, null);
}
}
}
}
This is where I use a IValueConverter to check for what color to use on the textblock and then add it:
MainPage.cs
public class NewsTextColorConverter : IValueConverter
{
protected IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
protected const string THEMES_SETTING_KEY = "Themes";
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (settings.Contains(THEMES_SETTING_KEY))
{
string themesValue = (string)settings[THEMES_SETTING_KEY];
if (themesValue == "Mørk")
{
return new SolidColorBrush(Colors.Green);
}
else
{
return new SolidColorBrush(Colors.Blue);
}
}
return new SolidColorBrush(Colors.Green);
//throw new NotSupportedException("ColorToBurshConverter only supports converting from Color and String");
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I think you need to redesign your app in the following way:
Add the following line into your app.xaml or page resources: <SolidColorBrush x:Key="brushListItemsForeground" Color="#FFFFFFFF" />
Replace Foreground="{Binding Converter={StaticResource NewsTextColorConverter}}" with Foreground="{StaticResource brushListItemsForeground}"
In your SelectionChanged:
var brush = (SolidColorBrush)Application.Current.Resources["brushListItemsForeground"]; if you’ve added the brush to app.xaml, or = (SolidColorBrush)this.Resources["brushListItemsForeground"]; if you’ve added the brush to page resources. Then change the Color property of the brush based on your settings.
P.S. There’re also other correct ways: e.g. create a SettingsContainer class that implements INotifyPropertyChanged, add it into some resource dictionary <local:SettingsContainer x:Key="mySettings" />, then bind to its properties e.g. Foreground="{Binding listItemsForeground, Source={StaticResource mySettings}}", when you need to change the value, change the listItemsForeground property of your class and raise PropertyChanged.
Currently, you’re abusing value converter using then as value providers, they were not designed for that, and that is why you have issues updating those values.

How to make IValueConverter return text with different fontsizes, superscripts and/or subscripts

Can anyone please let me know how I could make a Converter return text with varying font-sizes, so that the bound textblock can display it? If this is not possible with a TextBlock, I can use alternative element as well.
Here is the code that I have right now, this obviously doesn't work
In my XAML file:
<TextBlock Text="{Binding Converter={StaticResource LabelFormatConerter}}"/>
In my XAML.cs file:
public class LabelFormatConerter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
TextBlock tb = new TextBlock();
Run runLargeFont = new Run();
runLargeFont.FontSize = 18;
runLargeFont.Text = "Larger Font Text";
tb.Inlines.Add(runBase);
Run runSmallFont = new Run();
runSmallFont.FontSize = 8;
runSmallFont.BaselineAlignment = BaselineAlignment.Superscript;
runSmallFont.Text = "Smaller Font Text";
tb.Inlines.Add(runSmallFont);
return tb.Text;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
This should work for you:
<TextBlock FontFamily="Calibri">
<Run>Normal Text</Run>
<Run Typography.Variants="Superscript">Test</Run>
<Run Typography.Variants="Subscript">7</Run>
</TextBlock>
Not all fonts support super\subscripts, so I had to specify it explictly.
What will be your input? Two/three separate values, or one value that you need to split into a normal value, superscript and subscript?
This might be possible to do with a TextBlock but I don't know how. Your converter returns a collection of Run objects, while the Text property expects a string.
An alternative is to user an items control:
<ItemsControl ItemsSource="{Binding Converter={StaticResource LabelFormatConerter}}" />
and return
tb.Inlines
from your converter. (ideally you just create just a collection inside your converter, not a new TextBlock)
A converter is not the right tool for this job - this is what ContentTemplate is there for. Simply use a ContentControl, bind the data to the Content property and display the data however your want to in your ContentTemplate:
<ContentControl Content="{Binding Person}">
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBlock>
<Run FontSize="18" Text="{Binding FirstName}" />
<Run FontSize="8" Text="{Binding LastName}" />
</TextBlock>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>

update TextBox text format on LostFocus - WinRT

I am developing an Windows Store app using C# and XAML and am working on a user input page. I use an IValueConverter that converts my bound data into currency formatted string instead of just showing a decimal.
When the user navigates to the page, the converter works fine and the TextBox text shows up with a currency format. However, when the user changes the TextBox.Text value and then the TextBox loses focus, the converter does not change it into a nice currency format again, it just stays formatted as it was entered by the user.
As far as I can tell, there is no StringFormat to use like in WPF, so how do I get the TextBox to display the currency format again after the user edits the value?
My converter:
public object Convert(object value, Type targetType, object parameter, string language)
{
if (parameter == null)
return ((decimal)value).ToString();
return String.Format((string)parameter, (decimal)value);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
decimal decVal = 0M;
decimal.TryParse((string)value, out decVal);
return decVal;
}
My XAML:
<Grid x:Name="InputGrid">
...
<Border>
<TextBox Text="{Binding MyValue, ConverterParameter='{}{0:c}', Converter={StaticResource DecimalValueConverter}, Mode=TwoWay}"
</Border>
....
</Grid>
InputGrid.DataContext = MyValueClassInstance set in the code-behind
Does your converter work both ways? I suspect that if it doesn't - the TwoWay binding gets broken.
What if you do this:
<TextBox Text="{Binding MyValue, UpdateSourceTrigger=PropertyChanged, ConverterParameter='{}{0:c}', Converter={StaticResource DecimalValueConverter}, Mode=TwoWay}"

Categories

Resources