I have an ItemsControl
like this
<ItemsControl Name="itemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton Style="{StaticResource ToggleButtonInputControl}">
<Grid>
<local:CustomTextBox
StringInTextBox="{Binding
Path=UIntProperty,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
Converter={local:UIntConverter Param=123}}"/>
</Grid>
</ToggleButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The source of the ItemsControl
is set in code and it is an ObservableCollection
of type MyClass
which has a property named UIntProperty
. The converter UIntConverter
extends MarkupExtension
so I can use it like above and has a uint
property called Param
.
Objective
The objective is to edit UIntProperty
using the CustomTextBox
. If a no numeric value is typed, the value UIntProperty
should not change and if the StringInTextBox
is empty,
the value should be set to 0
and display that value. More conditions are required for a float
type, but the case of a uint
should give the idea of the final objective.
I have tried
I can use the dependency property StringInTextBox
with the CoerceValueCallback
and PropertyChangedCallback
, but according to my test, the bindings trigger first, so the UIntConverter performs first and that is where the UIntProperty
is set. I could use an intermediary string
property to bind to StringInTextBox
and use the set
get
methods to validate, but I would have to implement that intermediary string
to every property in MyClass
(many properties). I could also make the UIntConverter
return null to know when to keep the original value, but I would have to make the UIntProperty
nullable across the code.
I figured I could use the UIntConverter
and pass the original UintProperty
so the converter can return that value when needed, but I don't know how to pass the UIntProperty
to the Param
, I thought UIntProperty
would be in the same "scope" as for the binding but Param=UIntProperty
in the code above doesn't work.
Question
So, the question is how can I pass UIntProperty
to Param
or as the converter parameter? I don't know if I can pass a value to Param
when the UIntConverter is a StaticResource
, if possible how to do that?
Thanks in advance.
UPDATE:
I solve the problem returning Binding.DoNothing
from ConvertBack
. Looks like the ConverterParameter
is not meant to pass values that depend on each object, but to indicate more precisely what kind of conversion is going to be performed, like a type of filter.
Also, in cases of complex logic, you can consider creating an additional property for the CustomTextBox or Attached Property. But in this case, I see it as redundant. If you need some additional logic, it is better to include it in the StringInTextBox property.
If you provide more clarification on the required functionality and application, then I would try to make an example implementation for you.
Addition to the answer.
An example of a Number to String converter that does not change the source property in case of an error converting from a string to the source type.
In case of an error, the special value Binding.DoNothing
is returned, which tells the binding system not to assign the value returned by the converter.
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace CommonCore.Converters
{
[ValueConversion(typeof(uint), typeof(string))]
[ValueConversion(typeof(int), typeof(string))]
[ValueConversion(typeof(byte), typeof(string))]
[ValueConversion(typeof(sbyte), typeof(string))]
[ValueConversion(typeof(short), typeof(string))]
[ValueConversion(typeof(ushort), typeof(string))]
[ValueConversion(typeof(long), typeof(string))]
[ValueConversion(typeof(ulong), typeof(string))]
[ValueConversion(typeof(double), typeof(string))]
[ValueConversion(typeof(float), typeof(string))]
[ValueConversion(typeof(decimal), typeof(string))]
public class NumberToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return System.Convert.ToString(value, culture);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string text = (string)value;
if (string.IsNullOrWhiteSpace(text))
return System.Convert.ChangeType(0, targetType, culture);
try
{
return System.Convert.ChangeType(value, targetType, culture);
}
catch
{
return Binding.DoNothing;
}
}
private NumberToStringConverter() { }
public static NumberToStringConverter Instance { get; } = new();
}
[MarkupExtensionReturnType(typeof(NumberToStringConverter))]
public class NumberToStringExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return NumberToStringConverter.Instance;
}
}
}