LookUpEdit many-to-many - xaml

I have this relation:
PRODUCT o---o< PRODUCTCATEGORY >o---o CATEGORY
Category information to show to the user: Name
This information loads from database correctly to the model (EF Core 2). The problem i can't resolve is to use LookUpEdit for choose different categories to a product and show this selection at the text control. Of course, i need to show ALL the categories to the user first, but i need to load the previosly database saved product's categories too when the product is selected.
Then essential problem is that i try to work with one criteria ( product.ProductCategory.Category ) but LookUpEdit binds at the start with all the categories:
LOOKUPEDIT XAML CODE:
<dxg:LookUpEdit ItemsSource="{Binding Categories}" IsTextEditable="False" EditValue="{Binding Product.ProductCategories, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource CategoryConverter}}">
AND CATEGORY CONVERTER CODE:
public class CategorYConverter : MarkupExtension, IValueConverter
{
public CategorYConverter() { }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
if (value is ObservableHashSet<ProductCategory>)
{
ObservableHashSet<ProductCategory> ProductCategories = value as ObservableHashSet<ProductoCategory>;
return new List<object>((ProductCategory.Select(c => c.Category)));
}
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
I can load producselected categories but i can't resolve when the user chooses a category from the lookupedit. It doesnt bind correctly to lookupedit. So i can't work with the both solutions together.

Finally i found out the problem: when the user select categories from the lookupedit category control, the ConvertBack function is called. So I only needed to 'transform' the data received to the data that expects the EditValue Binding: Product.ProductCategories. Exactly in the same format, in my case an ObservableHashSet
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
IList<object> map = value as IList<object>;
ObservableHashSet<ProductCategory> pc = new ObservableHashSet<ProductCategory>();
foreach (var m in map)
{
pc.Add(new ProductCategory() { Category = m as Category });
}
return pc;
}
Hope this helps someone

Related

Why does Xamarin.Forms databinding work for one control but not another?

I am developing a Xamarin app which I am testing on an Android device. I have a XAML view and I am binding an enum property in the viewmodel to multiple controls - one for text value, and the other to background color with an IValueConverter. Relevant XAML code:
<ContentView
BackgroundColor="{Binding MyField, Converter={StaticResource MyFieldEnumValueToColorConverter}}"
>
<ContentView.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding MyFieldClickCommand}" />
</ContentView.GestureRecognizers>
<Label
Text="{Binding MyField}"
/>
</ContentView>
IValueConverter implementation:
public class MyFieldEnumValueToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is MyFieldEnum)
{
switch ((MyFieldEnum)value)
{
case MyFieldEnum.Value1:
return Color.Orange;
case MyFieldEnum.Value2:
return Color.Green;
case MyFieldEnum.Value3:
return Color.Red;
}
}
return Color.White;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Property in the viewmodel (yes, it does implement INotifyPropertyChanged):
public event PropertyChangedEventHandler PropertyChanged;
private MyFieldEnum _myField;
public MyFieldEnum MyField
{
get => _myField;
set
{
_myField= value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyField)));
}
}
When I load this page, the controls load properly for every distinct enum value: the text and the background color both reflect the actual value.
In a click handler, I simply set the property value like this:
MyField = MyFieldEnum.Value2;
The text changes, but the background color does not. Why?
I also tried introducing a new field (of type Color), implement the IValueConverter logic directly in the getter, and bind that to the BackgroundColor attribute of the ContentView. Same issue. The page has many other data-bound items and all work properly except this one.
UPDATE: it appears that the problem is with the ContentView. I put the exact same BackgroundColor binding to the Label itself and there it works as expected, but for the ContentView it does not.

How would I default visibility of a button to false if binding is null in Xamarin

