WPF Textblock Convert Issue - wpf

am usina text block in usercontrol, but am sending value to textblock from other form, when i pass some value it viewed in textblock, but i need to convert the number to text. so i used converter in textblock. but its not working
<TextBlock Height="21" Name="txtStatus" Width="65" Background="Bisque" TextAlignment="Center" Text="{Binding Path=hM1,Converter={StaticResource TextConvert},Mode=OneWay}"/>
converter class
class TextConvert : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
if (value.ToString() == "1")
{
return value = "Good";
}
if (value.ToString() == "0")
{
return value = "NIL";
}
}
return value = "";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (string)value;
}
}
is it right? whats wrong in it??

ok I think I know what the problem is - let see if I can define it for you :)
in your xaml file where you want to use TextConvert, define Resource for it (unless you are doing it already, then I haven't a clue why its not working)
<Grid.Resources>
<Shared:TextConvert x:Key="TextConvertKey" />
</Grid.Resources>
shared being the xmlns ofcourse.
Then in the textbox use it like:
Text="{Binding Path=hM1,Converter={StaticResource TextConvertKey},Mode=OneWay}"/>
EDIT:
If you set a breakpoint in the converter class, does the debugger go in there?????
EDIT 2:
am using like this voodoo
local:HealthTextConvert x:Key="TextConvert"
This is absolutely wrong. How can you Call it HealthTextConvert when the converter name is TextConvert???
it should be
local:TextConvert x:Key="whateverKeyNameYouWant"
and
in the textbox is should be
Text="{Binding Path=hM1,Converter={StaticResource whateverKeyNameYouWant},Mode=OneWay}"

I can see immediately a problem with your converter definition.
class TextConvert : IValueConverter
{
...
Should be declared public to be able to use it as a resource.
public class TextConvert : IValueConverter
{
...
Also, its not a good thing to be doing this...
return value = "Good";
...
return value = "NIL";
It should just be (even though it will not matter if you leave it, just bad programming =P):
return "Good";
...
return "Nill";

Try by removing Path in the below line
Text="{Binding **Path**=hM1,Converter={StaticResource TextConvert},Mode=OneWay}".
Sometimes it works without Path :).
Also look into the output window(Alt+Cntl+O)...to see where the issue is.

Related

WPF stringformat show only digital

i want in wpf application show only decimal number in a textblock.
I have for example "35.56", i want show only ".56"
Thank you
You can represent 0.56 as only .56 using <TextBlock Text="{Binding Path=yourprop, StringFormat='#.##'}" /> but 35.56 cannot be represented using .56 using a StringFormat because 35.56 and .56 are two different values.
If you want to represent 35.56 as .56 for some strange reason, you'd better format the string using some custom logic in your view model:
public string YourPropFormatted
{
get
{
string s = yourprop.ToString("#.00", CultureInfo.InvariantCulture);
int index = s.IndexOf('.');
return (index > 0) ? s.Substring(index) : s;
}
}
XAML is a markup language and can't handle this.
Complementing mm8 answer with another way to get the same result is implementing a IValueConverter.
public class NumberToDecimalPartConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(!(value is double val))
return default(double).ToString(); // or ".00", you decide
string s = val.ToString("#.00", CultureInfo.InvariantCulture);
int index = s.IndexOf('.');
return index > 0 ? s.Substring(index) : s;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// you don't need convert back
throw new NotImplementedException();
}
}
Assuming the converter is placed at same namespace
<Window.Resources>
<local:NumberToDecimalPartConverter x:Key="NumberToDecimalPart"/>
</Window.Resources>
<TextBlock Text="{Binding Path=yourprop, Converter={StaticResource NumberToDecimalPart}}" />
When your property changes, the UI will be updated, right?
With converter, you have one more step that is convert the value.
So what I'm doing here it's when the property (your probably double value) change, the WPF will call the Convert function from NumberToDecimalPartConverter. When the property have Mode setted as TwoWay will call ConvertBack.

Possible to use constant/static value inside a binding in XAML?

