Bind OneWay and OneWayToSource separately? - c#

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.

Related

How to add binding property to Label with converter BoolToObjectConverter

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!!

MVVM bind IsEnabled to multiple bools in ViewModel

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.

Philosophy on Binding to Parameters in WPF

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); }
}
}

How to bind the IsEnabled property to an OR of two values?

currently when I have to make an OR of two values on the IsEnabled property of a control I end using an invisible container control (I use a Border) and setting the IsEnabled of the control and the one of the container.
Is there a better approach? If not, what is the most lightweight control for doing this?
Thanks in advance.
If IsEnabled is set via binding, you may use MultiBinding in conjunction with a multi-value converter.
You can use a converter like this:
public class BooleanOrConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
foreach (object value in values)
{
if ((value is bool) && (bool)value == true)
{
return true;
}
}
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("BooleanOrConverter is a OneWay converter.");
}
}
And this is how you would use it:
<myConverters:BooleanOrConverter x:Key="BooleanOrConverter" />
...
<ComboBox Name="MyComboBox">
<ComboBox.IsEnabled>
<MultiBinding Converter="{StaticResource BooleanOrConverter}">
<Binding ElementName="SomeCheckBox" Path="IsChecked" />
<Binding ElementName="AnotherCheckbox" Path="IsChecked" />
</MultiBinding>
</ComboBox.IsEnabled>
</ComboBox>
Could use a MultiBinding with a converter which or's the values passed in.

Bind to the difference of two Properties from two different ViewModels

I have two viewmodels (each with thier own models):
AmendmentViewModel
YearViewModel
each have a property:
AmendmentViewModel.TotalAmended
YearViewModel.TotalCommitted
On the view there is a TabControl and each of the viewmodels is the datacontext for a tabcontrol page. Yes i know I could technically use one view model but its a LOT of code and a large view that Has to be in a tabcontrol.
How do i set the binding of a TextBox to the sum of AmendmentViewModel.TotalAmended & YearViewModel.TotalCommitted ?
You can use a MultiBinding together with an IMultiValueConverter. You can find an example here.
Edit:
Here's an example:
<Grid>
<Grid.Resources>
<sys:String x:Key="dataSource1">42</sys:String>
<sys:String x:Key="dataSource2">22</sys:String>
<local:SubtractionConverter x:Key="subtractionConverter"/>
</Grid.Resources>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource subtractionConverter}">
<Binding Path="." Source="{StaticResource dataSource1}"/>
<Binding Path="." Source="{StaticResource dataSource2}"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
and the converter:
public class SubtractionConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return (double.Parse((string)values[0]) - double.Parse((string)values[1])).ToString();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Categories