IValueConverter Using Lookup Collection - c#

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;
}

Related

Dependency injection in IValueConverter MVVM Prism

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.

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.

Check if Hex string is correct in Converter

I made a text box to input and show color Hex value.The binding is twoway to a color property of a parent.
Everything is working but, I need to make sure, in case I enter manually a Hex in the text box, and if this a not correct string, then use and display the current Hex value of the color, rather than trying to change it.
Here is what I tried but obviously it's not working, I'm a beginner and I have only a little experience with converter and WPF. If I write anything but not a valid Hex string, at the moment the textbox gets a red outline, but I wish that in this case, the Hex previous string reappears.
[ValueConversion(typeof(Color), typeof(String))]
public class ColorToStringConverter : IValueConverter
{
public Object Convert(Object value, Type targetType, Object parameter, CultureInfo culture)
{
Color colorValue = (Color)value;
return ColorNames.GetColorName(colorValue);
}
public Object ConvertBack(Object value, Type targetType, Object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class ColorHexConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var hexCode = System.Convert.ToString(value);
//if (string.IsNullOrEmpty(hexCode))
// return null;
try
{
var color = (Color)ColorConverter.ConvertFromString(hexCode);
return color;
}
catch
{
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var hexCode = System.Convert.ToString(value);
Regex myRegex = new Regex("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$");
bool isValid = false;
if (string.IsNullOrEmpty(hexCode))
{
isValid = false;
}
else
{
isValid = myRegex.IsMatch(hexCode);
}
try
{
return hexCode;
}
catch
{
return null;
}
}
}
And the C# class for the TextBox
public class ColorHex : TextBox
{
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.Key == Key.Enter)
{
BindingExpression bindingExpression = BindingOperations.GetBindingExpression(this, TextProperty);
if (bindingExpression != null)
bindingExpression.UpdateSource();
}
}
}
And its xaml in Generic.xaml
<local:ColorHex x:Name="PART_ColorHex" Style="{StaticResource ColorPickerTextBox}" Text="{Binding SelectedColor, Converter={StaticResource ColorToHexConverter}, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ColorPicker}}}" />
Any idea ?
thank you
A valid hex color has the form '#nnn' or '#nnnnnn'.
So, the regex for this case would be: (^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)
NOw, you could add these lines of code:
var regex = #"(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)"
var match = Regex.Match(inputYouWantToCheck, regex,RegexOptions.IgnoreCase);
if (!match.Success)
{
// Color is not valid
}
Hope this helps.
What if you did something where you add a label next to the textbox to show an example of what color has been entered. You would just need to change the label color change each time.
I'd do it differently, using property to validate the color:
public Color Color { get; set; } = Colors.Red;
public string ColorText
{
get { return (new ColorConverter()).ConvertToString(Color); }
set
{
Color = (Color)ColorConverter.ConvertFromString(value);
OnPropertyChanged();
}
}
Bind to ColorText, it will throw in case it's wrong hex, and you can use ExceptionValidationRule to display it (as a red border):
<TextBox Text="{local:ExceptionBinding Path=ColorText}" />
where
public class ExceptionBinding : Binding
{
public ExceptionBinding() : base()
{
ValidationRules.Add(new ExceptionValidationRule());
}
}
As a bonus you can enter known colors as text, e.g. "Red":

Databinding Attribute values to a combobox

I have a set of classes with a set of properties like this; Each having a custom attribute indicating the possible values it could take. Is there anyway to databind these values to a combobox instead of hardcoding using <ComboBoxItem/> ?
[Values("Cash","Bank","Not Applicable")]
public Nullable<int> PaymentMethod{ get; set; }
Edit: My attribute looks like this
class ValuesAttribute:Attribute
{
public List<string> values { get; set; }
public ValuesAttribute(params String[] values)
{
this.values= new List<string>();
foreach (var v in values)
{
this.values.Add(v);
}
}
}
I would use a converter for this. Send it the underlying object and the property name as the parameter. Return a key/value array so that you can bind both the value (index/enum value) and display text:
<ComboBox ItemsSource="{Binding ConverterParameter='PaymentMethod',Converter={StaticResource AttributeConverter}}"
DisplayMemberPath="Value" SelectedValuePath="Key"
/>
The converter can then get the values using reflection:
public class AttributeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && parameter as string != null)
{
var property = value.GetType().GetProperty((string)parameter);
if (property != null)
{
var attribute = property.GetCustomAttributes(typeof(ValuesAttribute), false).OfType<ValuesAttribute>().FirstOrDefault();
if (attribute != null)
return attribute.values.Select((display, index) =>
new KeyValuePair<int, string>(index, display)
).ToArray();
}
}
return DependencyProperty.UnsetValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Note: if you need to do this a lot in your application, it might be worthwhile subclassing ComboBox, or creating a Behavior that applies the relevant properties.

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();
}
}

Categories

Resources