I've bound my TextBox to a string value via
Text="{Binding AgeText, Mode=TwoWay}"
How can I display string.empty or "" for the string "0", and all other strings with their original value?
Thanks for any help!
Cheers
PS: One way would be a custom ViewModel for the string.. but I'd prefer to do it somehow in the XAML directly, if it's possible.
I think the only way beside using the ViewModel is creating a custom ValueConverter.
So basically your choices are:
ViewModel:
private string ageText;
public string AgeText{
get{
if(ageText.equals("0"))
return string.empty;
return ageText;
}
...
}
ValueConverter:
public class AgeTextConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value.Equals("0"))
return string.Empty;
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
}
}
I found something on http://www.codeproject.com/KB/books/0735616485.aspx
This will do the trick:
Text="{Binding AgeText, StringFormat='\{0:#0;(#0); }'}"
Cheers
Since the Age property is obviously a number here, an other way to go would be to expose the Age as an int and use the StringFormat attribute of the Binding:
Text="{Binding Age, Mode=TwoWay, StringFormat='{}{0:#}'}"
Related
I know that you can limit the input characters of TextBox from user by setting MaxLength property.
Is there a similar way to limit the number of characters shown in Text when the Text is updated with Binding? For example, when it is updated from Binding just show the first 5 characters and leave the rest?
Update:
Thanks for all the info, I got inspired by your recommendation and in the end did it with a converter. Here is how I did it, if someone wants to use it later.
public class StringLimiter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string val = value.ToString();
if (val.Length < 5)
return val;
else
return val.Substring(0, 5);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string val = value.ToString();
if (val.Length < 5)
return val;
else
return val.Substring(0, 5);
}
}
This should work:
Xaml:
<TextBox Text="{Binding TextToDisplay}" />
Code:
private const int maxLength = 5;
private string _textToDisplay = "Hello SO";
public string TextToDisplay
{
get
{
if(_textToDisplay.Length > maxLength)
{
return _textToDisplay.Substring(0, maxLength);
}
return _textToDisplay;
}
set
{
_textToDisplay = value;
RaiseProperyChanged();
}
}
I hope to understand you right. You could create a new Property in the ViewModel that returns only the first 5 chars of the text and set your binding to that property.
You might need to call PropertyChanged for the new Property when the text changes.
A simple but very flexible way of doing it would be to introduce a projected property in your Viewmodel that returns the first 5 characters of the original property and then bind your control to this property. Since you're only showing part of the property value, I assume that you don't want to write to this property from that TextBox. So make you projected property read-only too.
I have a TextBlock as follow:
<TextBlock Text="You don't have any more items." Visibility="{binding}"
and in code behind I defined a Stack called items as follow:
private Stack<Item> _items;
How do I bind the text visibility in xaml to visible when _item.Any is false?
There are several steps to achieving what you want to do and they are all described here
You need to create a value converter similar to this;
public class EmptyCollectionToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var collection = (Stack<int>) value;
return collection.Any() ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then you need to add a reference to is in your resource dictionary in your xaml like this;
<views:EmptyCollectionToVisibilityConverter x:Key="EmptyCollectionToVisibilityConverter"/>
Finally bind your property in your view model to the visibility of your control and give the binding the converter like this;
Visibility="{Binding Items, Converter={StaticResource EmptyCollectionToVisibilityConverter}}"
Your property will probably need to be an observableCollection (which will mean changing the value converter example I gave you slightly.
I'd probably go with:
private Stack<Item> _items;
// bind to this property using converter
public bool IsVisible => !(_items?.Any(...) ?? false);
You shouldn't expose your _stack directly, but e.g. use methods to do something (because you need to rise notification every time you push/pop an item):
public void PushItem(Item item)
{
_items.Push(item);
OnPropertyChanged(nameof(IsVisible)); // implement INotifyPropertyChanged
}
I am working on a WPF app. In this app, I have some XAML segments. I need to display the XAML segments in a TextBlock. In my XAML, I have the following line:
<TextBlock Text="{Binding Path=XamlSegment, Converter={StaticResource XamlToTextConverter}}" />
The XamlSegment property will have a value like "-0.275*x2". In an attempt to render this XAML in my UI so that the Superscript shows, I'm using the XamlToTextConverter, which is defined as follows:
namespace MyApp.Converters
{
public class XamlToTextConverter : IValueConverter
{
private static readonly Regex Regex = new Regex("(<.*?)>(.*)(</.*?>)", RegexOptions.Compiled);
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// value looks like this: -0.275*x<Run Typography.Variants="Superscript">2</Run>
var xamlText = value as string;
if (xamlText != null)
{
try
{
xamlText = "<TextBlock>" + xamlText + "</TextBlock>";
var xamlTextWithNamespace = Regex.Replace(xamlText, "$1 xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">$2$3");
return XamlReader.Parse(xamlTextWithNamespace);
}
catch (Exception)
{
return value;
}
}
else
{
return value;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
When this converter gets ran, my UI shows "System.Windows.Controls.TextBlock" instead of the rendered XAML. Yet, I don't know why. How do I get my XamlSegment to render in my UI?
Thanks
The Text property of the TextBlock will be set to a TextBlock object according to your XamlToTextConverter. Since the Text property should be type of string, it doesn't know how to show a TextBlock as string. So the default way to get thing done is to use the ToString method on TextBlock to fill the Text property, which makes the value of Text as "System.Windows.Controls.TextBlock".
It seems that you would like to dynamically render the xaml. You could reference this link(Loading XAML XML through runtime?) for a solution.
I have a C# WPF 4.51 application. On one of my XAML forms I have a list box that has its ItemsSource property bound to a property in my main ViewModel that is of type Collection. When the Collection was of type string, everything worked fine and I saw the collection of strings in the list box.
But then I changed the type in the Collection to a class named ObservableStringExt. The class has two fields: StrItem that contains the string I want displayed in the list box, and IsSelected, a supporting field. I then created a value converter to extract the StrItem field and return it.
However, when I look at the targetType passed to the Convert() method of the value converter I see a type of IEnumerable. Given that the Count property in that parameter matches the number of list items expected, it looks like the Convert() method is receiving a reference to the entire Collection instead of ObservableStringExt, the type of each item in the Collection. This of course is a problem. What is causing this? I have done this sort of thing many times in Windows Phone and WinRT (windows store apps) many times without trouble.
Here is the code for the value converter:
public class ObservableStringExtToStrItem : IValueConverter
{
// The targetType of the value received is of type IEnumerable, not ObservableStringExt.
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is ObservableStringExt)
return (value as ObservableStringExt).StrItem;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Below is the XAML code for the list box. Note Commands_FrequentyUsed is a property of type ObservableCollectionWithFile found in the main view model, which is the data context for the entire form:
<ListBox x:Name="listFrequentlyUsedCommands"
Width="278"
Height="236"
Margin="30,103,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemsSource="{Binding Commands_FrequentyUsed.Collection,
Converter={StaticResource ObservableStringExtToStrItem}}" />
Here is the code for the class that contains the Collection that the list box binds to and the class the Collection contains:
public class ObservableStringExt
{
public string StrItem { get; set;}
public bool IsSelected{ get; set; }
}
public class ObservableCollectionWithFile : BaseNotifyPropertyChanged
{
public const string CollectionPropertyName = "Collection";
private ObservableCollection<ObservableStringExt> _observableCollection = new ObservableCollection<ObservableStringExt>();
public ObservableCollection<ObservableStringExt> Collection
{
get { return _observableCollection; }
private set { SetField(ref _observableCollection, value); }
}
} // public class ObservableCollectionWithFile
I just had the same problem. Not sure how it should normally work, but changing the converter to also convert list of items helped (I found this easier than creating a separate converter for List)
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var list = value as IEnumerable<ObservableStringExt>;
if (list != null)
{
return list.Select(x => Convert(x, typeof(string), null, culture));
}
if (value is ObservableStringExt)
return (value as ObservableStringExt).StrItem;
}
I am just learning Datagrids and had a question I could not find a clear answer to.
How would I be able to manipulate a bound string to replace everything before the last / and add then a different prefix.
This will be to show an image in a Datagrid.
For example what is bound to img variable is
img: "/img_banner/testBanner.jpg"
What finally needs to be output is this:
http://www.testsite.com/img_thumnail/testBanner.jpg
So far, I have found information about adding text to a bound string but I would like to remove part of the String.
Here is what I got so far:
<Image Source="{Binding img, StringFormat=http://www.testsite.com/img_thumnail{0}}" Height="40"/>
Is there any way to easily trim everything before the last / ?
You can use an IValueConverter:
public class MyUrlConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(value == null)
{
return null;
}
var urlString = value as string;
//now do whatever you want to do with the string
//then return it
return urlString;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
then add it to your resources
<Window.Resources>
<local:MyUrlConverter x:Key="conv"></local:MyUrlConverter>
</Window.Resources>
and use it as:
<Image Source="{Binding img,Converter={StaticResource ResourceKey=conv}}"></Image>
note that if the logic dependant on your view model then you better do your thing in the getter or the setter of your img property