Dependency injection in IValueConverter MVVM Prism - c#

I Want to pass Dependency injection in IValueConverter in MVVM (Prism).
I used ServiceLocator, but it not working with .Net Core 3.1.
please let me if any way to do Dependency injection in IValueConverter.
public class RowToColorConverter : MarkupExtension, IValueConverter
{
private ICommonInventoryReference _commonInventoryReference;
/// <summary>
/// back Event Args Converter
/// </summary>
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (_commonInventoryReference == null)
{
_commonInventoryReference = null;
} // CommonServiceLocator.ServiceLocator.Current.GetInstance<CommonInventoryReference>(); }
if (_commonInventoryReference == null || !(value is Inventory inventory)) { return Brushes.White; }
var brand = _commonInventoryReference.LoadBrandsByIdOrName(new BrandModel() { Name = inventory.Brand });
if (brand == null || string.Compare(brand.IsBrand, "YES", StringComparison.OrdinalIgnoreCase) == 0)
{
return Brushes.White;
}
var color = (Color)ColorConverter.ConvertFromString("#edecbb");
return new SolidColorBrush(Color.FromArgb(color.A, color.R, color.G, color.B));
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

Use ContainerLocator instead, as seen in the relase notes.

Related

IValueConverter Using Lookup Collection

I am using a converter like this:
public class BreedConverter : IValueConverter
{
static ObservableCollection<Breed_> Breeds = Breed_.GetBreeds();
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && Breeds.Count > 0)
{
short breedID = (short)value;
Breed_ breed = Breeds.Single(s => s.BreedID == breedID);
return (string)breed.Breed;
}
else
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
The Breeds collection is retrieved from a SQL Server database. I want to retrieve it once and then use it to do the conversion. I don't want to go to the database each and every time I need to convert.
Is there a better way to do this, e.g. ResourceDictionary (which I don't know how to use in this scenario, since I'm still a noob)?
This is what I have been able to get to work. As #Darthchai mentioned, I have created the collection in the class/window. However, I cannot figure out how to use INotifyPropertyChanged to let the specific TextBlock know to do the conversion. I am a noob, after all. :) What I was able to do - and I don't know if this is the right approach - was to use the Loaded event of the TextBlock to do the conversion. here is the code:
private void BreedTextBlock_Loaded(object sender, RoutedEventArgs e)
{
TextBlock textBlock = (TextBlock)sender;
if (textBlock != null && Breeds.Count > 0)
{
try
{
short breedID = short.Parse(textBlock.Text);
Breed_ breed = Breeds.Single(s => s.BreedID == breedID);
textBlock.Text = (string)breed.Breed;
}
catch
{
}
}
return;
}

Datepicker ValidationRules from code behind: validation rule not called on user input

I'm creating a wpf UserControl that contains a Datepicker. This datepicker is generated from code behind in c#.
public partial class EditorDatePicker : UserControl
{
public EditorDatePicker(TagEntry element, bool isTagPresent)
{
InitializeComponent();
// datepicker binding and validation
Binding binding = new Binding();
binding.Path = new PropertyPath("DateDict[" + element.ParentTag + element.ChildTag + "]");
binding.NotifyOnValidationError = true;
binding.ValidatesOnDataErrors = true;
binding.Converter = new DateTimeConverter();
binding.Mode = BindingMode.TwoWay;
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
binding.ValidationRules.Add(new DateValidationRule());
this.datePicker.SetBinding(DatePicker.SelectedDateProperty, binding);
}
class DateTimeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null)
{
try
{
DateTime test = (DateTime)value;
string date = test.ToString("d/M/yyyy");
return (date);
}
catch
{
return null;
}
}
return null;
}
The fact is that the validation rule is never called when I manualy enter a date in the DatePicker text field (But it's called when using the datepicker). The only thing I got is a FormatException on lost focus.
Any idea? Thanx.
One possibility is to use converter:
public class DateTimeNullConverter : MarkupExtension, IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider) => this;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is DateTime)
return value.ToString();
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var text = value as string;
DateTime result;
if (text != null && DateTime.TryParse(text, out result))
return result;
return null;
}
}
You can use it like this to bind to public DateTime? DateTime property:
<TextBox Text="{Binding DateTime, Converter={local:DateTimeNullConverter}}" />
ConvertBack will be called on lost focus.

Converter cannot be applied to a property that expects the type System.Windows.Data.IValueConverter

