I am new to WPF MVVM. Here's what my viewmodel is doing:
A button is pressed and a ping command is launched to see if servers are availables:
-If true, the button is set to Hidden.
-If false, a label with a message ("Servers not availables) is set to
visible
How can I reuse the following IsVisible method to set the Label's visibility?
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Input;
namespace WpfTest
{
public class PrnViewModel1 : ViewModelBase
{
private ICommand m_PrnServPingCommand;
private Visibility _isVisible=Visibility.Visible;
public PrnViewModel1()
{
PrnServPingCommand = new RelayCommand(new Action<object>(PrnServPing));
}
public ICommand PrnServPingCommand
{
get
{
return m_PrnServPingCommand;
}
set
{
m_PrnServPingCommand = value;
}
}
public void PrnServPing(object obj)
{
string[] serverNames = { "svmsimp1", "svmsimp2" };
bool serversArePingable = Cmethods.PingableAll(serverNames);
if (serversArePingable)
{
IsVisible = Visibility.Hidden; //Button is Hidden
}
else
{
//*** Label with Message "Servers not pingable" set to visible
}
}
public Visibility IsVisible
{
get
{
return _isVisible;
}
set
{
_isVisible = value;
OnPropertyChanged("IsVisible");
}
}
}
}
You can use an IValueConverter to reverse the Visibility on your label.
Here is an example implementation:
public static class Extensions
{
public static System.Windows.Visibility Reversed(this System.Windows.Visibility visibility)
{
if (visibility == System.Windows.Visibility.Visible)
visibility = System.Windows.Visibility.Collapsed;
else if (visibility == System.Windows.Visibility.Collapsed || visibility == System.Windows.Visibility.Hidden)
visibility = System.Windows.Visibility.Visible;
return visibility;
}
}
public class VisibilityReversedConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ((System.Windows.Visibility)value).Reversed();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ((System.Windows.Visibility)value).Reversed();
}
}
Usage:
<TextBlock Text="Servers not available" Visibility="{Binding Visibility, Converter={StaticResource VisibilityReversedConverter}}" ... />
Don't forget to instantiate the converter in the Resources. See here for a tutorial on converters.
I myself often use Visibility type in ViewModel (to avoid writing converters to control layout, because Hidden != Collapsed).
What you need here is either 2 properties to control label and button visibility or single property (bool) to switch which one is displayed and converter (with parameter to invert or just 2 converters).
Here is solution which uses converter and parameter (bonus - MarkupExtension!):
public class SuperConverter : MarkupExtension, IValueConverter
{
public SuperConverter() { }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool)
{
bool inverted = false;
if (parameter != null && parameter.ToString().Contains("Inverted"))
inverted = true;
return (inverted && (bool)value || !inverted && !((bool)value)) ? Visibility.Hidden : Visibility.Visible;
}
throw new InvalidCastException();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and use it (you have to have bool IsOperationInProgress in ViewModel, which value will toggle display of either text or button):
<TextBlock Visibility="{Binding IsOperationInProgress, Converter={l:SuperConverter}}" ... />
<Button Visibility="{Binding IsOperationInProgress, Converter={l:SuperConverter}, ConverterParameter=Inverted}" ... />
Related
I have defined an enum type detailing various color palettes for colorizing grayscale images, for which I am using Description attributes and a TypeConverter in order to use the description strings of the enum values for comboboxes, list boxes etc. that I am binding to this type. The enum looks like this:
// available color palettes for colorizing 8 bit grayscale images
[TypeConverter(typeof(EnumDescriptionTypeConverter))]
public enum ColorPalette
{
[Description("Alarm Blue")]
AlarmBlue,
[Description("Alarm Blue High")]
AlarmBlueHi,
[Description("Alarm Green")]
AlarmGreen,
[Description("Alarm Red")]
AlarmRed,
[Description("Fire")]
Fire,
[Description("Gray BW")]
GrayBW,
[Description("Ice 32")]
Ice32,
[Description("Iron")]
Iron,
[Description("Iron High")]
IronHi,
[Description("Medical 10")]
Medical10,
[Description("Rainbow")]
Rainbow,
[Description("Rainbow High")]
RainbowHi,
[Description("Temperature 256")]
Temperature256,
[Description("Nano Green")]
NanoGreen
};
The EnumDescriptionTypeConverter looks like this:
public class EnumDescriptionTypeConverter : EnumConverter
{
public EnumDescriptionTypeConverter(Type type) : base(type) { }
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
if (value != null)
{
FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
if (fieldInfo != null)
{
var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
return ((attributes.Length > 0) && (!string.IsNullOrEmpty(attributes[0].Description))) ? attributes[0].Description : value.ToString();
}
}
return string.Empty;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
Using this, I can bind the enum type to say, a combo box's ItemsSource property and have the description strings be used automatically as the combo box elements, using another custom markup extension class the code of which I don't believe is relevant here.
The problem is, that if I try to create a public dependency property on a custom control based on this enum type, it won't work. Here's an example custom control:
public class TestControl : Control
{
public ColorPalette Test1
{
get => (ColorPalette)GetValue(Test1Property);
set => SetValue(Test1Property, value);
}
public static readonly DependencyProperty Test1Property = DependencyProperty.Register(nameof(Test1), typeof(ColorPalette),
typeof(TestControl), new PropertyMetadata
{
DefaultValue = ColorPalette.Rainbow
});
}
This code compiles without error and I can put the TestControl into a window, until I try to set the value of the test property in the XAML - then I don't get the usual IntelliSense containing the enum values and when I try to manually set a value anyway, I get an Access Violation exception as soon as I run the application, right at the InitializeComponent() method of the MainWindow:
" Exception thrown at 0x00007FF84723A799 (KernelBase.dll) in .exe: 0xC0000005: Access violation reading location 0x0000000000000008. occurred "
This does not happen when I remove the TypeConverter attribute from the enum definition, but then of course the Description string binding doesn't work any more.
I don't know enough about WPF to realize what exactly the problem is. Is there a way to avoid this, and still use the TypeConverter for binding using the Description string attributes?
So I found a workaround by using a different kind of MarkupExtension as binding source for enum types:
public class EnumDescriptionBindingSourceExtension : MarkupExtension
{
public Type EnumType
{
get => enumType;
set
{
if (enumType != value)
{
if (value != null)
{
Type type = Nullable.GetUnderlyingType(value) ?? value;
if (!type.IsEnum)
throw new ArgumentException("Type must be an enum type");
}
enumType = value;
}
}
}
private Type enumType;
public EnumDescriptionBindingSourceExtension() { }
public EnumDescriptionBindingSourceExtension(Type enumType) => this.enumType = enumType;
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (enumType == null)
throw new InvalidOperationException("The enum type must be specified");
Type actualEnumType = Nullable.GetUnderlyingType(enumType) ?? enumType;
Array enumValues = Enum.GetValues(actualEnumType);
if (actualEnumType == enumType)
{
List<string> descriptions = new List<string>(enumValues.Length);
foreach (object value in enumValues)
{
FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
if (fieldInfo != null)
{
DescriptionAttribute[] attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
descriptions.Add(((attributes.Length > 0) && !string.IsNullOrEmpty(attributes[0].Description)) ? attributes[0].Description : value.ToString());
}
}
return descriptions;
}
else
{
Array tempArray = Array.CreateInstance(actualEnumType, enumValues.Length + 1);
enumValues.CopyTo(tempArray, 1);
return tempArray;
}
}
}
This extension returns an array of the description strings (if any, otherwise just value.ToString()) of the enum values. When using this in XAML bindings, I can have my combo boxes be filled with the enum value descriptions directly, while previously I would use a markup extension that would just return an array of the enum values themselves and have the conversion to their description strings be done by the TypeConverter.
When using this new markup extension, I have to use a converter that can determine an original enum value from its description string:
public class EnumDescriptionConverter : IValueConverter
{
object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Enum enumObject)
{
FieldInfo fieldInfo = enumObject.GetType().GetField(enumObject.ToString());
object[] attributes = fieldInfo.GetCustomAttributes(false);
if (attributes.Length == 0)
return enumObject.ToString();
else
{
DescriptionAttribute attribute = attributes[0] as DescriptionAttribute;
return attribute.Description;
}
}
else
throw new ArgumentException($"Conversion is only defined for enum types");
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string valString)
{
Array enumValues = targetType.GetEnumValues();
FieldInfo fieldInfo;
DescriptionAttribute[] attributes;
string target;
foreach (object enumValue in enumValues)
{
fieldInfo = enumValue.GetType().GetField(enumValue.ToString());
if(fieldInfo != null)
{
attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
target = ((attributes.Length == 1) && !string.IsNullOrEmpty(attributes[0].Description)) ? attributes[0].Description : enumValue.ToString();
if (valString == target)
return enumValue;
}
}
throw new ArgumentException($"Back-conversion failed - no enum value corresponding to string");
}
else
throw new ArgumentException($"Back-conversion is only defined for string type");
}
}
With both of these I can do for example the following in XAML:
<ns:EnumDescriptionConverter x:Key="enumDescriptionConverter"/>
(...)
<ComboBox ItemsSource="{Binding Source={ns:EnumDescriptionBindingSource {x:Type ns:MyEnumType}}, Mode=OneTime}" SelectedItem="{Binding MyEnumTypeProperty, Converter={StaticResource enumDescriptionConverter}}"/>
Which will automatically fill the combo box with the enum values, represented by their description strings, and bind the selected item to a property of that type. This then works without setting the TypeConverter attribute on the enum definition and thus my original problem doesn't occur.
I'm still none the wiser why it happened in the first place or if there's a better way to solve it but hey, it works.
do you must use dependency property?
For this cases I used ViewModel with Enum object and IValueConverter in XAML code
example of ViewModel for Enum type
public abstract class VM_PropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChange(string propertyName)
{
var handler = PropertyChanged;
if (PropertyChanged != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class VM_EnumItem<T> : VM_PropertyChanged
{
public T Enum { get; }
public bool IsEnabled
{
get { return isEnabled; }
set { isEnabled = value; OnPropertyChange(nameof(IsEnabled)); }
}
private bool isEnabled;
public VM_EnumItem(T Enum, bool IsEnabled)
{
this.Enum = Enum;
this.IsEnabled = IsEnabled;
}
public override int GetHashCode()
{
return Enum.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj != null && obj is VM_EnumItem<T> item)
return System.Enum.Equals(item.Enum, this.Enum);
return false;
}
public override string ToString()
{
return string.Format("{0} | {1}", Enum, IsEnabled);
}
}
example of ViewModel for WPF Control
class ViewModel : VM_PropertyChanged
{
public enum ColorPalette
{
[Description("Alarm Blue")]
AlarmBlue,
[Description("Alarm Blue High")]
AlarmBlueHi
}
// all options
public ObservableCollection<VM_EnumItem<ColorPalette>> EnumItems { get; } = new ObservableCollection<VM_EnumItem<ColorPalette>>()
{
new VM_EnumItem<ColorPalette>(ColorPalette.AlarmBlue, true),
new VM_EnumItem<ColorPalette>(ColorPalette.AlarmBlueHi, true)
};
public VM_EnumItem<ColorPalette> SelectedEnumItem
{
get { return EnumItems.Where(s => s.Enum == SelectedEnum).FirstOrDefault(); }
set { SelectedEnum = value.Enum; OnPropertyChange(nameof(SelectedEnumItem)); }
}
private ColorPalette SelectedEnum; // your selected Enum
}
example of Converter
public class VM_Converter_EnumDescription : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Type type = value.GetType();
if (!type.IsEnum)
return value;
string name = Enum.GetName(type, value);
FieldInfo fi = type.GetField(name);
DescriptionAttribute descriptionAttrib = (DescriptionAttribute)Attribute.GetCustomAttribute(fi, typeof(DescriptionAttribute));
return descriptionAttrib == null ? value.ToString() : descriptionAttrib.Description;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
example of WPF Control
<Window.Resources>
<ResourceDictionary >
<local:VM_Converter_EnumDescription x:Key="Converter_EnumDescription"/>
</ResourceDictionary>
</Window.Resources>
////////////
<ComboBox
ItemsSource="{Binding Path=EnumItems, Mode=OneWay}"
SelectedItem="{Binding Path=SelectedEnumItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ComboBox.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Path=Enum, Converter={StaticResource Converter_EnumDescription}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="IsEnabled" Value="{Binding Path=IsEnabled}"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
I'm using the Xceed PropertyGrid with data binding and AutoGenerateProperties = true. I have nullable properties like described below that result in a strange UI behavior.
The grid lets me click on the values Yes and No but the null choice is slightly obsecured by the property grid and won't allow me to click on it to select it. If I select Yes and use the UP Arrow key I'm able to select it. The Microsoft property grid full shows the empty choice and allows me to click it.
Am I doing something wrong or is this a bug? I asked in GitHub Issues but have had no responses to my issue.
YesNo? _compressed;
[CategoryAttribute("Package")]
[Description("Set to 'yes' to have compressed files in the source. This attribute cannot be set for merge modules. ")]
public YesNo? Compressed { get { return _compressed; } set { _compressed = value; RaisePropertyChangedEvent("Compressed"); } }
This is not a bug really. If you want to display the value of default(YesNo?) as something else than an empty string or null, you need to define how you want it to be displayed somehow. You could do this by creating your own custom editor:
public class CustomEditor : Xceed.Wpf.Toolkit.PropertyGrid.Editors.ComboBoxEditor
{
protected override IValueConverter CreateValueConverter()
{
return new CustomValueConverter<T>();
}
protected override ComboBox CreateEditor()
{
ComboBox comboBox = base.CreateEditor();
FrameworkElementFactory textBlock = new FrameworkElementFactory(typeof(TextBlock));
textBlock.SetBinding(TextBlock.TextProperty, new Binding(".") { Converter = new CustomValueConverter<T>() });
comboBox.ItemTemplate = new DataTemplate() { VisualTree = textBlock };
return comboBox;
}
protected override IEnumerable CreateItemsSource(Xceed.Wpf.Toolkit.PropertyGrid.PropertyItem propertyItem)
{
return new string[1] { CustomValueConverter<T>.Null }
.Concat(Enum.GetValues(typeof(T)).OfType<T>().Select(x => x.ToString()));
}
}
public class CustomValueConverter: IValueConverter
{
internal const string Null = "";
public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return Null;
return value.ToString();
}
public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture)
{
string s = value?.ToString();
if (s == Null)
return null;
return Enum.Parse(typeof(T), s);
}
}
Usage:
YesNo? _compressed;
[CategoryAttribute("Package")]
[Description("Set to 'yes' to have compressed files in the source. This attribute cannot be set for merge modules. ")]
[Editor(typeof(CustomEditor<YesNo>), typeof(CustomEditor<YesNo>))]
public YesNo? Compressed { get { return _compressed; } set { _compressed = value; RaisePropertyChangedEvent("Compressed"); } }
I have a combo-box im populating with a collection of objects in my ViewModel.
<ComboBox x:Name="ChangelistComboBox"
SelectedIndex="{Binding MyObjectSelectionIndex, Mode=TwoWay}"
ItemsSource="{Binding MyObjectList, Mode=OneWay}"
Margin="5"
Grid.Column="0">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource MyObjectToComboBoxConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Im using a Converter to convert parameters from the object to a displaystring that shows up in the ComboBox
[ValueConversion(typeof(MyObject), typeof(string))]
class MyObjectToComboBoxConverter : ValueConverterBase
{
public override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
try {
MyObject theObject = (MyObject)value;
int id = theObject.Id;
return (((id != -1) ? id.ToString() : "default") + " : " + theObject.Description);
} catch(InvalidCastException e) {
return (String)value;
}
}
public override object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return new MyObject(); //Not used
}
}
abstract class ValueConverterBase : IValueConverter
{
public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
return DependencyProperty.UnsetValue;
}
public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
return DependencyProperty.UnsetValue;
}
}
In the Model the list is defined as:
private ObservableCollection<MyObject> _MyObjectList;
public ObservableCollection<MyObject> MyObjectList {
get
{
if (_MyObjectList != null) { return _MyObjectList; } else { return new ObservableCollection<MyObject>(); }
}
set
{
if (_MyObjectList != value) {
_MyObjectList = value;
NotifyPropertyChanged("MyObjectList");
}
}
}
In the ViewModel the MyObjectList is simply referenced from the model through the interface:
public ObservableCollection<MyObject> MyObjectList {
get
{
if (Model != null) {
return Model.MyObjectList;
} else {
return new ObservableCollection<MyObject>();
}
}
}
Without the TryCatch, this converter crashes when my MyObjectList is updated. It gives an error like Cannot cast type string to object on the MyObject theObject = (MyObject)value; line
With the TryCatch the converter works as intended. It even returns a correctly assembled string. The problem is I get InvalidCastExceptions in the error log, which isnt good. Also I have no idea why it works despite the exception.
The only hunch I have is that for some reason the object is being converted twice, once from object to string and then it tries to convert the string to string and fails there. I cant figure out why it would be doing that though.
MVVM IValueConverter Convert method getting empty string argument when expecting a float
Just found this which seems to be the same issue that I'm experiencing. I dont fully understand the explanation for it given there, but if modifying the Convert to accept an empty string is a viable solution i can live with that
Still feels like a hack but beggars cant be choosers i guess
You bind the ComboBox source to a list of MyObject, so the value should never be of type string. I guess there is a problem in your model state.
The way you are binding your view is correct, also the first version of the converter:
[ValueConversion(typeof(MyObject), typeof(string))]
class MyObjectToComboBoxConverter : IValueConverter
{
public override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
try {
var theObject = (MyObject)value;
var id = theObject.Id;
return (((id != -1) ? id.ToString() : "default") + " : " + theObject.Description);
} catch(InvalidCastException e) {
return (string)value;
}
}
public override object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
Please start to change your ViewModel to:
public ObservableCollection<MyObject> MyObjectList { get; set; } = new ObservableCollection<MyObject>();
You need to implement the OnPropertyChangeEvent on the properties of the MyObject model (https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-create-and-bind-to-an-observablecollection)
so you only need to set the ObservableCollection in your ViewModel to an empty instance by default.
Then adding items:
MyObjectList.Add(new MyObject{ id=1, Description="First"});
I have a HierarchicalDataTemplate that holds a number of items and has a small image next to each item. It is the visibility of these images I want to bind. Whether an item's image should be visible is dependent on whether the item appears in a DataTable on a server.
In order to determine an item's visibility, I need both the ID number of that item and the interface that accesses the server. The interface is kept as a field in the ViewModel.
Ideally I'd just have this method in my ViewModel, if I could bind based on the item's ID:
public Visibility ItemIsInTable(int ID)
{
string sql = "SELECT ID FROM MyTable WHERE ID = " + ID;
DataTable dataTable = serverInterface.FetchDataFromDatabase(sql);
if (dataTable.Rows.Count > 0)
return Visibility.Visible;
else
return Visibility.Collapsed;
}
How can my binding rely on both the item ID and my ViewModel Interface?
Your ViewModel Property should be boolean, and you will use a converter.
public bool ItemIsInTable {get { ... } set { ...; OnPropertyChanged("ItemIsInTable"); }}
[Edit]
Ok.
You must have a ObservableColletction of Items (in my example ItemsSource)
class ItemSource: INotifyPropertyChanged
{
public int Id {get { ... } set { ...; OnPropertyChanged("Id"); }}
public bool ItemIsInTable {get { ... } set { ...; OnPropertyChanged("ItemIsInTable"); }}
....
}
In your ViewModel:
ObservableColletction<ItemSource> ItemsSource {get { ... } set { ...; OnPropertyChanged("ItemsSource"); }}
Now you should verify the Id
foreach(var item in ItemsSource)
item.ItemIsInTable = MethodToVerifyIdInDataBase(item.Id);
[/Edit]
XAML like this:
<HierarchicalDataTemplate ItemsSource="{Binding ItemSource}" >
<Grid Visibility="{Binding ItemIsInTable, Converter={StaticResource BoolToVisibilityConverter}}" >
....
</Grid>
</HierarchicalDataTemplate>
App.Xaml:
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
Converter class:
public class BoolToVisibilityConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var rv = Visibility.Visible;
try
{
if (value != null)
rv = (bool)value ? Visibility.Visible : Visibility.Collapsed;
}
catch
{
// ignored
}
return rv;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value != null) && (value == (object)Visibility.Visible);
}
}
I need to bind color to fill the rectangle.
XAML:
<Rectangle Fill="{Binding Colorr}"
VerticalAlignment="Center"
Height="3" Width="16"
Margin="3, 1, 5, 0"
Visibility="Visible"/>
ViewModel:
public ItemViewModel()
{
Colorr = Colors.Red;;
}
public Color Colorr
{
get {
return color; }
set
{
color = value;
NotifyOfPropertyChange(() => Colorr);
}
}
The resulting rectangle is not visible (or is transparent - it's hard to say...) instead of being visible and red. How can I get rid of this problem?
The other way around is to use ColorToBrushConverter, just like the one below:
using System.Windows.Data;
using System.Windows.Media;
public class ColorToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new SolidColorBrush((Color)value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value as SolidColorBrush).Color;
}
}
Then in XAML define the converter as resource and use it like this:
<Rectangle Fill="{Binding Colorr, Converter={StaticResource ColorToBrushConverter}}"/>
Rectangle.Fill (which it inherits from Shape) is a Brush, not a Color. So make your property a Brush instead:
private Brush _colorr = Brushes.Red;
public Brush Colorr
{
get
{
return _colorr;
}
set
{
_colorr = value;
NotifyOfPropertyChange(() => Colorr);
}
}
There may be other problems, but you need to fix this one first.
I have made minor changes to the Color BrushConverter proposed by Vaidas's to take care of the possible null reference exception:
using System.Windows.Data;
using System.Windows.Media;
namespace Common.Client.Wpf.Converters
{
public class ColorToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is null ? null : new SolidColorBrush((Color)value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value as SolidColorBrush)?.Color;
}
}
}