Is there any way to bind a value to a textblock that is obtained from a method. For example, I pass my Person object into the HierarchicalDataTemplate, from there I can access its Weight property. Now lets say I want to get the weight in mars, I would call the InMars method that takes a parameter of int EarthWeight . Now earthweight is going to change from Person to Person, how can this parameter be set every time?
The best way to do this is with a converter.
public class WeightOnMarsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// value will be the persons weight
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException("This method should never be called");
}
}
Then you just need to set up the binding.
<l:WeightOnMarsConverter x:key="weightOnMars" /> <-- Add this to the resources
{Binding Path=Weight, Converter={StaticResource weightOnMars}}
Related
I am trying to convert a value on my view model to the type that a control needs. I have an IValueConverter implementation that is being called. It is returning a valid value. However, the value that is returned to the control is not what's returned by the converter.
NOTE Below is a rough example, though not exactly my scenario. I can't post my code as it belongs to the company. Also, I've been unable to replicate the problem. The same scenario in another application works fine.
My converter:
public class TupleCollectionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var members = (IEnumerable<Tuple<string, string>>) value;
var retVal = new ObservableCollection<string>(members.SelectMany(t => new[] {t.Item1, t.Item2}));
return retVal;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And in the XAML:
<ItemsControl ItemsSource="{Binding ValuePairs, Converter={StaticResource MyTupleCollectionConverter}}"/>
When I debug, putting a break point at the return of the converter, I see that it's converting correctly with values, but then I get nothing in the control. Running Snoop, it says that the ItemsSource property is set to null.
I'm relatively new to WPF so apologies if there is an obvious or simple answer to this that I am not seeing.
I have an ObservableCollection of items with several different sized images for each.
The relative path for each image is in string format, with image files stored in different subfolders.
The image paths are in the format:
imagepath = #"subfolder/subfolder/filename.png"
I would like to be able to bind to a textblock but show only the filename below each image without changing the image path. Is this possible? I gather it would require a converter of some sort, but I have been struggling with it as I cannot find a reasonable way of showing only part of a string.
Thanks for your help.
Edit
To clarify, my 'value' is non-static, referencing items in an observable collection eg.
ObservableCollection<Icon> items = new ObservableCollection<Item>();
items.Add(new Item{imagename = "someimagename",
imagepath= "somefolder/somesubfolder/somefilename.png"}) etc...
I'm only starting to get to grips with 'getting' values from my collection. Any help with filling in the 'value.Tostring()' part to get dynamic values for item.imagepath would be really appreciated so I can get this working.
I've currently been trying along these lines:
class getFilenameFromPathConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{ Item item = value as Item;
PropertyInfo info = value.GetType().GetProperty("imagepath");
string filename = info.GetValue(item, null).ToString();
return System.IO.Path.GetFileNameWithoutExtension(filename); }
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
But I'm getting an Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
Thanks
XAML as specified by fatty.
Your converter should look like:
class getFilenameFromPathConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Path.GetFileName(value.ToString());
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
You will need to enlist the help of a converter to get this value.
Create a class that implements IValueConverter, and in the Convert method return the part of the string that you want to display.
In your TextBlock, you bind to the property like {Binding Path=imagepath, Converter={StaticResource getFilenameFromPathConverter}}
I know there are several ways to do it, but I would like to make it even easier if possible because I have a lot of comboboxes to bind in this way. There is a suggestion using ObjectDataProvider here. The problem is that I have to create a resource entry for each enum and that's a lot. So far, I have been using the code-behind way because it's much shorter:
cmb.ItemsSource = Enum.GetValues(typeof(MyTypes));
I'm wondering if an equivalent can be produced in Xaml. I thought we could archive this by using a converter. We could convert the type to an array and then bind the array to combobox' ItemsSource. But I got stuck on how to specify my enum to the converter. Here is my code:
My enum:
public enum MyTypes { Type1, Type2, Type3 };
This is my converter:
public class EnumToArrayConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Enum.GetValues(value.GetType());
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null; // I don't care about this
}
}
My Xaml Resource:
<lib:EnumToArrayConverter x:Key="E2A"/>
Here is how to use it:
<ComboBox SelectedItem="{Binding MyType}" ItemsSource="{Binding MyTypes, Converter={StaticResource E2A}}"/>
So, my question is how to specify my enum "MyTypes" to the converter. I also tried to prepend namespace, but it doesn't help.
You would be better off with a MarkupExtension, like this one.
CodeNaked posts a great way of doing this
For your approach to work you can change the converter to Enum.GetValues(value as Type) and use the x:Type syntax as Source for the Binding
ItemsSource="{Binding Source={x:Type local:MyValues},
Converter={StaticResource EnumToArrayConverter}}"
EnumToArrayConverter
public class EnumToArrayConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Enum.GetValues(value as Type);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null; // I don't care about this
}
}
I'm using MVVM, in case it makes a difference.
My MainWindowViewModel has two DependencyProperties, TheList, and TheSelectedItem. TheList is a List<Type>, TheSelectedItem is a Type.
The MainWindow has a ComboBox. When the MainWindowViewModel loads it grabs a list of all the classes in the assembly that implement IMyInterface and sets TheList to this.
Each of these classes has a custom attribute applied called DisplayName, which has one parameter, that will be used to show a user-friendly name for the class instead of the name the application knows about for the class.
I've also got a ValueConverter for the express purpose of converting these types into the display names.
public class TypeToDisplayName : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (targetType.Name == "IEnumerable")
{
List<string> returnList = new List<string>();
if (value is List<Type>)
{
foreach (Type t in value as List<Type>)
{
returnList.Add(ReflectionHelper.GetDisplayName(t));
}
}
return returnList;
}
else
{
throw new NotSupportedException();
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return typeof(BasicTemplate);
}
}
So, what I wind up with is a ComboBox with a list of names in it that the user should be able to understand. Awesome! This is just what I want!
Next step: I bind the SelectedItem property of my ComboBox to my TheSelectedItem property in my ViewModel.
Here's the problem: When I make a selection, I get a little red box around my ComboBox and the TheSelectedItem property on my ViewModel never gets set.
I'm pretty sure it's because of a type mismatch (the items in the ComboBox appear to be strings now, and TheSelectedItem is of type Type--also, when I change TheSelectedItem to a string instead of a Type, it works). But I don't know where I need to start coding to convert the (hopefully unique) DisplayName that's in the ComboBox back to a Type object.
Thanks in advance for any help. I'm pretty stumped on this one.
If I understand your question correctly then you use that Converter on the ItemsSource for the ComboBox? In that case I think you can let the ItemsSource be like it is and instead just Convert each type when they are presented like this.
<ComboBox ...>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=typeName, Converter={StaticResource TypeToDisplayNameConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
And then just convert each type in the Converter.
public class TypeToDisplayNameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Type t = (Type)value;
return ReflectionHelper.GetDisplayName(t);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}
Make sure you have IsSynchronizedWithCurrentItem set to true on the ComboBox. Check this out...
I have a PriorityBinding
<PriorityBinding FallbackValue="Bindings were null">
<Binding Path="Foo" />
<Binding Path="Bar" />
</PriorityBinding>
I'd like to make it so if Foo is null it will use Bar and if both are null it will use the FallbackValue. However null is a valid value for this property because it only expects an object.
Is there any way to make the PriorityBinding advance to the next binding when the value is null? I'd prefer to do it in XAML, but if I can't I'll just make a converter for it.
Edit
I ended up just writing a converter for it
public class NullToDependencyPropertyUnsetConverter
: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value ?? DependencyProperty.UnsetValue;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I'd go with the valueconverter returning UnsetValue if the bound value is null.
PriorityBindings are more useful if you want to share a datatemplate between different types of objects.