How can I limit the number of characters in this TextBlock?
E.g.: It can only show up to 5 characters.
<TextBlock >
<Run Text="Tender Amount:"/>
<Run Text="{Binding TenderAmount,
Converter={StaticResource StringFormatConverter},
ConverterParameter='{}{0:C}'}" />
</TextBlock>
Here's the Code in my Converter
public sealed class StringFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
return null;
if (parameter == null)
return value;
return string.Format((string)parameter, value);
}
public object ConvertBack(object value, Type targetType, object parameter,
string language)
{
throw new NotImplementedException();
}
}
Use this:
public sealed class StringFormatConverter : IValueConverter
{
public int MaxLength { get; set; }
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
return null;
if (parameter == null)
return value;
var result = string.Format((string)parameter, value);
if (MaxLength > 0)
result = result.Substring(0, MaxLength);
return result;
}
public object ConvertBack(object value, Type targetType, object parameter,
string language)
{
throw new NotImplementedException();
}
}
Where you declare your converter resource, do this:
<stuff:StringFormatConverter x:Key="StringFormatConverter" MaxLength="5" />
Related
My issue is that when I use a converter on one of the multibinding's binding. It doesn't send the right thing to the converter. As per the DOC(https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/data-binding/multibinding?view=net-maui-7.0) in the Consume a IMultiValueConverter it should work, so I don't know what I'm doing wrong...
My multibinding class is the following:
public class BooleanAndConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || !targetType.IsAssignableFrom(typeof(bool)))
{
return false;
}
foreach (var value in values)
{
if (!(value is bool b))
{
return false;
}
else if (!b)
{
return false;
}
}
return true;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
I have a Boolean inverter class which is the following:
public class InverseBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("The target must be a boolean");
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Then in my XAML I'm using it like so:
<Button Text="Passer à la ronde suivante" Command="{Binding NextRoundCommand}">
<Button.IsVisible>
<MultiBinding Converter="{StaticResource BooleanAndConverter}">
<Binding Path="isGameStarted"/>
<Binding Path="isPlayersPlaying" Converter="{StaticResource InvertedBoolConverter}"/>
</MultiBinding>
</Button.IsVisible>
</Button>
When the inverter converter gets called in the multibinding, instead of receiving a TagetType of bool, it receive a "System.object", so it throws the InvalidOperationException.
Why when using normal bindings it receive a targettype of bool and in multibinding it doesn't?
Thanks
I couldn't see other code, but I tested the sample code DataBindingDemos, it works on my side.
But I found that you didn't implement function ConvertBack for both of your (BooleanAndConverter and InverseBooleanConverter ).
And if I replace the code of ConvertBack to yous as follows, the app will throw exception.
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
You can refer to the following code:
public class InverterConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool? b = value as bool?;
if (b == null)
{
return false;
}
return !b.Value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Convert(value, targetType, parameter, culture);
}
}
public class AllTrueMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || !targetType.IsAssignableFrom(typeof(bool)))
{
return false;
// Alternatively, return BindableProperty.UnsetValue to use the binding FallbackValue
}
foreach (var value in values)
{
if (!(value is bool b))
{
return false;
// Alternatively, return BindableProperty.UnsetValue to use the binding FallbackValue
}
else if (!b)
{
return false;
}
}
return true;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
if (!(value is bool b) || targetTypes.Any(t => !t.IsAssignableFrom(typeof(bool))))
{
// Return null to indicate conversion back is not possible
return null;
}
if (b)
{
return targetTypes.Select(t => (object)true).ToArray();
}
else
{
// Can't convert back from false because of ambiguity
return null;
}
}
}
For more information, you can check: AllTrueMultiConverter.cs and InverterConverter.cs
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"});
How can I limit the number of characters in this TextBlock?
E.g.: It can only show up to 5 characters.
<TextBlock >
<Run Text="Tender Amount:"/>
<Run Text="{Binding TenderAmount,
Converter={StaticResource StringFormatConverter},
ConverterParameter='{}{0:C}'}" />
</TextBlock>
btw heres the Code in my Converter
public sealed class StringFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
return null;
if (parameter == null)
return value;
return string.Format((string)parameter, value);
}
public object ConvertBack(object value, Type targetType, object parameter,
string language)
{
throw new NotImplementedException();
}
}
Thanks,
Nico
You can write your own Binding Converter to limit the number of displayed characters.
This one for example would simply limit the number of displayed characters to the first 5 characters in the string (or string representation of whatever you are passing into it).
public class StringLengthLimitConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
string val = value.ToString();
int length = parameter as int? ?? 5;
if (val.Length > length)
return val.Substring(0, length);
return val;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
Usage:
<Run Text="{Binding Path=TenderAmount,
Converter={StaticResource StringLengthLimitConverter},
ConverterParameter=5}" />
Depending on what you actually want to do, you might want to add an ellipsis (…) at the end.
You have to write your own converter like:
public class StringMaxLengthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
int MaxLength = Convert.ToInt32(parameter);
string TheString = (string)Value;
if (MaxLength > TheString.Length)
{
return TheString.SubString(0, MaxLength);
}
else
{
return TheString;
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
Declare it in your resources like:
<local:StringMaxLengthConverter x:Key="StrMaxLengthCvtr"/>
Use it in your TextBlock and pass the max string length like:
<TextBlock >
<Run Text="Tender Amount:"/>
<Run Text="{Binding TenderAmount, Converter={StaticResource StrMaxLengthCvtr}, ConverterParameter=5}" />
</TextBlock>
I'm trying to binding the TextColor from a label on ViewCell:
Label myLabel = new Label { Text = "SomeText" };
myLabel.SetBinding(Label.TextColorProperty,
new Binding("TheTextColor", BindingMode.TwoWay, new LabelTextColorConverter()));
Here's the converter:
public class LabelTextColorConverter : IValueConverter
{
public bool OldValue { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
OldValue = (bool) value;
Debug.WriteLine("asdadasdsadsada");
if ((bool)value)
return Color.Red;
else
return Color.Silver;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Debug.WriteLine("qwqweqewqeeqe");
return OldValue;
}
}
The debug output doesn't appear, and the color doesn't change either. I don't see anything wrong.
Why do you need Two Way binding for this? I don't think it's necessary.
myLabel.SetBinding(Label.TextColorProperty, new Binding("TheTextColor", BindingMode.OneWay, new LabelTextColorConverter()));
Then:
public class LabelTextColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool val = (bool)value;
if (val)
return Color.Red;
else
return Color.Silver;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
... and it should work fine. Also make sure you're setting BindingContext for your page/controls correctly.
I am using the following query:
string query = #"SELECT r.id, user_name, user_phone, date_create, REPLACE( date_payment, '0000-00-00 00:00:00', 'Не оплачено' ) as
date_payment, payment_method, amount, rs.name_ru
FROM request AS r, request_status AS rs
WHERE r.status = rs.id";
And i am binding datatemplate in the following way:
DataTemplate>
<TextBlock VerticalAlignment="Center" Text="{Binding date_payment}" Width="135" />
</DataTemplate>
Its throwing correct output except the "date_payment" Its output value comes "System.Byte[]".
please help!!!
Thank from MBen
class ByteArrayToString : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
var listOfBytes = value as Byte[];
string output = "";
output = System.Text.Encoding.UTF8.GetString(listOfBytes);
return output;
}
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
date_payment is an Array, and you didn't provide any way for WPF for displaying it, so it calls ToString . You can provide a data converter for it.
Add a resource to your Window or page :
<Window.Resources>
<local:ByteArrayToString x:Key="ByteArrayConverter" />
</Window.Resources>
Use it in your TextBlock as such :
<TextBlock VerticalAlignment="Center" Text="{Binding date_payement, Converter={StaticResource ByteArrayConverter}}" Width="135" />
Now you need to add a new class that does the conversion :
class ByteArrayToString : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
var listOfBytes = value as Byte[];
string output ="";
output = listOfBytes.Aggregate(output, (current, elemt) => current + elemt.ToString());
return output;
}
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
This thing happened to me to. It seems this is due to a bug in the connector. Try to cast the date column to CHAR. It worked for me.