I've written a converter BoolToStringConverter. The converter has two properties TrueString and FalseString. Here's how I've used it in XAML
<UserControl.Resources>
<local:BooleanToStringConverter x:Key="BooleanToStringConverter" TrueString="{Binding Strings.Open, Source={StaticResource MyStrings}}"></local:BooleanToStringConverter>
</UserControl.Resources>
This compiles ok, but I get an xml parse exception when running it. If I change the setting of the TrueString property to TrueString = "Open" it all works fine.
Here's the converter being used:
<Button x:Name="MyButton" Content="{Binding Path=IsOpen, Converter={StaticResource BooleanToStringConverter}}" Command="{Binding MyCommand}" VerticalAlignment="Top" Style="{StaticResource MyStyle}" Margin="0,2,10,2"/>
Any ideas what is wrong? All I want to do as set a property of a local resource to a localized value.
EDIT Here's my converter class
public class BooleanToStringConverter : IValueConverter
{
public BooleanToStringConverter()
{
}
public string TrueString
{
get;
set;
}
public string FalseString
{
get;
set;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool boolValue = System.Convert.ToBoolean(value, CultureInfo.InvariantCulture);
return boolValue ? TrueString : FalseString;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Here's the runtime exception message:
A first chance exception of type 'System.Windows.Markup.XamlParseException' occurred in System.Windows.dll
Additional information: Set property 'Optimize.Client.Presentation.BooleanToStringConverter.FalseString' threw an exception. [Line: 18 Position: 86]
You cannot bind to the TrueString and FalseString properties. From the MSDN help:
in order to be the target of a binding, the property must be a dependency propert
You can try using the ConverterParameter part of the binding for your xaml
<Button x:Name="MyButton" Content="{Binding Path=IsOpen, Converter={StaticResource BooleanToStringConverter}, ConverterParameter=Open}"
Command="{Binding MyCommand}" VerticalAlignment="Top"
Style="{StaticResource MyStyle}" Margin="0,2,10,2"/>
You could also make your converter less generic and only handle Open/Closed strings.
Another option is to have your value converter extend DependencyObject, and convert your properties to DependencyProperties.
You can also set the public properties in your XAML like this:
<localHelpers:BoolToTextConverter x:Key="boolToTextConverter">
<localHelpers:BoolToTextConverter.TrueText>
Sent
</localHelpers:BoolToTextConverter.TrueText>
<localHelpers:BoolToTextConverter.FalseText>
Not Sent
</localHelpers:BoolToTextConverter.FalseText>
</localHelpers:BoolToTextConverter>
The full example is on my blog post here.
Related
I have an ObservableCollection<MyEntity> and MyEntity has a IsChecked property with a PropertyChanged event.
I have a Button and I would like to change IsEnabled property to true when at least one of MyEntity of the MyObservableCollection is checked.
I created a converter which takes the ObservableCollection and return true when a MyEntity is checked at least.
But the return "null" is returned.
What is wrong ? Thank you for your help.
XAML
<Window.Resources>
<CollectionViewSource x:Key="MyObservableCollection"/>
<src:MyConverter x:Key="MyConverter"/>
</Window.Resources>
<Button IsEnabled="{Binding Converter={StaticResource MyConverter}, Source={StaticResource MyObservableCollection}}"/>
C# Converter
class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (null == value)
return "null";
ReadOnlyObservableCollection<object> items = (ReadOnlyObservableCollection<object>)value;
List<MyEntity> myEntities = (from i in items select (MyEntity)i).ToList();
foreach (MyEntity entity in myEntities)
{
if (entity.IsChecked)
{
return true;
}
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new System.NotImplementedException();
}
}
I think your Binding is wrong. The Converter want's the underlying collection not the CollectionView. And set the CollectionViewSource.Source after InitializeComponent(), the Binding will be refreshed.
<Button IsEnabled="{Binding Path=SourceCollection,
Converter={StaticResource MyConverter},
Source={StaticResource MyObservableCollection}}" />
Since StaticResources are resolved at the time of intializing itself i.e. at the time of InitializeComponent() but till that time your collection is yet not intialized that's why null value is passed to the converter.
So, better choice would be to move that property in your code behind and bind to that property since binding will be resloved after InitializeComponent(). Create property in your code-behind-
public CollectionViewSource MyObservableCollection { get; set; }
and bind to your button -
<Button IsEnabled="{Binding MyObservableCollection, RelativeSource=
{RelativeSource AncestorType=Window}, Converter={StaticResource MyConverter}}"/>
I'm trying to make a custom converter that inherits from DependencyObject, but it doesn't work:
Converter:
public class BindingConverter : DependencyObject , IValueConverter
{
public object Value
{
get { return (object)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(object), typeof(BindingConverter), new PropertyMetadata(null));
public object Convert(object value, Type targetType, object parameter, Globalization.CultureInfo culture)
{
Debug.Assert(Value != null); //fails
return Value;
}
public object ConvertBack(object value, Type targetType, object parameter, Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Xaml:
<StackPanel x:Name="this">
<!--works-->
<ContentControl Content="{Binding ActualHeight, ElementName=this}"/>
<!--doesn't work-->
<ContentControl>
<Binding>
<Binding.Converter>
<BindingConverter Value="{Binding ActualHeight, ElementName=this}" />
</Binding.Converter>
</Binding>
</ContentControl>
<TextBlock Text="{Binding Animals}"/>
</StackPanel>
Am I missing out anything?
I have some places in my projects where I needed similar functionality. Can't show you exact sample, just an idea:
perhaps you have to inherit from FrameworkElement, not IValueConverter, Something like this:
public class BindingHelper : FrameworkElement
in the BindingHelper class, set Visibility to Collapsed and IsHitTestVisible to false;
to make it working, insert it into visual tree directly. In your example, it should be a child of the StackPanel. So, it will have the same DataContext as other StackPanel children;
then, you can add one ore more dependency properties depending on your needs. For example, you might have single property for the source of data and some different properties which you then will use as converter return values. Handle all changes to the source property in your BindingHelper class and change output properties accordingly;
bind other controls to properties of the BindingHelper class using ElementName syntax
in Silverlight, ActualHeight and ActualWidth properties don't do notifications on property updates. So, binding to them won't work.
Note! ActualHeight property's binding is buggy on binding!
Why you inherit DependencyObject when coding a converter? You should just implement IValueConverter.
Try that,
First add MyConverter by the key of "MyConverterResource" on your resources then,
You can do than on XAML side or on cs side by
//You may do it on XAML side <UserControl.Resources>...
this.Resources.Add("MyConverterResource",new MyConverter());
<TextBlock Text="{Binding ActualHeight,ElementName=this
,Converter=MyConverterResource}"/>
public class MyConverter: IValueConverter
{
public object Convert(object value, Type targetType
, object parameter,Globalization.CultureInfo culture)
{
return "Your Height is:"+Value.toString();
}
}
Hope helps
I have a collection
private ObservableCollection<ImageData> imageDataList = new ObservableCollection<ImageData>(); where ImageData is a custom object. It has an attribute called fileName a string that stores full path of an image file. In my XAML code, I have a listbox with datatemplate as the following.
<ListBox Name="listBox_ImageList" Grid.ColumnSpan="3" Grid.Row="2" SelectionChanged="listBox_ImageList_SelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding fileName}" Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ListBox, AncestorLevel=1}, Path=ActualHeight}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
After populating ImagaData objects into imageDataList, I set this.listBox_ImageList.ItemsSource = imageDataList;
However, when I run it, I don't see any images. Can you please tell me how to databind properly to a string member of an object to WPF image source?
Checkout this http://social.msdn.microsoft.com/Forums/en-AU/wpf/thread/f94cc770-8d86-4a9f-a5f9-2ee2ea146c1a
Set the DataContext to where the object where the ObservableCollection is located
DateContext = this;
Also instead of fileName bind it to a ImageSource Property or a BitmapImage Property and this is created using the fileName.
To answer your question: You cannot bind the ImageSource property to a string. It works in XAML because WPF uses a default converter from string to ImageSource when you set the value in XAML. If you want to set the value with a binding or from code you need to provide an ImageSource object.
There are 2 ways to do it via binding:
The first one is presented here (the link Juan Carlos mentioned), and it involves creating a IValueConverter that will take your string and transform it to a ImageSource. I would modify the converter code presented there with this:
public sealed class StringToImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
return new BitmapImage(new Uri((string)value));
}
catch
{
return DependencyProperty.UnsetValue;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The second option is to create your ImageSource in your ImageData class and bind directly to it.
private ImageSource _imageSource
public ImageSource ImageSource
{
get
{
if (_imageSource == null)
{
_imageSource = new BitmapImage(new Uri(fileName), UriKind.RelativeOrAbsolute);
}
return _imageSource;
}
}
Public Class View
Public Property Items As String() = {"One", "Two", "Three"}
Public Property Index As Integer = 0
End Class
It's instance is set as DataContext of this XAML:
<Window>
<StackPanel>
<ListBox ItemsSource="{Binding Items}" SelectedIndex="{Binding Index}"/>
<Label Content="{Binding Items[Index]}"/>
</StackPanel>
</Window>
But this doesn't work.
<Label Content="{Binding Items[{Binding Index}]}"/>
This neither.
<Label Content="{Binding Items[0]}"/>
This works.
Is there any solution except making extra property in view? Something directly in XAML?
I'm afraid it's not possible without some code-behind, but using reflection and dynamic, you can create a converter that can do this (it would be possible without dynamic, but more complex):
public class IndexerConverter : IValueConverter
{
public string CollectionName { get; set; }
public string IndexName { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Type type = value.GetType();
dynamic collection = type.GetProperty(CollectionName).GetValue(value, null);
dynamic index = type.GetProperty(IndexName).GetValue(value, null);
return collection[index];
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Put following into resources:
<local:IndexerConverter x:Key="indexerConverter" CollectionName="Items" IndexName="Index" />
and use it like this:
<Label Content="{Binding Converter={StaticResource indexerConverter}}"/>
EDIT: The previous solution doesn't update properly when the values change, this one does:
public class IndexerConverter : IMultiValueConverter
{
public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
{
return ((dynamic)value[0])[(dynamic)value[1]];
}
public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
In resources:
<local:IndexerConverter x:Key="indexerConverter"/>
Usage:
<Label>
<MultiBinding Converter="{StaticResource indexerConverter}">
<Binding Path="Items"/>
<Binding Path="Index"/>
</MultiBinding>
</Label>
What you write in the binding markup extension is assigned to the Path property by default, this property is a string so any dynamic content you refer to inside it will not be evaluated. There is no simple XAML-only method to do what you try to do.
Why don't use this:
<StackPanel>
<ListBox Name="lsbItems" ItemsSource="{Binding Items}" SelectedIndex="{Binding Index}"/>
<Label Content="{Binding ElementName=lsbItems, Path=SelectedItem}"/>
</StackPanel>
I'm trying to bind element's Height value to Checkbox.IsChecked property. Why that's not working?
<Window.Resources>
<local:BoolToHeightConverter x:Key="BoolToHeightConverter"/>
</Window.Resources>
<Button Name="JustBtn" Content="Hello World"/>
<CheckBox IsChecked="{Binding ElementName=JustButton, Path=Height, Converter=BoolToHeightConverter}" />
[ValueConversion(typeof(Nullable<bool>), typeof(double))]
public class BoolToHeightConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return double.NaN;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
It doesn't even initalize the window. Says:
'IValueConverter' type does not have a public TypeConverter class
There are a couple of problems. First, it looks like you are trying to modify the Height property when the CheckBox is checked. If this is the case, you should implement your logic in the ConvertBack method of the converter, and specify a Mode on the Binding. Secondly, your Binding should use a StaticResource to reference your converter:
<CheckBox IsChecked="{Binding ElementName=JustButton, Path=Height, Converter={StaticResource BoolToHeightConverter}, Mode=OneWayToSource}" />
I'm sorry - my bad: I forgot to attach converter through StaticResource.
Sorry guys...