hello I've tried to multibind of rectangle.
but i've got System.InvalidCastException: ‘Unable to cast object of type ‘MS.Internal.NamedObject’ to type ‘System.Boolean’.’ error
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
SolidColorBrush color = new SolidColorBrush(Colors.Transparent);
if (values == null)
{
return color;
}
bool A= (bool)(values[0]);
bool B= (bool)values[1];
if (A&& B)
{
color = new SolidColorBrush(Colors.Red);
}
else if (A== true && B== false)
{
color = new SolidColorBrush(Colors.Gray);
}
else if (A== true && B== true)
{
color = new SolidColorBrush(Colors.Green);
}
else
{
color = new SolidColorBrush(Colors.LightBlue);
}
return color;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I Want to change colors of rectangle depending on conditions.
xaml
<Rectangle.Fill>
<MultiBinding Converter="{StaticResource BackGroundColorConverters}">
<Binding Path="CheckAStatus"/>
<Binding Path="CheckDStatus"/>
</MultiBinding>
</Rectangle.Fill>
</Rectangle>
private bool _checkAStatus;
public bool CheckAStatus
{
get => _checkAStatus;
set => SetProperty(ref _checkAStatus, value);
}
private bool _checkDStatus;
public bool CheckDStatus
{
get => _checkDStatus;
set => SetProperty(ref _checkDStatus, value);
}
public ViewModel()
{
this.CheckAstatus = false;
this.CheckDstats = true;
}
You should validate the arguments in your Convert method and do nothing when an MS.Internal.NamedObject is passed as an argument:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || values.Length < 2 || !(values[0] is bool A) || !(values[1] is bool B)
return Binding.DoNothing;
SolidColorBrush color = new SolidColorBrush(Colors.Transparent);
if (values == null)
{
return color;
}
if (A && B)
{
color = new SolidColorBrush(Colors.Red);
}
else if (A == true && B == false)
{
color = new SolidColorBrush(Colors.Gray);
}
else if (A == true && B == true)
{
color = new SolidColorBrush(Colors.Green);
}
else
{
color = new SolidColorBrush(Colors.LightBlue);
}
return color;
}
Related
I have a custom control which works fine until I added the following section for a PointF variable but it shows up as grayed out in the properties list and I can't change the values at design time. See image below.
[DefaultValue(0)]
[Description("Gets or sets the jetting sword position")]
public virtual PointF jettingPosition
{
get
{
return jettingCentrePos;
}
set
{
jettingCentrePos = value;
Refresh();
}
}
How do I make this property enabled? I need something like the inherited Location property but the values for X and Y need to be float type.
If you used the type Point instead of PointF I would say just specify a TypeConverter of type PointConverter and be done with it.
[DefaultValue(0)]
[Description("Gets or sets the jetting sword position")]
[TypeConverter(typeof(PointConverter))]
public virtual PointF jettingPosition
However, you will see the error Object does not match target type. if you did that. Unfortunately you will need to create your own PointF type converter. Luckily we can just copy the PointConverter and change the type to PointF which is what we have here:
using System;
using System.Collections;
using System.Drawing;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Globalization;
using System.Reflection;
using System.Windows.Forms;
public class PointFConverter : TypeConverter {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
return ((sourceType == typeof(string)) || base.CanConvertFrom(context, sourceType));
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
return ((destinationType == typeof(InstanceDescriptor)) || base.CanConvertTo(context, destinationType));
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
string str = value as string;
if (value == null) return base.ConvertFrom(context, culture, value);
str = str.Trim();
if (str.Length == 0) return null;
if (culture == null) culture = CultureInfo.CurrentCulture;
char ch = culture.TextInfo.ListSeparator[0];
string[] strArray = str.Split(new char[] { ch });
int[] numArray = new int[strArray.Length];
TypeConverter converter = TypeDescriptor.GetConverter(typeof(float));
for (int i = 0; i < numArray.Length; i++) {
numArray[i] = (int)converter.ConvertFromString(context, culture, strArray[i]);
}
if (numArray.Length != 2) throw new ArgumentException("Invalid format");
return new PointF(numArray[0], numArray[1]);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
if (destinationType == null) throw new ArgumentNullException("destinationType");
if (value is Point) {
if (destinationType == typeof(string)) {
PointF point = (PointF)value;
if (culture == null) culture = CultureInfo.CurrentCulture;
string separator = culture.TextInfo.ListSeparator + " ";
TypeConverter converter = TypeDescriptor.GetConverter(typeof(float));
string[] strArray = new string[2];
int num = 0;
strArray[num++] = converter.ConvertToString(context, culture, point.X);
strArray[num++] = converter.ConvertToString(context, culture, point.Y);
return string.Join(separator, strArray);
}
if (destinationType == typeof(InstanceDescriptor)) {
PointF point2 = (PointF)value;
ConstructorInfo constructor = typeof(PointF).GetConstructor(new Type[] { typeof(float), typeof(float) });
if (constructor != null) return new InstanceDescriptor(constructor, new object[] { point2.X, point2.Y });
}
}
return base.ConvertTo(context, culture, value, destinationType);
}
public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) {
if (propertyValues == null) throw new ArgumentNullException("propertyValues");
object xvalue = propertyValues["X"];
object yvalue = propertyValues["Y"];
if (((xvalue == null) || (yvalue == null)) || (!(xvalue is float) || !(yvalue is float))) {
throw new ArgumentException("Invalid property value entry");
}
return new PointF((float)xvalue, (float)yvalue);
}
public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) {
return true;
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) {
return TypeDescriptor.GetProperties(typeof(PointF), attributes).Sort(new string[] { "X", "Y" });
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context) {
return true;
}
}
Then you can use this:
[DefaultValue(0)]
[Description("Gets or sets the jetting sword position")]
[TypeConverter(typeof(PointFConverter))]
public virtual PointF jettingPosition
Here is more information about custom Type Converters. This specific case covers string to point:
https://learn.microsoft.com/en-us/previous-versions/ayybcxe5(v=vs.140)?redirectedfrom=MSDN
I would like to override message provided by the custom conversion class which inherits IValueConverter interface.
public class BoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//string convertedValue = "";
bool initialValue = (bool)value;
if (initialValue == false)
{
return "0";
}
else if (initialValue == true)
{
return "1";
}
else return null;//{ return convertedValue; }
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string initialValue = ((string)value).Trim();
try {
if (initialValue == "1") { return true; }
else if (initialValue == "0") { return false; }
else { return null; }
}
catch (Exception)
{
return null;
}
}
}
For example, if I enter 3 into text box binded with property I am receiving message: Value '3' could not be converted. I would like to provide longer message.
I want to make a label that will extract the name or some other data of the bound item.
[Display(Description = "Gimme your goddamm first name will ya")]
public string FirstName { get; set; }
Code:
public class TitleLabel : ContentView
{
public Label Label { get; } = new Label();
public TitleLabel()
{
//TODO ensure Content is not accessed manually
Content = Label;
}
protected override void OnBindingContextChanged() =>
Label.Text = GetPropertyTitle();
string GetPropertyTitle()
{
var bcp = BindingContextProperty;
//pseudo:
var binding = GetBinding(bcp);
var obj = binding.Object;
var propertyName = binding.Path;
var propertyInfo = obj.GetType().GetTypeInfo().DeclaredMembers
.SingleOrDefault(m => m.Name == propertyName);
if (propertyInfo == null)
throw new InvalidOperationException();
return propertyInfo.GetCustomAttribute<DisplayAttribute>().Description;
}
}
XAML:
<my:TitleLabel Text="{Binding FirstName}" />
Rendered result:
<my:TitleLabel Text="Gimme your goddamm first name will ya" />
The best option is to define a value converter.
namespace SampleFormsApp {
public class DisplayNameConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (value == null || targetType != typeof(string))
return null;
var propertyName = parameter as string;
if (propertyName == null)
return null;
var propertyInfo = value.GetType().GetTypeInfo().DeclaredMembers
.SingleOrDefault(m => m.Name == propertyName);
if (propertyInfo == null)
return null;
return propertyInfo.GetCustomAttribute<DisplayAttribute>().Name;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then declare it in your global ResourceDictionary in App.xaml:
<ResourceDictionary>
<local:DisplayNameConverter x:Key="DisplayNameConverter"/>
</ResourceDictionary>
Making sure to declare the namespace:
xmlns:local="clr-namespace:SampleFormsApp"
Then when you want to use it, you bind to the object containing the property, and pass the property name as a parameter:
<Label Text="{Binding .,
Converter={StaticResource DisplayNameConverter}, ConverterParameter=FirstName}"/>
If you throw and exception in the Convert method (as your example above), it will crash the app. During page rendering, it will likely call the converter with a null value, so it has to be resilient to that at least.
Gotcha (Gist):
public class DisplayExtension : IMarkupExtension<string>
{
public object Target { get; set; }
BindableProperty _Property;
public string ProvideValue(IServiceProvider serviceProvider)
{
if (Target == null
|| !(Target is Enum
|| Target is Type
|| (Target is Binding binding && !string.IsNullOrWhiteSpace(binding.Path))))
throw new InvalidOperationException($"'{nameof(Target)}' must be properly set.");
var p =(IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
if (!(p.TargetObject is BindableObject bo
&& p.TargetProperty is BindableProperty bp
&& bp.ReturnType.GetTypeInfo().IsAssignableFrom(typeof(string).GetTypeInfo())))
throw new InvalidOperationException(
$"'{nameof(DisplayExtension)}' cannot only be applied"
+ "to bindable string properties.");
_Property = bp;
bo.BindingContextChanged += DisplayExtension_BindingContextChanged;
return null;
}
void DisplayExtension_BindingContextChanged(object sender, EventArgs e)
{
var bo = (BindableObject)sender;
bo.BindingContextChanged -= DisplayExtension_BindingContextChanged;
string display = null;
if (Target is Binding binding)
display = ExtractMember(bo, (Binding)Target);
else if (Target is Type type)
display = ExtractDescription(type.GetTypeInfo());
else if (Target is Enum en)
{
var enumType = en.GetType();
if (!Enum.IsDefined(enumType, en))
throw new InvalidOperationException(
$"The value '{en}' is not defined in '{enumType}'.");
display = ExtractDescription(
enumType.GetTypeInfo().GetDeclaredField(en.ToString()));
}
bo.SetValue(_Property, display);
}
string ExtractMember(BindableObject target, Binding binding)
{
var container = target.BindingContext;
var properties = binding.Path.Split('.');
var i = 0;
do
{
var property = properties[i++];
var type = container.GetType();
var info = type.GetRuntimeProperty(property);
if (properties.Length > i)
container = info.GetValue(container);
else
{
return ExtractDescription(info);
}
} while (true);
}
string ExtractDescription(MemberInfo info)
{
var display = info.GetCustomAttribute<DisplayAttribute>(true);
if (display != null)
return display.Name ?? display.Description;
var description = info.GetCustomAttribute<DescriptionAttribute>(true);
if (description != null)
return description.Description;
return info.Name;
}
object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider) =>
ProvideValue(serviceProvider);
}
Usage:
<Label Text="{my:Display Target={Binding FirstName}}"/>
<Label Text="{my:Display Target={Binding User.Person.Address.City.Country}}"/>
<Label Text="{my:Display Target={Type models:Person}}"/>
<Label Text="{my:Display Target={Static models:Gender.Male}}"/>
I want to implement TypeConverter for a custom type, Thickness. I've looked over the microsoft released typeconverters like SizeConverter.
When I type a string or change one of the properties on my Thickness property, the designer works with it, it just doesn't save the change to 'Designer.cs'. It has to be the conversion from the type 'Thickness' to 'InstanceDescriptor' but I can't find anything wrong with my code...
Here is Thickness:
[TypeConverter(typeof(ThicknessConverter))]
public struct Thickness
{
Double top;
Double bottom;
Double right;
Double left;
public Thickness(Double uniformLength)
{
top = uniformLength;
bottom = uniformLength;
right = uniformLength;
left = uniformLength;
}
public Thickness(Double left, Double top, Double right, Double bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public Double Top
{
get { return top; }
set { top = value; }
}
public Double Bottom
{
get { return bottom; }
set { bottom = value; }
}
public Double Right
{
get { return right; }
set { right = value; }
}
public Double Left
{
get { return left; }
set { left = value; }
}
public Double UniformLength
{
get
{
if (!IsUniform)
throw new InvalidOperationException();
else return top;
}
set
{
top = value;
bottom = value;
right = value;
bottom = value;
}
}
public Boolean IsUniform
{
get { return top == bottom && bottom == right && bottom == left; }
}
}
And here is the type converter:
public class ThicknessConverter : TypeConverter
{
public ThicknessConverter()
{
}
public override Boolean CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(String) || destinationType == typeof(InstanceDescriptor);
}
public override Object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, Object value, Type destinationType)
{
Thickness thickness = (Thickness)value;
if (destinationType == typeof(String))
{
if (thickness.IsUniform)
return thickness.Right.ToString();
else return thickness.Left + "," + thickness.Top + "," + thickness.Right + "," + thickness.Bottom;
}
else if (destinationType == typeof(InstanceDescriptor))
{
if (thickness.IsUniform)
return new InstanceDescriptor(typeof(Thickness).GetConstructor(new Type[] { typeof(Double) }), new Object[] { thickness.UniformLength });
else
{
ConstructorInfo constructor = typeof(Thickness).GetConstructor(new Type[] { typeof(Double), typeof(Double), typeof(Double), typeof(Double) });
return new InstanceDescriptor(constructor, new Object[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom });
}
}
else return null;
}
public override Boolean CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(String);
}
public override Object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, Object value)
{
if (value is String)
{
String stringValue = (String)value;
if (stringValue.Contains(","))
{
String[] stringValues = stringValue.Split(',');
Double[] values = new Double[stringValues.Length];
if (values.Length == 4)
{
try
{
for (Int32 i = 0; i < 4; i++)
values[i] = Double.Parse(stringValues[i]);
}
catch (Exception)
{
return new Thickness();
}
return new Thickness(values[0], values[1], values[2], values[3]);
}
else return new Thickness();
}
else
{
try
{
return new Thickness(Double.Parse(stringValue));
}
catch (Exception)
{
return new Thickness();
}
}
}
else return base.ConvertFrom(context, culture, value);
}
public override Boolean GetCreateInstanceSupported(ITypeDescriptorContext context)
{
return true;
}
public override Object CreateInstance(ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
{
return new Thickness((Double)propertyValues["Left"], (Double)propertyValues["Top"], (Double)propertyValues["Right"], (Double)propertyValues["Bottom"]);
}
public override Boolean GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, Object value, Attribute[] attributes)
{
PropertyDescriptorCollection collection = TypeDescriptor.GetProperties(typeof(Thickness));
collection = collection.Sort(new String[] { "Left", "Top", "Right", "Bottom" });
collection.RemoveAt(4);
collection.RemoveAt(4);
return collection;
}
}
The only thing I can think of is does the converter have to be in a seperate assembly than the designer code using it?
So, I hope that my investigation was ended succesfully.
The result:
This is something strange. It seems there is some bug in VS2008.
To get your code works as expected:
1. Rename ThicknessConverter to something else (for example: Thickness1Converter).
2. Build the solution by pressing Shift+F6.
Now you should be able to edit the Thickness property in the designer UI. The appropriate code will appear in the *.Designer.cs file.
After that, in some circumstances, it is possible to rename it back to the ThicknessConverter without getting an error. But I can't discover the rule, sorry.
If something isn't clear enough, do not hesitate to ask me.
Good luck.
This works fine for me. Here are the steps I followed:
Start VS2010.
Create a new WinForms project (.NET Framework 4 Client Profile).
Copy-paste your Thickness and ThicknessConverter classes.
Create a new user control UserControl1.
Add a property to the user control: public Thickness Thickness { get; set; }
Build project.
Open Form1 designer.
Place a UserControl1 on Form1.
Select UserControl1.
Edit Thickness property.
Save.
Verify thickness is correct in Form1.Designer.cs.
If this isn't working for you, you're either doing something else in your project, or there is a difference in our setups. Can you provide details (.NET version, for example)?
If there is a PNG image connects two coordinates (like a rope),
coordinateA.x;
coordinateA.y;
coordinateB.x;
coordinateB.y;
The two coordinates will keep changing all the time, so the PNG image connects between two coordinates needs to resize and rotate.
Any sample code to make this easier?
This is my approach:
Coordinates.cs:
public class Coordinates : DependencyObject
{
public Coordinates(double x, double y)
{
X = x;
Y = y;
}
public Coordinates()
{
X = 0;
Y = 0;
}
//X Dependency Property
public double X
{
get { return (double)GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
public static readonly DependencyProperty XProperty =
DependencyProperty.Register("X", typeof(double), typeof(Coordinates), new UIPropertyMetadata(0d));
//Y Dependency Property
public double Y
{
get { return (double)GetValue(YProperty); }
set { SetValue(YProperty, value); }
}
public static readonly DependencyProperty YProperty =
DependencyProperty.Register("Y", typeof(double), typeof(Coordinates), new UIPropertyMetadata(0d));
}
Geometry.cs:
public class ConnectorLocationConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var start = (Coordinates)values[0];
var end = (Coordinates)values[1];
return new Thickness(start.X, start.Y, 0, 0);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
public class ConnectorAngleConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var start = (Coordinates)values[0];
var end = (Coordinates)values[1];
//dy/dx = tan(t) => t = arcTan(dy/dx)
double t = Math.Atan2(
Math.Abs(start.Y - end.Y),
Math.Abs(start.X - end.X)) * 180 / Math.PI;
if (end.X <= start.X && end.Y >= start.Y) return 180 - t;
if (end.X >= start.X && end.Y <= start.Y) return -t;
if (end.X <= start.X && end.Y <= start.Y) return 180 + t;
return t;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
public class ConnectorWidthConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var start = (Coordinates)values[0];
var end = (Coordinates)values[1];
//get side for states
return Math.Abs(start.X - end.X);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
public class ConnectorHeightConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var start = (Coordinates)values[0];
var end = (Coordinates)values[1];
//get side for states
return Math.Abs(start.Y - end.Y);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
MainWindow.xaml.cs:
//nothing fancy here
public MainWindow()
{
Start = new Coordinates();
End = new Coordinates();
DataContext = this;
InitializeComponent();
}
//Start Dependency Property
public Coordinates Start
{
get { return (Coordinates)GetValue(StartProperty); }
set { SetValue(StartProperty, value); }
}
public static readonly DependencyProperty StartProperty =
DependencyProperty.Register("Start", typeof(Coordinates), typeof(MainWindow), new UIPropertyMetadata(null));
//End Dependency Property
public Coordinates End
{
get { return (Coordinates)GetValue(EndProperty); }
set { SetValue(EndProperty, value); }
}
public static readonly DependencyProperty EndProperty =
DependencyProperty.Register("End", typeof(Coordinates), typeof(MainWindow), new UIPropertyMetadata(null));
//--------------------------------------------------
//Click causes the mouse position stick to Start/End
//--------------------------------------------------
bool flag = false;
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
var p = e.GetPosition(this);
if(flag) Start = new Coordinates(p.X, p.Y);
else End = new Coordinates(p.X, p.Y);
}
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
flag = !flag;
}
And finally MainWindow.xaml:
<Window x:Class="WpfTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfTest"
Title="MainWindow" Height="650" Width="825">
<Window.Resources>
<local:ConnectorLocationConverter x:Key="connectorLocationConverter"/>
<local:ConnectorAngleConverter x:Key="connectorAngleConverter"/>
<local:ConnectorWidthConverter x:Key="connectorWidthConverter"/>
<local:ConnectorHeightConverter x:Key="connectorHeightConverter"/>
</Window.Resources>
<Canvas Background="#2000"
MouseMove="Canvas_MouseMove" MouseDown="Canvas_MouseDown">
<Canvas Width="0" Height="0">
<Image Source="/WpfTest;component/im.png" Stretch="Fill">
<Image.Margin>
<MultiBinding Converter="{StaticResource connectorLocationConverter}">
<Binding Path="Start"/>
<Binding Path="End"/>
</MultiBinding>
</Image.Margin>
<Image.Width>
<MultiBinding Converter="{StaticResource connectorWidthConverter}">
<Binding Path="Start"/>
<Binding Path="End"/>
</MultiBinding>
</Image.Width>
<Image.Height>
<MultiBinding Converter="{StaticResource connectorHeightConverter}">
<Binding Path="Start"/>
<Binding Path="End"/>
</MultiBinding>
</Image.Height>
<Image.RenderTransform>
<RotateTransform CenterX="0" CenterY="0">
<RotateTransform.Angle>
<MultiBinding Converter="{StaticResource connectorAngleConverter}">
<Binding Path="Start"/>
<Binding Path="End"/>
</MultiBinding>
</RotateTransform.Angle>
</RotateTransform>
</Image.RenderTransform>
</Image>
</Canvas>
</Canvas>
</Window>