We are using the following mechanism/syntax to bind commands in XAML:
Command="{Binding CommandAggregator[FooCmd], Mode=OneTime}"
Here, CommandAggregator is an object that you can use an indexer (with string parameter) on to get back the actual command.
The command registrations with the aggregator are bugging me a bit, because we are still using magic strings for the command names like this:
this.CommandAggregator.SetCommand("FooCmd", new RelayCommand(execute, canExecute));
While I don't necessarily like this whole process, I cannot change much. The one thing I would like to do for now is quit using magic strings by making them constants or static readonly string objects inside a static CommandName class.
But is it possible to define the binding inside the XAML and reference the constant (say CommandName.Foo)? I thought about using {x:Static ...}, but I don't know how to get the returned value into the indexer.
you can implement an IValueConverter which will return a command from CommandAggregator based on converter Parameter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var aggregator = value as CommandAggregator;
var cmd = parameter as string;
if (aggregator != null && cmd != null)
return aggregator[cmd];
return null;
}
and pass the parameter from xaml:
Command="{Binding Path=CommandAggregator,
Converter={StaticResource MyConverter},
ConverterParameter={x:Static Constants.FooCmd},
Mode=OneTime}"
Not sure if this is the best, but it works for me. I didn't like so many CmdWhatEvers for my Relay commands showing up in my debugger so moved them to a dictionary, which seems similar to your CommandAggregator, then indexed them with a static string (magic strings have caused so many binding failures for me.)
Anyway, my string constants live in a static class
internal static class Str
{
public static readonly string CmdReset = "CmdReset";
}
In the View Model
internal class CtrlVm : ViewModelBase
{
public Dictionary<string, IRelayCommand> Commands { get; }
public CtrlVm()
{
Commands = new Dictionary<string, IRelayCommand>()
{
// My relay command class takes, Execute, CanExecute
// and a Header parameter, that I use in binding
Str.CmdReset, new RelayCommand(Reset, CanReset, "Reset");
},
}
private bool CanReset(object parameter)
{
return bHasChanges; // or whatever
}
private void Reset(object parameter)
{
// do the reset work
}
}
Then in the Xaml
<Button Command= "{Binding Commands[CmdReset]}"
Content= "{Binding Commands[CmdReset].Header}"/>

Binding float data type with UpdateSourceTrigger set toPropertyChanged in WPF

