I have a databound TextBox in my application like so: (The type of Height is decimal?)
<TextBox Text="{Binding Height, UpdateSourceTrigger=PropertyChanged,
ValidatesOnExceptions=True,
Converter={StaticResource NullConverter}}" />
public class NullableConverter : IValueConverter {
public object Convert(object o, Type type, object parameter, CultureInfo culture) {
return o;
}
public object ConvertBack(object o, Type type, object parameter, CultureInfo culture) {
if (o as string == null || (o as string).Trim() == string.Empty)
return null;
return o;
}
}
Configured this way, any non-empty strings which cannot be converted to decimal result in a validation error which will immediately highlight the textbox. However, the TextBox can still lose focus and remain in an invalid state. What I would like to do is either:
Not allow the TextBox to lose focus until it contains a valid value.
Revert the value in the TextBox to the last valid value.
What is the best way to do this?
Update:
I've found a way to do #2. I don't love it, but it works:
private void TextBox_LostKeyboardFocus(object sender, RoutedEventArgs e) {
var box = sender as TextBox;
var binding = box.GetBindingExpression(TextBox.TextProperty);
if (binding.HasError)
binding.UpdateTarget();
}
Does anyone know how to do this better? (Or do #1.)
You can force the keyboard focus to stay on the TextBox by handling the PreviewLostKeyBoardFocus event like this:
<TextBox PreviewLostKeyboardFocus="TextBox_PreviewLostKeyboardFocus" />
private void TextBox_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) {
e.Handled = true;
}
It sounds to me that you'll want to handle two events:
GotFocus: Will trigger when the textbox gains focus. You can store the initial value of the box.
LostFocus: Will trigger when the textbox loses focus. At this point you can do your validation and decide if you want to roll back or not.
Related
tl;dr: Coerced values are not propagated across data bindings. How can I force the update across the data binding when code-behind doesn't know the other side of the binding?
I'm using a CoerceValueCallback on a WPF dependency property and I'm stuck at the issue that coerced values don't get propagated through to bindings.
Window1.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace CoerceValueTest
{
public class SomeControl : UserControl
{
public SomeControl()
{
StackPanel sp = new StackPanel();
Button bUp = new Button();
bUp.Content = "+";
bUp.Click += delegate(object sender, RoutedEventArgs e) {
Value += 2;
};
Button bDown = new Button();
bDown.Content = "-";
bDown.Click += delegate(object sender, RoutedEventArgs e) {
Value -= 2;
};
TextBlock tbValue = new TextBlock();
tbValue.SetBinding(TextBlock.TextProperty,
new Binding("Value") {
Source = this
});
sp.Children.Add(bUp);
sp.Children.Add(tbValue);
sp.Children.Add(bDown);
this.Content = sp;
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value",
typeof(int),
typeof(SomeControl),
new PropertyMetadata(0, ProcessValueChanged, CoerceValue));
private static object CoerceValue(DependencyObject d, object baseValue)
{
if ((int)baseValue % 2 == 0) {
return baseValue;
} else {
return DependencyProperty.UnsetValue;
}
}
private static void ProcessValueChanged(object source, DependencyPropertyChangedEventArgs e)
{
((SomeControl)source).ProcessValueChanged(e);
}
private void ProcessValueChanged(DependencyPropertyChangedEventArgs e)
{
OnValueChanged(EventArgs.Empty);
}
protected virtual void OnValueChanged(EventArgs e)
{
if (e == null) {
throw new ArgumentNullException("e");
}
if (ValueChanged != null) {
ValueChanged(this, e);
}
}
public event EventHandler ValueChanged;
public int Value {
get {
return (int)GetValue(ValueProperty);
}
set {
SetValue(ValueProperty, value);
}
}
}
public class SomeBiggerControl : UserControl
{
public SomeBiggerControl()
{
Border parent = new Border();
parent.BorderThickness = new Thickness(2);
parent.Margin = new Thickness(2);
parent.Padding = new Thickness(3);
parent.BorderBrush = Brushes.DarkRed;
SomeControl ctl = new SomeControl();
ctl.SetBinding(SomeControl.ValueProperty,
new Binding("Value") {
Source = this,
Mode = BindingMode.TwoWay
});
parent.Child = ctl;
this.Content = parent;
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value",
typeof(int),
typeof(SomeBiggerControl),
new PropertyMetadata(0));
public int Value {
get {
return (int)GetValue(ValueProperty);
}
set {
SetValue(ValueProperty, value);
}
}
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
}
Window1.xaml
<Window x:Class="CoerceValueTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CoerceValueTest" Height="300" Width="300"
xmlns:local="clr-namespace:CoerceValueTest"
>
<StackPanel>
<local:SomeBiggerControl x:Name="sc"/>
<TextBox Text="{Binding Value, ElementName=sc, Mode=TwoWay}" Name="tb"/>
<Button Content=" "/>
</StackPanel>
</Window>
i.e. two user controls, one nested inside the other, and the outer one of those in a window. The inner user control has a Value dependency property that is bound to a Value dependency property of the outer control. In the window, a TextBox.Text property is bound to the Value property of the outer control.
The inner control has a CoerceValueCallback registered with its Value property whose effect is that this Value property can only be assigned even numbers.
Note that this code is simplified for demonstration purposes. The real version doesn't initialize anything in the constructor; the two controls actually have control templates that do everything that's done in the respective constructors here. That is, in the real code, the outer control doesn't know the inner control.
When writing an even number into the text box and changing the focus (e.g. by focusing the dummy button below the text box), both Value properties get duly updated. When writing an odd number into the text box, however, the Value property of the inner control doesn't change, while the Value property of the outer control, as well as the TextBox.Text property, show the odd number.
My question is: How can I force an update in the text box (and ideally also in the outer control's Value property, while we're at it)?
I have found an SO question on the same problem, but doesn't really provide a solution. It alludes to using a property changed event handler to reset the value, but as far as I can see, that would mean duplicating the evaluation code to the outer control ... which is not really viable, as my actual evaluation code relies on some information basically only known (without much effort) to the inner control.
Moreover, this blogpost suggests invoking UpdateTarget on the binding in TextBox.Text in the CoerceValueCallback, but first, as implied above, my inner control cannot possibly have any knowledge about the text box, and second, I would probably have to call UpdateSource first on the binding of the Value property of the inner control. I don't see where to do that, though, as within the CoerceValue method, the coerced value has not yet been set (so it's too early to update the binding), while in the case that the value is reset by CoerceValue, the property value will just remain what it was, hence a property changed callback will not get invoked (as also implied in this discussion).
One possible workaround I had thought of was replacing the dependency property in SomeControl with a conventional property and an INotifyPropertyChanged implementation (so I can manually trigger the PropertyChanged event even if the value has been coerced). However, this would mean that I cannot declare a binding on that property any more, so it's not a really useful solution.
I have been looking for an answer to this rather nasty bug myself for a while.
One way to do it, without the need to force an UpdateTarget on the bindings is this:
Remove your CoerceValue callback.
Shift the logic of the CoerceValue callback into your ProcessValueChanged callback.
Assign your coerced value to your Value property, when applicable (when the number is odd)
You will end up with the ProcessValueChanged callback being hit twice, but your coerced value will end up being effectively pushed to your binding.
Base on your code, your dependency property declaration would become this:
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value",
typeof(int),
typeof(SomeControl),
new PropertyMetadata(0, ProcessValueChanged, null));
And then, your ProcessValueChanged would become this:
private static void ProcessValueChanged(object source, DependencyPropertyChangedEventArgs e)
{
int baseValue = (int) e.NewValue;
SomeControl someControl = source as SomeControl;
if (baseValue % 2 != 0)
{
someControl.Value = DependencyProperty.UnsetValue;
}
else
{
someControl.ProcessValueChanged(e);
}
}
I slightly modified your logic, to prevent raising the event when the value needs to be coerced. As mentionned before, assigning to someControl.Value the coerced value will cause your ProcessValueChanged to be called twice in a row. Putting the else statement would only raise the events with valid values once.
I hope this helps!
I just noticed that WPF textboxes bound to numeric data do not fire Property Set when non-numeric events happen such as letters/spaces typed or text cleared. This becomes a problem when I'm trying to validate that a textbox has a valid number. If the user types in 5 and presses backspace, the databound property remains 5 while the textbox appears empty! I have no way of disabling a button to stop further progress. Is there anyway to enable non-numeric notifications when bound to numeric data? Or, am I forced to use a string property/data converter? Thanks.
If you do not like the default converter you need to create your own which returns a valid value if the input is empty or unparsable.
public class IntBindingConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string input = value as string;
if (String.IsNullOrWhiteSpace(input))
{
return 0;
}
else
{
int outInt;
if (int.TryParse(input, out outInt))
{
return outInt;
}
else
{
return 0;
}
}
}
}
Example usage:
<TextBox>
<TextBox.Text>
<Binding Path="Max">
<Binding.Converter>
<vc:IntBindingConverter/>
</Binding.Converter>
</Binding>
</TextBox.Text>
</TextBox>
This may look a bit messy but normally you in fact just stop the user from proceeding.
I have just upgraded our wpf application from 3.5sp1 to 4.0.
The code below we use to bind the textbox to the underlying view model. The textbox is editable.
<TextBox HorizontalContentAlignment="Right"
Text="{Binding Path=Price, StringFormat={0:#,##0;(#,##0)}, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}"/>
In 3.5sp1 the formatting would only occur initially. So when the textbox was loaded and bound to value 4000, the formatting would change it to 4,000. If user edited this value no formatting would occur.
In 4.0 the formatting occurs as the value changes (ie while user enters in new value). While in theory this sounds OK, in reality its a disaster. The cursor is all over the place. Its unusable.
Now, we could change the UpdateSourceTrigger to "LostFocus" but that introduces new problems with data not being validated in certain scenarios.
Is there a way to get the old 3.5sp1 behaviour back?
Update 1
Using Converter still procudes same behaviour:
public class DecimalConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
return ((decimal)value).ToString("#,##0;(#,##0)");
return string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
and the modified XAML:
<TextBox Text="{Binding Path=Price, Converter={StaticResource DecimalConverter}, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}"/>
Update 2
Similar to this connect article.
As an update I took Jonathans suggestion and rejigged the Binding to use LostFocus instead of PropertyChanged (where appropriate - ie wherever StringFormat was also specified).
As Jonathan said, in some cases you have to trigger binding refresh / validation manually taking this approach.
If anyone has better approach, I would love to see it.
I wasn't satisfied with the LostFocus solution, so I decided to code a method that manually moves the caret correctly. I've put it in the code behind file and by adding it to the TextChanged event on the TextBox it get it to run every time the text changes.
void moveCaret(object sender, TextChangedEventArgs args)
{
TextBox tb = (TextBox) sender;
if (args.Changes.Any())
{
var first = args.Changes.First();
int offset = 1;
if(first.AddedLength > 0)
{
if (tb.Text.Length > 4 && tb.Text.Length % 4 == 1)
offset = 2;
tb.CaretIndex = first.Offset + offset;
}
else
{
if (tb.CaretIndex > 0)
{
offset = 0;
if (tb.Text.Length > 2 && (tb.Text.Length + 2) % 4 == 1)
offset = -1;
tb.CaretIndex = first.Offset + offset;
}
}
}
args.Handled = true;
}
Just add this to the TextChanged event like so:
MyTextBox.TextChanged += moveCaret;
I'm not 100% sure, but this seems to behave well, though it doesn't handle deleting of the thousand separator.
EDIT: I figured out how to handle the thousand separator. I made another method in the code behind file, and put it on the PreviewKeyDown event on the TextBox. This method checks if the TextBox is receiving a Backspace of Delete button input, and just ignores it and moves the caret in stead.
private void handleThousandSeparator(object sender, KeyEventArgs e)
{
var textBox = sender as TextBox;
if (e.Key == Key.Back)
{
if (textBox.CaretIndex > 0)
{
if (textBox.Text[textBox.CaretIndex - 1] +"" == System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator)
{
if (textBox.Text[0] + "" == System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator)
return;
textBox.CaretIndex = textBox.CaretIndex - 1;
e.Handled = true;
}
}
}
if (e.Key == Key.Delete)
{
if (textBox.CaretIndex < textBox.Text.Length)
{
if (textBox.Text[textBox.CaretIndex] + "" == System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator)
{
if (textBox.Text[0] + "" == System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator)
return;
textBox.CaretIndex = textBox.CaretIndex + 1;
e.Handled = true;
}
}
}
}
Notice the special case for a thousand separator at the first char in the TextBox, where it is deleted in stead of skipped. A thousand separator should ideally never get to be there, but the n0 number formatter doesn't handle the case where you delete the first numbers before the first thousand separator.
You can try to remove StringFormat={0:#,##0;(#,##0)} and write converter to do formating.
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.
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.