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

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"/>

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

Doing the maths of numbers in a listbox based on the first character of each item

I have a program that adds items to a list box. The items consist of a character (+,-,*,/) followed by numbers. I want to get my program to recognize the first character of the item in the list box, and do the math appropriately. However, I cannot seem to get it right. I'm not even sure what code I should be using.
Just to make it a little more clear
Items get entered into a textbox called txtCalculation using this format "+34233". That value is then added into the list box lstDisplay so after 5 items it should look like this.
+2
+4
/2
-1
+5
*3
total = 21
I would like to find a way to make the program go through the list and do the math, and then add it as a final item to the list as shown above. I already have the code to add the numbers to the list and an attempt to add them together and then put them in a textbox called txtTest, but everytime I add a new number txtTest is just set to the last number I added to the listbox instead of displaying the overall total of these numbers
Code that I have so far
double total = 0;
string line = "";
if (txtCalculation.Text.Length > 1)
{
if (e.KeyChar == (char)Keys.Enter)
{
string Number = txtCalculation.Text;
try
{
switch (Number[0])
{
case '+':
total += Convert.ToInt32(Number.Substring(1));
break;
case '-':
total -= Convert.ToInt32(Number.Substring(1));
break;
}
lstDisplay.Items.Add(Number);
txtCalculation.Text = "";
txtTest.Text = total.ToString();
}
catch (Exception ex)
{
MessageBox.Show("Error");
}
}
I would really appreciate if someone could help me fix this code up and explain to me what I am doing wrong.
I've made a small example on this:
<Window x:Class="AddTotalListConverter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:AddTotalListConverter"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<local:ComputationConverter x:Key="ComputationConverter"/>
</Grid.Resources>
<StackPanel>
<ListView x:Name="lvNumbers" ItemsSource="{Binding numbers}">
<ListView.View>
<GridView>
<GridViewColumn Header="Numbers">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<StackPanel Orientation="Horizontal" Margin="0,10">
<TextBlock Text="New Number" Margin="5,0"/>
<TextBox x:Name="tbNewNumber" MinWidth="50" Margin="5,0"/>
<Button Content="Add Number" Margin="5,0" Click="btnAddNumber_Click"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,20"/>
<TextBlock Text="{Binding numbers.Count, Converter={StaticResource ComputationConverter}, ConverterParameter={x:Reference lvNumbers}}"/>
</StackPanel>
</Grid>
This is the View. Nothing too fancy, just the ListView bounded to the ObservableCollection numbers. The TextBlock which will display the total is bounded to the items count. So, every time you add or remove an item, it will be updated. A converter is present there and its parameter is the ListView itself.
class ComputationConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int result = 0;
ListView lv = parameter as ListView;
Console.WriteLine(lv.Items);
foreach (var item in lv.Items)
{
result += (int)item;
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Again, i kept it simple without string parsings just to finish it on time, coffee time, sorry.
Indeed i work with ints, you will receive string, parse the sign and take the value also in order to continue with your case switches flow.
Here is the code behing:
public partial class MainWindow : Window
{
public ObservableCollection<int> numbers { get; set; }
public MainWindow()
{
InitializeComponent();
numbers = new ObservableCollection<int>();
this.DataContext = this;
}
private void btnAddNumber_Click(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(tbNewNumber.Text))
{
numbers.Add(int.Parse(tbNewNumber.Text));
}
}
}
If you need an MVVM approach i could make an update later, using commands and having the list in the ViewModel.
I hope this helps.
I hate to assume, but the code you provided above appears to be code inside of a method which is not provided as part of your code snip. There are two options to fix your problem, if my assumptions are correct. The first option would be to move your "total" variable out of the method and give it a class scope. By declaring the variable "total" inside your method, you are clearing it to 0 every time your method is called. Giving it a class scope should keep the value of the variable every time you call the method/event. The second option would be set the value of your "total" variable to txtTest.Text. You would have to convert txtTest.Text to a double. There are a few different ways to convert a string to a double. You can research the following methods: double.Parse(string input), double.TryParse(string input, out double output), or Convert.ToDouble(string input) to do the conversion.

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.

WPF MultiValueConverter Data Binding to string value not reference

I am new to WPF and data binding so I could have easily missed something in my research or I have been am using the wrong search terms (more likely) to find a solution.
The value of a binding seems to be getting passed and not a reference to the object so when the value gets set in the code behind it does not get updated.
In trying to generalize an OpenFileDialog to be useful on some different tabs of a tab control. I created a custom data object that holds the parameters (Path, Filter, and TextBox)
class OpenFileCommandParameters
{
public string Filter { get; set; }
public string Path { get; set; }
public string TextBox { get; set; }
}
class OpenFileCommandParamtersConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
OpenFileCommandParameters parameters = new OpenFileCommandParameters();
if (values[0] is string) parameters.Filter = (string)values[0];
if (values[1] is string) parameters.Path = (string)values[1];
if (values[2] is string) parameters.TextBox = (string)values[2];
return parameters;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
The XAML for passing the information looks like:
<TextBox Name="ButtonTagImportFileName" Text="{Binding Path=TagImportTabVM.TbFileName}" Height="23" HorizontalAlignment="Left" Margin="83,17,0,0" VerticalAlignment="Top" Width="221" />
<Button Name="TagImportOpenFile" Content="Open File" Command="{Binding Path=OpenFileCommand}" Height="23" HorizontalAlignment="Left" Margin="342,17,0,0" VerticalAlignment="Top" Width="98" >
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource openFileCommandParametersConverter}">
<MultiBinding.Bindings>
<Binding Source="XML files (*.xml)|*xml|All files (*.*)|*.*"/>
<Binding Path="AppPath"/>
<Binding Path="TagImportTabVM.TbFileName"/>
</MultiBinding.Bindings>
</MultiBinding>
</Button.CommandParameter>
Both the textbox and the Open File button have bindings to the same string property.
The property gets updated through the execution of the Command
private void OpenFile(object parameter)
{
var parameters = parameter as OpenFileCommandParameters;
FileDialog.Filter = parameters.Filter;
FileDialog.InitialDirectory = parameters.Path;
if (parameters == null) return;
var result = FileDialog.ShowDialog();
if (result == true)
{
parameters.TextBox = FileDialog.SafeFileName;
}
}
Once this command finishes I would expect the value of TbFileName to be the same as what came from the file dialog. This is not the case. as seen from a break point right before the end of the OpenFile block.
I appreciate any assistance you can offer me.
I believe this does not work because the MultiBinding never tries to update its source bindings. It may be possible to force this some how, but when I tried using
BindingOperations.GetBindingExpression(TagImportOpenFile, Button.CommandParameterProperty)
it always returned null, so I'm not sure an approach with a binding on CommandParameters would work. Getting the expression for CommandProperty works fine, CommandParameters must be different... somehow.
I think a way to do this that would definitely work would be:
a) Convert your parameters class into a ViewModel (or at least have it as INotifyPropertyChanged/DependencyObject for two-way binding), the TextBox property would probably need to be renamed to something like 'FileName'.
b) Put an instance of this class on your screens ViewModel ('TagImportTabVM' in this case?),
c) Change your XAML to something like the following:
<TextBox Name="ButtonTagImportFileName" Text="{Binding Path=OpenFileVM.FileName}" Height="23" HorizontalAlignment="Left" Margin="83,17,0,0" VerticalAlignment="Top" Width="221" />
<Button Name="TagImportOpenFile" Content="Open File" Command="{Binding Path=OpenFileCommand}" CommandParameter="{Binding OpenFileVM}"
Height="23" HorizontalAlignment="Left" Margin="342,17,0,0" VerticalAlignment="Top" Width="98" />
This is assuming that you don't mind setting the file filter in code when you make the new instance of the parameters (since I assumed you did something like this for AppPath anyway).
I was pretty much to the solution, and #a little sheep helped direct me the rest of the way with his solution. Just wanted to fully wrap up the question.
I was thinking that because the source was not getting updated that I was breaking the binding when I was setting the value through code. This is not the case. It was just simply not knowing that the value had changed so it did not know to update the source.
Once I discovered this, it was straight forward to find out how to notify the system to let it know it needed to update the source. This gets added to the private void OpenFile(object parameter)
BindingExpression binding = BindingOperations.GetBindingExpression(parameters.PassedTextBox, TextBox.TextProperty);
binding.UpdateSource();
Thats all that was added to get it to update properly.

Dynamic custom content in RichTextBox

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>

Categories

Resources