I am having a problem in my WPF app that I do not quite understand. I am trying to bind a fill to a different color depending on the value of a certain property.
Here are the snippets involved:
public class GeoLocations
{
private static ObservableCollection<Bus> _locations = new ObservableCollection<Bus>();
public static ObservableCollection<Bus> locations
{
get { return _locations; }
set
{
_locations = value;
}
}
.
.
.
}
public class Bus : INotifyPropertyChanged
{
private double _VSAI;
public double VSAI
{
get
{
return _VSAI;
}
set
{
_VSAI = value;
OnPropertyChanged(new PropertyChangedEventArgs("VSAI"));
}
}
public class VsaiToColorConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double vsai = (double)value;
if (vsai < App.Settings.medVSAI)
return Brushes.Green;
if (vsai >= App.Settings.medVSAI && vsai <= App.Settings.maxVSAI)
return Brushes.Yellow;
if (vsai > App.Settings.maxVSAI)
return Brushes.Red;
return Brushes.Transparent;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then in my xaml I have the following:
in my resources I have
<local:GeoLocations x:Key="geolocs"/>
and then I have a map
<ig:GeographicProportionalSymbolSeries x:Name="ProportionalSeries"
LongitudeMemberPath="Longitude"
LatitudeMemberPath="Latitude"
ItemsSource="{Binding Source={StaticResource geolocs}, Path=locations}"
>
<!-- custom marker template for GeographicProportionalSymbolSeries -->
<ig:GeographicProportionalSymbolSeries.MarkerTemplate>
<DataTemplate>
<Ellipse x:Name="RootElement" Width="10" Height="10"
Stroke="DarkGray"
Opacity=".8"
StrokeThickness="0.5"
Fill="{Binding Path=Item.VSAI, Converter={StaticResource VSAIConverter}}">
</Ellipse>
</DataTemplate>
</ig:GeographicProportionalSymbolSeries.MarkerTemplate>
But the error I'm getting is on the FILL=..... above. I'm hoping this is an easy fix. I'm just a little too new still to understand how to fix this and what this error means.
Your converter needs to implement the IValueConverter interface. You've implemented the two methods but you omitted the IValueConverter interface so as far as the CLR is concerned, you just have a class that happens to have the same methods as IValueConverter but isn't actually implementing it.
public class VsaiToColorConverter : IValueConverter
You generally want to handle null value cases as well
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if(value == null) return Brushes.Transparent; //or null
double vsai = (double)value;
//..
}

How do I implement a CollectionLengthToVisibility converter?

I want to implement a converter so that certain XAML elements only appear/disappear if there are items in an ObservableCollection.
I have referenced How to access generic property without knowing the closed generic type but cannot get it to work with my implementation. It build and deploys OK (to Windows Phone 7 emulator and device) but does not work. Moreover Blend throws an exception and will no longer render the page,
NullReferenceException: Object reference not set to an instance of an
object.
Here is what I have so far,
// Sets the vsibility depending on whether the collection is empty or not depending if parameter is "VisibleOnEmpty" or "CollapsedOnEmpty"
public class CollectionLengthToVisibility : System.Windows.Data.IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo cultureInfo)
{
// From https://stackoverflow.com/questions/4592644/how-to-access-generic-property-without-knowing-the-closed-generic-type
var p = value.GetType().GetProperty("Length");
int? length = p.GetValue(value, new object[] { }) as int?;
string s = (string)parameter;
if ( ((length == 0) && (s == "VisibleOnEmpty"))
|| ((length != 0) && (s == "CollapsedOnEmpty")) )
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo cultureInfo)
{
return null;
}
}
Here is how I referenced the converter on Blend/XAML
<TextBlock Visibility="{Binding QuickProfiles, ConverterParameter=CollapsedOnEmpty, Converter={StaticResource CollectionLengthToVisibility}}">Some Text</TextBlock>
I would use the Enumerable.Any() extension method. It will work on any IEnumerable<T> and avoids you having to know what sort of collection you're dealing with. Since you don't know T you can just use .Cast<object>()
public class CollectionLengthToVisibility : System.Windows.Data.IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo cultureInfo)
{
var collection = value as System.Collections.IEnumerable;
if (collection == null)
throw new ArgumentException("value");
if (collection.Cast<object>().Any())
return Visibility.Visible;
else
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo cultureInfo)
{
throw new NotImplementedException();
}
}

Binding converter as inner class?

