I've the next converter code like the example in this post Xamarin.Forms Binding Converter but when the value come in method only have the property Path="DeviceHasScanner"
and the Binding property never is called :(
public class BoolToObjectConverter<T> : IValueConverter
{
public T TrueObject { set; get; }
public T FalseObject { set; get; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? TrueObject : FalseObject;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((T)value).Equals(TrueObject);
}
}
And I'm try implement it on this way:
<Image HorizontalOptions="Center" WidthRequest="200">
<Image.Source>
<Binding Source="{Binding DeviceHasScanner}">
<Binding.Converter>
<converters:BoolToObjectConverter x:TypeArguments="x:String" TrueObject="presstoscan.png" FalseObject="scanwithcamera.png" />
</Binding.Converter>
</Binding>
</Image.Source>
</Image>
And the boolean binding property in viewmodel is:
public bool DeviceHasScanner
{
get
{
return Settings.Get.DeviceHasScanner; //This code return true or false
}
}
What is the correct way to implement it?
This is not something that you should be using a converter for tbh, what you should be doing instead is assigning your ImageSource through a boolean or keeping the condition for this in your VM where this is directly handled. Converters are usually used in scenarios where you have value conversions.
But if you still insist on using this approach then there are some basic changes that you will need to do first of all you are misunderstanding what Source means here,
<Binding Source="{Binding DeviceHasScanner}">
Source in the above code does not mean the property which you need to provide but the context in which you wanna perform the lookup, that is the reason when you see the object it just copy-pastes the name you have as binding here, Now Source is only required if your Converter should be looking up in a particular context. I am pretty sure this is not the case here so all you need to do is give the Path property the value of your Binding.
So this <Binding Source="{Binding DeviceHasScanner}"> would become something like
<Binding Path="DeviceHasScanner"/>
Now here if your Source is in the same Binding Context you won't need to do anything but if it's not then you need to provide the reference of your BindingContext as shown in the example you can find https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/data-binding/converters#binding-converter-properties
Good luck!!
Related
I have the button below which has it's IsEnabled property bound to a bool in the ViewModel called EnableBtn.
If I have another bool called EnableMail how would I amend this so that the IsEnabled is bound to both?
<Button IsEnabled="{Binding EnableBtn, Converter={StaticResource InvertBooleanConverter}}" x:Name="SaveSendButton" Grid.Row="0" Grid.Column="1" Text="{i18n:Translate SaveAndSend}" Style="{StaticResource bottomButtonsBlue}" Command="{Binding EmailPlanCommand}"></Button>
public bool IsBothEnabled
{
get
{
if (EnableBtn && EnableMail)
return true;
return false;
}
}
Now bind your Button.IsEnabled Property to IsBothEnabled.
Alternative to the valid solution from meq, you could use a multi binding:
The XAML code would look like:
<Button.IsEnabled>
<MultiBinding Converter="{StaticResource AreAllTrueMultiValueConverter}">
<Binding Path="EnableBtn" />
<Binding Path="EnableMail" />
</MultiBinding>
</TextBox.IsEnabled>
However, you need a MultiValueConverter similar to:
public class AreAllTrueMultiValueConverter: IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
return values.OfType<bool>().All();
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("Cannot convert back");
}
}
I would prefer the MultiBinding to the additional view model property because it doesn't require "dependent properties" that has to be notified if another property changed. Therefore it results in simpler view model logic.
Is it possible to bind a TextBox Text field to a property TextView in my model with mode OneWay, and bind to another property TextInput with mode OneWayToSource?
ie. If I change the TextView property in code, the wpf control content changes. And if I type in the TextBox, the change is reflected in TextInput.
I agree it's not the right way to do this, but being able to do this would spare me a lot of work right now.
Is it possible to bind a TextBox Text field to a property TextView in my model with mode OneWay, and bind to another property TextInput with mode OneWayToSource?
No. You can only apply a single binding to a target property.
You could use a multi converter to convert the value of several properties into one target value though: https://blog.csainty.com/2009/12/wpf-multibinding-and.html.
Something like this:
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource MultiValueConverter}">
<Binding Path="TextView" />
<Binding Path="TextInput" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
public class NameMultiValueConverter : IMultiValueConverter
{
private string _textView;
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//display the value of TextView
_textView = values[0].ToString();
return _textView;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return new object[] { _textView, /* TextInput: */ value.ToString() };
}
}
Alternatively you could handle the conversion logic in the setter of your source property.
I am working on a windows universal app and I'm trying to work out data binding.
I have a listview which has an item template and data template in which a property of a custom class is bound.
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<Textblock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This works fine an displays the names all instances of my custom class in the ObservableCollection I bind to the listview. I was wondering however if there is some way of modifying what is being bound before it is bound without changing the class itself.
I'm trying to bind a capitalisation of the string property Name so if the name was Test I want to bind TEST instead. Currently the way I'm doing this is to have a separate property called NameLabel which I populate like this
NameLabel = Name.ToUpper();
However this seems very messy and I was wondering if there's a neater way of doing it without creating a separate property?
You can use a Converter.
Create a StringToUpper.cs File with a StringToUpper Class which inherits form IValueConverter:
public class StringToUpper: IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
var valueString = value.ToString();
if (!string.IsNullOrEmpty(valueString))
{
return valueString.ToUpper();
}
return string.Empty;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
// do nothing.
}
}
Add the resource of your created Converter:
...
xmlns:converter="clr-namespace:StringToUpper"
...>
<Window.Resources>
<converter:StringToUpper x:Key="StringToUpperConverter" />
</Window.Resources>
Add the converter:
<Textblock Text="{Binding Name, Converter={StaticResource StringToUpperConverter}}"/>
Here is a good Tutorial about Converters in WPF.
You could create a converter, that turns the value of a property into a form desired for the view (in your case, a simple capitalisation of the string).
Your converter class might look like this:
public class StringToUpperStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var valueString = value.ToString();
if (!string.IsNullOrEmpty(valueString))
{
return valueString.ToUpper();
}
return string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Your xaml might have this defined in the resource section:
<converters:StringToUpperStringConverter x:Key="StringToUpperStringConverter" />
And your binding would then look like this:
<Textblock Text="{Binding Name, Converter={StaticResource StringToUpperStringConverter}}"/>
So I'm trying to build out a project that will allow a user to type some text into a textbox on the left side of the form and that will filter out the available items from my datasource list.
<Label Content="Enter item name below"></Label>
<TextBox Name="SearchTermTextBox" TabIndex="0" Text="" />
I was under the impression I could bind to the datasource the list then use a converter to filter out the items that were unlike the string.
<ListBox DataContext="{Binding Colors}">
<ListBox.ItemsSource>
<MultiBinding Converter="{StaticResource FilterTextValueConverter}" ConverterParameter="{Binding ElementName=SearchTermTextBox, Path=Text}" />
</ListBox.ItemsSource>
<ListBox.ItemTemplate>
//etc...
</ListBox.ItemTemplate>
</ListBox>
However, you can't bind to an elementname in the converterparameter unless you use something called a dependency property.
Edit: Seeing as I've created confusion with the code above, here's the converter I'm trying to bind:
public class FilterTextValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var trackedColors = value as List<Colors>;
if (trackedColors != null)
return (trackedColors).Where(item => item.ColorName.Contains(parameter.ToString())).ToList();
return null;
}
public object ConvertBack(object value, Type targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class Colors
{
public String ColorName;
public String Description;
}
What is wrong with my approach here? Clearly I'm angering the WPF gods since this is a fairly straightforward operation but I'm being denied it on principle. Any help would be appreciated.
Simple binding with converter will work here, no need for MultiBinding.
<ListBox ItemsSource="{Binding Path=Text, ElementName=SearchTermTextBox,
Converter="{StaticResource FilterTextValueConverter}">
......
</ListBox>
Assuming FilterTextValueConverter is implementing IValueConverter, you can access text from value passed to Convert method.
public class FilterTextValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
string text = value.ToString(); // TEXT for textBox can be accessed here.
return new List<string>(); // Return filtered list from here.
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return Binding.DoNothing;
}
}
UPDATE
In case you want to pass multiple bindings to converter, use IMultiValueConverter because ConverterParameter is not Dependency property, hence cannot be bound.
XAML
<ListBox DataContext="{Binding Colors}">
<ListBox.ItemsSource>
<MultiBinding Converter="{StaticResource FilterTextValueConverter}">
<Binding/>
<Binding ElementName="SearchTermTextBox" Path="Text"/>
</MultiBinding>
</ListBox.ItemsSource>
</ListBox>
Converter
public class FilterTextValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
var trackedColors = values[0] as List<Colors>;
if (trackedColors != null && !String.IsNullOrEmpty(values[1].ToString()))
return (trackedColors).Where(item =>
item.ColorName.Contains(values[1].ToString())).ToList();
return null;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I continued looking into this issue well after the accepted answer was posted and working for me. What I discovered is that it's a fairly trivial task to wrap the control you're trying to get a new dependencyproperty out of to allow for proper binding.
I will not be accepting my own answer to this determined so much later, but this seems (in my amateur opinion) like a much more elegant solution than adding a converter despite being a bit more complex:
Note that this is for a new dependency on the caretindex property of a textbox, not for the original question on binding, but it just requires some smart renaming to get it working ;).
public class TextBoxDependencyWrapper : TextBox
{
public static readonly DependencyProperty CaretIndexProperty = DependencyProperty.Register(
"CaretIndex", typeof (int), typeof (TextBoxDependencyWrapper), new FrameworkPropertyMetadata(default(int), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, CaretIndexChanged ));
protected override void OnKeyUp(KeyEventArgs e) //Event that changes the property we're trying to track
{
base.OnKeyUp(e);
CaretIndex = base.CaretIndex;
}
protected override void OnKeyDown(KeyEventArgs e) //Event that changes the property we're trying to track
{
base.OnKeyDown(e);
CaretIndex = base.CaretIndex;
}
public new int CaretIndex
{
get { return (int) GetValue(CaretIndexProperty); }
set { SetValue(CaretIndexProperty, value); }
}
}
I have an enum with each value representing an image, for example:
public enum AnimalImages
{
Cow,
Cat,
Dog
}
I also have a converter that takes the enum and returns an ImageSource:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ResourceManager.Instance.GetImageFromEnum((AnimalImages)value);
}
I am using WPF with MVVM; now, I want to add an image to my view. Using the Cow enum, I would like the cow image. I know that I can add a property to my ViewModel and call it like this:
public AnimalImages CowImage
{
return AnimalImages.Cow;
}
and then bind it to my UI. But, I'm thinking there is a better way of doing this. Something like the following (which doesn't work of course):
<Image Source="{x:Static images:AnimalImages.Cow}, Converter={StaticResource AnimalImagesImageBitmapSource}}"/>
any suggestions?
Answer
That is the way to do it if the ViewModel property is an enum. It would look like this:
<!-- DataContext of the Image has to be the ViewModel -->
<Image Source="{Binding CowImage, Converter={StaticResource AnimalImagesImageBitmapSource}}"/>
Alternative
You could also do it so that your ViewModel property CowImage actually returns a URI or an ImageSource of the image, that way you don't need the converter and it looks "cleaner".
Like this:
ViewModel.cs
public ImageSource CowImage
{
get
{
return ResourceManager.Instance.GetImageFromEnum(AnimalImages.Cow);
}
}
Xaml File
<Image Source="{Binding CowImage}"/>
You are almost there. Just need to use Binding and pass static value as Source.
<Image Source="{Binding Source={x:Static images:AnimalImages.Cow},
Converter={StaticResource AnimalImagesImageBitmapSource}}"/>