I have a problem with Float data type while using UpdateSourceTrigger in WPF.I have a property with float data type and it is binded it to a TextBox and set its UpdateSourceTrigger of the binding to PropertyChanged,but WPF dosen't let me type '.' in the TextBox unless i change UpdateSourceTrigger to LostFocus.I think it's because of we can not type '.' in the end of float value.I don't have any idea how can i fix it because i need to type '.' and set UpdateSourceTrigger to PropertyChanged.
I have designed my textbox in such a way that it can take only 7 characters
for example
1) 12.3456
2) 1234.56 e.t.c
The property is:`
public float? Expenditure
{
get;set;
}
And in the XAML:
<TextBox Text="{Binding Expenditure, UpdateSourceTrigger=PropertyChanged}"/>
StringFormat does not help as decimal can be put anywhere.
Any help would be great.
Just change StringFormat property of the binding to display two decimal places of the property:
<TextBox Text="{Binding Expenditure, UpdateSourceTrigger=PropertyChanged, StringFormat='{}{0:F2}'}"/>
Also you can write a custom FloatToStringConverter (here is an example). Your own float-to-string and string-to-float conversion methods will allow you to handle empty text field of TextBox and convert it to null.
I wrote a value converter, that solves your problem:
Usage:
<Window x:Class="BindingExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BindingExample"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:DoubleToStringConverter x:Key="DoubleToStringConverter" DigitsCount="5"/>
</Window.Resources>
<StackPanel>
<TextBox Text="{Binding FloatProperty, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource DoubleToStringConverter}}" Margin="5"/>
</StackPanel>
</Window>
Converter:
[ValueConversion(typeof(double), typeof(string))]
public class DoubleToStringConverter : IValueConverter
{
#region IValueConverter Members
public DoubleToStringConverter()
{
// Default value for DigitsCount
DigitsCount = 7;
}
// Convert from double to string
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return null;
double doubleValue = System.Convert.ToDouble(value);
// Calculate digits count
int digitsBeforePoint = System.Convert.ToInt32(Math.Ceiling(Math.Log10(doubleValue)));
int digitsAfterPoint = DigitsCount - digitsBeforePoint;
// TODO: You have to handle cases where digitsAfterPoint < 0
// Create formatString that is used to present doubleValue in desired format
string formatString = String.Format("{{0:F{0}}}", digitsAfterPoint);
return String.Format(formatString, doubleValue);
}
// Convert from string to double
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return null;
double? result = null;
try
{
result = System.Convert.ToDouble(value);
}
catch
{
}
return result.HasValue ? (object)result.Value : DependencyProperty.UnsetValue;
}
public int DigitsCount { get; set; }
#endregion
}

Order of execution in a WPF Textbox

I'm running into an issue with a formatting converter and data validation. I have the following textbox XAML declaration
<TextBox FontFamily="Segoe" FontSize="16" FontWeight="Medium"
TabIndex="{Binding TabBinding}" Foreground="Black"
Opacity="0.9" IsTabStop="True" Uid="{Binding PriceID}"
Text="{Binding NewPrice,Converter={StaticResource FormattingConverter},
ConverterParameter=' \{0:C\}', Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
Background="#FFE6DED3" BorderBrush="#FFE6DED3"
DataContext="{Binding StringFormat=\{0:c\}, NotifyOnValidationError=True}"
Padding="0" KeyDown="TextBox_KeyDown" AcceptsReturn="False">
</TextBox>
The issue I'm up against is a data validation issue. When a user enters an invalid price (Ex: a value of "abc" or "0.0.4"), the textbox attempts to perform the conversion in the "FormattingConverter" method. (ConvertBack method pasted below) This causes an exception and the program errors out. Is there a way to delay the FormattingConverter call or bypass it if the data in the textbox is not valid?
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var objTypeConverter = System.ComponentModel.TypeDescriptor.GetConverter(targetType);
object objReturnValue = null;
if (objTypeConverter.CanConvertFrom(value.GetType())) {
objReturnValue = objTypeConverter.ConvertFrom(value.ToString().Replace("$", ""));
}
return objReturnValue;
}
Thank you,
A converter's ConvertBack always runs to convert the data to a value suitable for the target object. It is the responsibility of the converter to handle exceptions (and in the case of exception, return the original value so the binding framework will also realize it's an invalid value).
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var objTypeConverter = System.ComponentModel.TypeDescriptor.GetConverter(targetType);
// Default return value is the original value - if conversion fails, return
// this value so the binding framework will see an invalid value (and not
// just null).
object objReturnValue = value;
if (objTypeConverter.CanConvertFrom(value.GetType())) {
try {
objReturnValue = objTypeConverter.ConvertFrom(value.ToString().Replace("$", ""));
}
catch( FormatException ) { }
// Catch all of your possible exceptions and ignore them by returning the original value
}
return objReturnValue;
}
You can also return DependencyProperty.UnsetValue, however in practice I prefer to see the actual error message returned in validation from an invalid value than to simply return an unset value.

WPF Binding and Dynamically Assigning StringFormat Property

I have a form that is generated based on several DataTemplate elements. One of the DataTemplate elements creates a TextBox out of a class that looks like this:
public class MyTextBoxClass
{
public object Value { get;set;}
//other properties left out for brevity's sake
public string FormatString { get;set;}
}
I need a way to "bind" the value in the FormatString property to the "StringFormat" property of the binding. So far I have:
<DataTemplate DataType="{x:Type vm:MyTextBoxClass}">
<TextBox Text="{Binding Path=Value, StringFormat={Binding Path=FormatString}" />
</DataTemplate>
However, since StringFormat isn't a dependency property, I cannot bind to it.
My next thought was to create a value converter and pass the FormatString property's value in on the ConverterParameter, but I ran into the same problem -- ConverterParameter isn't a DependencyProperty.
So, now I turn to you, SO. How do I dynamically set the StringFormat of a binding; more specifically, on a TextBox?
I would prefer to let XAML do the work for me so I can avoid playing with code-behind. I'm using the MVVM pattern and would like to keep the boundaries between view-model and view as un-blurred as possible.
Thanks!
This is a solution from Andrew Olson that uses attached properties and thus can be used in various situations.
Used like this:
<TextBlock
local:StringFormatHelper.Format="{Binding FormatString}"
local:StringFormatHelper.Value="{Binding Value}"
Text="{Binding (local:StringFormatHelper.FormattedValue)}"
/>
The required helper: (source Gist)
public static class StringFormatHelper
{
#region Value
public static DependencyProperty ValueProperty = DependencyProperty.RegisterAttached(
"Value", typeof(object), typeof(StringFormatHelper), new System.Windows.PropertyMetadata(null, OnValueChanged));
private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
RefreshFormattedValue(obj);
}
public static object GetValue(DependencyObject obj)
{
return obj.GetValue(ValueProperty);
}
public static void SetValue(DependencyObject obj, object newValue)
{
obj.SetValue(ValueProperty, newValue);
}
#endregion
#region Format
public static DependencyProperty FormatProperty = DependencyProperty.RegisterAttached(
"Format", typeof(string), typeof(StringFormatHelper), new System.Windows.PropertyMetadata(null, OnFormatChanged));
private static void OnFormatChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
RefreshFormattedValue(obj);
}
public static string GetFormat(DependencyObject obj)
{
return (string)obj.GetValue(FormatProperty);
}
public static void SetFormat(DependencyObject obj, string newFormat)
{
obj.SetValue(FormatProperty, newFormat);
}
#endregion
#region FormattedValue
public static DependencyProperty FormattedValueProperty = DependencyProperty.RegisterAttached(
"FormattedValue", typeof(string), typeof(StringFormatHelper), new System.Windows.PropertyMetadata(null));
public static string GetFormattedValue(DependencyObject obj)
{
return (string)obj.GetValue(FormattedValueProperty);
}
public static void SetFormattedValue(DependencyObject obj, string newFormattedValue)
{
obj.SetValue(FormattedValueProperty, newFormattedValue);
}
#endregion
private static void RefreshFormattedValue(DependencyObject obj)
{
var value = GetValue(obj);
var format = GetFormat(obj);
if (format != null)
{
if (!format.StartsWith("{0:"))
{
format = String.Format("{{0:{0}}}", format);
}
SetFormattedValue(obj, String.Format(format, value));
}
else
{
SetFormattedValue(obj, value == null ? String.Empty : value.ToString());
}
}
}
This code (inspired from DefaultValueConverter.cs # referencesource.microsoft.com) works for a two way binding to a TextBox or similar control, as long as the FormatString leaves the ToString() version of the source property in a state that can be converted back.
(i.e. format like "#,0.00" is OK because "1,234.56" can be parsed back, but FormatString="Some Prefix Text #,0.00" will convert to "Some Prefix Text 1,234.56" which can't be parsed back.)
XAML:
<TextBox>
<TextBox.Text>
<MultiBinding Converter="{StaticResource ToStringFormatConverter}"
ValidatesOnDataErrors="True" NotifyOnValidationError="True" TargetNullValue="">
<Binding Path="Property" TargetNullValue="" />
<Binding Path="PropertyStringFormat" Mode="OneWay" />
</MultiBinding>
</TextBox.Text>
</TextBox>
Note duplicate TargetNullValue if the source property can be null.
C#:
/// <summary>
/// Allow a binding where the StringFormat is also bound to a property (and can vary).
/// </summary>
public class ToStringFormatConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length == 1)
return System.Convert.ChangeType(values[0], targetType, culture);
if (values.Length >= 2 && values[0] is IFormattable)
return (values[0] as IFormattable).ToString((string)values[1], culture);
return null;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
var targetType = targetTypes[0];
var nullableUnderlyingType = Nullable.GetUnderlyingType(targetType);
if (nullableUnderlyingType != null) {
if (value == null)
return new[] { (object)null };
targetType = nullableUnderlyingType;
}
try {
object parsedValue = ToStringFormatConverter.TryParse(value, targetType, culture);
return parsedValue != DependencyProperty.UnsetValue
? new[] { parsedValue }
: new[] { System.Convert.ChangeType(value, targetType, culture) };
} catch {
return null;
}
}
// Some types have Parse methods that are more successful than their type converters at converting strings
private static object TryParse(object value, Type targetType, CultureInfo culture)
{
object result = DependencyProperty.UnsetValue;
string stringValue = value as string;
if (stringValue != null) {
try {
MethodInfo mi;
if (culture != null
&& (mi = targetType.GetMethod("Parse",
BindingFlags.Public | BindingFlags.Static, null,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider) }, null))
!= null) {
result = mi.Invoke(null, new object[] { stringValue, NumberStyles.Any, culture });
}
else if (culture != null
&& (mi = targetType.GetMethod("Parse",
BindingFlags.Public | BindingFlags.Static, null,
new[] { typeof(string), typeof(IFormatProvider) }, null))
!= null) {
result = mi.Invoke(null, new object[] { stringValue, culture });
}
else if ((mi = targetType.GetMethod("Parse",
BindingFlags.Public | BindingFlags.Static, null,
new[] { typeof(string) }, null))
!= null) {
result = mi.Invoke(null, new object[] { stringValue });
}
} catch (TargetInvocationException) {
}
}
return result;
}
}
One way may be to create a class that inherits TextBox and in that class create your own dependency property that delegates to StringFormat when set. So instead of using TextBox in your XAML you will use the inherited textbox and set your own dependency property in the binding.
Just bind the textbox to the instance of a MyTextBoxClass instead of MyTextBoxClass.Value and use a valueconverter to create a string from the value and formatstring.
Another solution is to use a multivalue converter which would bind to both Value and FormatString.
The first solution don't support changes to properties, that is if value or formatstring changes the value converter will not be called like it would be if you are using a multivalueconverter and binding directly to the properties.
One could create an attached behavior that could replace the binding with one that has the FormatString specified. If the FormatString dependency property then the binding would once again be updated. If the binding is updated then the FormatString would be reapplied to that binding.
The only two tricky things that I can think that you would have to deal with. One issue is whether you want to create two attached properties that coordinate with each other for the FormatString and the TargetProperty on which the binding exist that the FormatString should be applied (ex. TextBox.Text) or perhaps you can just assume which property your dealing with depending on the target control type. The other issue may be that it may be non-trivial to copy an existing binding and modifying it slightly given the various types of bindings out there which might also include custom bindings.
It's important to consider though that all of this only achieves formatting in the direction from your data to your control. As far as I can discover using something like a MultiBinding along with a custom MultiValueConverter to consume both the original value and the FormatString and produce the desired output still suffers from the same problem mainly because the ConvertBack method is only given the output string and you would be expected to decipher both the FormatString and the original value from it which at that point is almost always impossible.
The remaining solutions that should work for bidirectional formatting and unformatting would be the following:
Write a custom control that extends TextBox that has the desired formatting behavior like Jakob Christensen suggested.
Write a custom value converter that derives from either DependencyObject or FrameworkElement and has a FormatString DependencyProperty on it. If you want to go the DependencyObject route I believe you can push the value into the FormatString property using the OneWayToSource binding with a "virtual branch" technique. The other easier way may to instead inherit from FrameworkElement and place your value converter into the visual tree along with your other controls so that you can just bind to it when needed by ElementName.
Use an attached behavior similar to the one I mentioned at the top of this post but instead of setting a FormatString instead have two attached properties, one for a custom value converter and one for the parameter that would be passed to the value converter. Then instead of modifying the original binding to add the FormatString you would be adding the converter and the converter parameter to the binding. Personally I think this option would result in the most readable and intuitive result because attached behaviors tend to be more clean yet still flexible enough to use in a variety of situations other than just a TextBox.

Resources