I have a UserControl that uses a binding converter. I've made the converter an inner class of
public partial class MyPanel : UserControl
{
public class CornerRadiusConverter : IValueConverter
{
How do I reference the Converter class from the XAML? The following does not work:
<controls:MyPanel.CornerRadiusConverter x:Key="CornerRadiusConverter" />
It gives this error:
The tag
'LensPanel.CornerRadiusConverter' does
not exist in XML namespace
'clr-namespace:MyApp.Windows.Controls'
I was thinking about this problem again, and I came up with something similar to Dennis's solution : create a "proxy" converter class, with a Type property, which will create the instance of the actual converter and delegate the conversion to it.
public class Converter : IValueConverter
{
private Type _type = null;
public Type Type
{
get { return _type; }
set
{
if (value != _type)
{
if (value.GetInterface("IValueConverter") != null)
{
_type = value;
_converter = null;
}
else
{
throw new ArgumentException(
string.Format("Type {0} doesn't implement IValueConverter", value.FullName),
"value");
}
}
}
}
private IValueConverter _converter = null;
private void CreateConverter()
{
if (_converter == null)
{
if (_type != null)
{
_converter = Activator.CreateInstance(_type) as IValueConverter;
}
else
{
throw new InvalidOperationException("Converter type is not defined");
}
}
}
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
CreateConverter();
return _converter.Convert(value, targetType, parameter, culture);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
CreateConverter();
return _converter.ConvertBack(value, targetType, parameter, culture);
}
#endregion
}
You use it like that :
<Window.Resources>
<my:Converter x:Key="CornerRadiusConverter" Type="{x:Type controls:MyPanel+CornerRadiusConverter}"/>
</Window.Resources>
It could be possible. A few months ago I wrote a markup extension to create the converter for you inline. It keeps a dictionary of weak references so that you don't create multiple instances of the same converter. Handles creating converters with different arguments too.
In XAML:
<TextBox Text="{Binding Converter={NamespaceForMarkupExt:InlineConverter {x:Type NamespaceForConverter:ConverterType}}}"/>
C#:
[MarkupExtensionReturnType(typeof(IValueConverter))]
public class InlineConverterExtension : MarkupExtension
{
static Dictionary<string, WeakReference> s_WeakReferenceLookup;
Type m_ConverterType;
object[] m_Arguments;
static InlineConverterExtension()
{
s_WeakReferenceLookup = new Dictionary<string, WeakReference>();
}
public InlineConverterExtension()
{
}
public InlineConverterExtension(Type converterType)
{
m_ConverterType = converterType;
}
/// <summary>
/// The type of the converter to create
/// </summary>
/// <value>The type of the converter.</value>
public Type ConverterType
{
get { return m_ConverterType; }
set { m_ConverterType = value; }
}
/// <summary>
/// The optional arguments for the converter's constructor.
/// </summary>
/// <value>The argumments.</value>
public object[] Arguments
{
get { return m_Arguments; }
set { m_Arguments = value; }
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
PropertyInfo propertyInfo = target.TargetProperty as PropertyInfo;
if (!propertyInfo.PropertyType.IsAssignableFrom(typeof(IValueConverter)))
throw new NotSupportedException("Property '" + propertyInfo.Name + "' is not assignable from IValueConverter.");
System.Diagnostics.Debug.Assert(m_ConverterType != null, "ConverterType is has not been set, ConverterType{x:Type converterType}");
try
{
string key = m_ConverterType.ToString();
if (m_Arguments != null)
{
List<string> args = new List<string>();
foreach (object obj in m_Arguments)
args.Add(obj.ToString());
key = String.Concat(key, "_", String.Join("|", args.ToArray()));
}
WeakReference wr = null;
if (s_WeakReferenceLookup.TryGetValue(key, out wr))
{
if (wr.IsAlive)
return wr.Target;
else
s_WeakReferenceLookup.Remove(key);
}
object converter = (m_Arguments == null) ? Activator.CreateInstance(m_ConverterType) : Activator.CreateInstance(m_ConverterType, m_Arguments);
s_WeakReferenceLookup.Add(key, new WeakReference(converter));
return converter;
}
catch(MissingMethodException)
{
// constructor for the converter does not exist!
throw;
}
}
}
What I do is:
<Window.Resources>
<ResourceDictionary>
<Converters:BooleanNotConverter x:Key="BooleanNotConverter"/>
</ResourceDictionary>
</Window.Resources>
And then in the control
<CheckBox IsChecked="{Binding Path=BoolProperty, Converter={StaticResource BooleanNotConverter} />

Categories

Resources