Apparantly in Xamarin forms there isn't an option to use FallbackValue or TargetNullValue, how could I use a converter to accomplish the task?
I'm looking to have visibility default to null if the data binding object is null.
NullConverter.cs
public class NullConverter : IMarkupExtension, IValueConverter
{
public object IsNullValue { get; set; }
public object IsNotNullValue { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value == null ? IsNullValue : IsNotNullValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
XAML
<Entry Text="{Binding WhateverProperty}" />
<Button IsVisible="{Binding WhateverProperty, Converter={local:NullConverter IsNullValue=False, IsNotNullValue=True}}" />
For my testing, WhateverProperty was a string that was originally set to null, whenever I update the Entry, the button shows up. Of course, you can use it with any type of property.

Return String formatted like a credit card number Using XAML And MVVM

How to set credit card number format In xaml
Like
1234-1234-1234-1234
This can be easily achieved by using Value converters.
public class CreditCardNumberValueConverter : IValueConverter
{
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var builder = new StringBuilder(Regex.Replace(value.ToString(), #"\D", ""));
foreach (var i in Enumerable.Range(0, builder.Length / 4).Reverse())
builder.Insert(4*i + 4, " ");
return builder.ToString().Trim();
}
public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Regex.Replace(value.ToString(), #"\D", "");
}
}
After initialising this in Styles.cs, you can apply it to the Text property of the control in XAML as:
Text="{Binding CardNo, Converter={StaticResource CreditCardNumberValueConverter}}"
Likewise, Phone numbers too can also be formatted.
<Label Text="{Binding YourVmPropertyNameHere StringFormat='{0:0000-0000-0000-0000}'}">
This is assuming that your view model has already converted the number to an integer and set it to the property "YourVmPropertyNameHere".

Localize with values from Dictionary of enums and strings in XAML

I´m trying to databind the current culture with the right enum set in a Dictionary in Xamarin Forms XAML.
I get a list of Events from the server that contain the Dictionary properties containing the languages that I´m going to use to show the right language depending on what Culture is set.
public class Event
{
// Having problem binding with the right CultureInfo í the Dictionary
public Dictionary<Language, string> Title { get; set; }
}
// Types of languages that I could use to pick the right language string
public enum Language
{
English = 0,
German
}
// Just a basic view-model
public class EventsViewModel
{
public EventsViewModel()
{
}
//James Montemagno´s MVVM Helper https://github.com/jamesmontemagno/mvvm-helpers
public ObservableRangeCollection<Event> Events { get; } = new ObservableRangeCollection<Event>();
}
// Just a basic Content page
public partial class EventsPage : ContentPage
{
public EventsPage ()
{
InitializeComponent();
BindingContext = new EventsViewModel(Navigation);
}
}
Now I just need the XAML to bind the right value in the Dictionary based on the enum/culture...
I have tried doing this "semi-manual" by using a Converter and checking the CultureInfo and picking the right value but for some reason I just can´t get the Converters to fire up.
I also tried to mix up IValueConverter and IMarkupExtension but no good.
Ok I found out what was missing from the Converter method I had been trying to use. I was missing the ConvertParameter and that was the reason the Convert method did´t fire.
So what I did.
1.
I put this line into the App.xaml to be able to access it from anywhere
<converters:LanguageDictionaryEnumConverter x:Key="LanguageConverter" />
2.
Then I put this one into my page. I do not use the parameter at all. (But I would if I could pass in my settings culture object)
<Label Text="{Binding Title,Converter={StaticResource LanguageConverter},ConverterParameter={x:Static dataObjects:Language.English}}" />
3.
And then I implemented a crude dictionary-language-enum converter/picker that chooses the right language string to send back to the page.
public class LanguageDictionaryEnumConverter : IMarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var dic = (Dictionary<Language, string>)value;
var cultInMySettings= Settings.Current.GetCultureInfoOrDefault;
switch (cultInMySettings.Name)
{
case "de-DE":
return dic[Language.German];
case "en-US":
return dic[Language.English];
default:
return dic[Language.English];
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
But if you have some better way to databind the values straight to the XAML in some other way I want to hear about it and will choose that answer over this one for sure if its more elegant.

Binding Indexer

I have a collection of items in my app, and I want to set the Content of a ContentPresenter to one of these items. The item will be randomly defined by an int index. I can bind an item like this:
<ContentPresenter Content={Binding Items[0]}/>
but not like this:
<ContentPresenter Content={Binding Items[{Binding Index}]}/>
I've seen a number of answers suggesting using MultiBinding in WPF, but this isn't available in UWP. Is there an alternative?
You could create a view model property, returning Items[Index]:
public string RandomItem => Items[Index];
For the PropertyChanged notifications to work, you will need to raise the event whenever Index or Items changes, e.g.:
public int Index
{
get { return _index; }
set
{
_index = value;
RaisePropertyChanged();
RaisePropertyChanged(() => RandomItem);
}
}
If you prefer to have the logic in the view and go the multi-binding way, you can use the Cimbalino toolkit. For that to work, first add 2 NuGet packages:
Cimbalino.Toolkit
Microsoft.Xaml.Behaviors.Uwp.Managed
Now you can create a converter:
public class CollectionIndexConverter : MultiValueConverterBase
{
public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var collection = (IList) values[0];
var index = (int?) values[1];
return index.HasValue ? collection[index.Value] : null;
}
public override object[] ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new System.NotImplementedException();
}
}
And use it from XAML:
<ContentPresenter>
<interactivity:Interaction.Behaviors>
<behaviors:MultiBindingBehavior PropertyName="Content" Converter="{StaticResource CollectionIndexConverter}">
<behaviors:MultiBindingItem Value="{Binding Items}" />
<behaviors:MultiBindingItem Value="{Binding Index}" />
</behaviors:MultiBindingBehavior>
</interactivity:Interaction.Behaviors>
</ContentPresenter>