Multibinding not working on TextBox.Text - wpf

I have a MultiBinding that is not working on TextBox.Text. I have the same code that is binding properly to Value of Extended WPF Toolkit's IntegerUpDown.
It is going through an IMultiValueConverter that takes the bound POCO and the listbox it is part of (it is displaying the order of the item in the listbox)
Here is the code:
<!--works-->
<wpf:IntegerUpDown ValueChanged="orderChanged" x:Name="tripOrder">
<wpf:IntegerUpDown.Value>
<MultiBinding Converter="{StaticResource listBoxIndexConverter}" Mode="OneWay">
<Binding />
<Binding ElementName="listTrips" />
</MultiBinding>
</wpf:IntegerUpDown.Value>
</wpf:IntegerUpDown>
<!--doesn't work-->
<TextBox x:Name="tripOrder2">
<TextBox.Text>
<MultiBinding Converter="{StaticResource listBoxIndexConverter}" Mode="OneWay">
<Binding />
<Binding ElementName="listTrips" />
</MultiBinding>
</TextBox.Text>
</TextBox>
Here is the result:
I don't believe it is relevant, but just in case, here is the class that performs the conversion:
public class ListBoxIndexConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var trip = values[0] as TripBase;
if (trip == null)
{
return null;
}
var lb = values[1] as CheckListBox;
if (lb == null)
{
return null;
}
//make it 1 based
return lb.Items.IndexOf(trip) + 1;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}

The converter should return the type that the property expects. The reason is that in regular use of the properties (i.e. without Binding), the properties may have type converters that convert from one type (or more) to the type required by the property. For example, when you write:
<ColumnDefinition Width="Auto"/>
there's a converter that converts string "Auto" to:
new GridLength(1, GridUnitType.Auto)
When using binding, this mechanism is bypassed since the converter should return the right type.
So, to fix your issue, at the return of your converter:
return (lb.Items.IndexOf(trip) + 1).ToString();
This should fix the TextBox.
Now, for the IntegerUpDown. It sounds like it actually expects to receive an int and returning a string will break it. So, again, change the return of the converter:
if (targetType == typeof(int))
{
return lb.Items.IndexOf(trip) + 1;
}
else if (targetType == typeof(string))
{
return (lb.Items.IndexOf(trip) + 1).ToString();
}
else
{
throw new NotImplementedException(String.Format("Can not convert to type {0}", targetType.ToString()));
}

The binding is not going to work, because the listTrips is not changing when the list box's selected value changes. The thing that changes is listTrips.SelectedItem, so you should bind against it:
<Binding Path="SelectedItem" ElementName="listTrips"/>
Actually, I wonder why it works for the first example.

Related

FallBackValue in IMultiValueConverter is UnsetValue

I am trying to use a single IMultiValueConverter for more than one controls in XAML.
I am using a simple string Literal to tell what value the IMultiValueConverter is supposed to return.
But I am getting DependencyProperty.UnsetValue in values[2],ie value of parametter named Command when it comes to convert function of ModifierCategoryEnableDisable.
Similar arrangment is working on similar controls on this XAML form within other IMultiValueConverters but not here.
Please guide what am i missing?
NOTE:
CurrentRec is the currently selected object from the ViewModel
DM_CategoryData is a Class and Current_Selected_Category is a List<DM_CategoryData> in the ViewModel's current object,ie CurrenRec.
XAML:
<GroupBox Width="226" Height="117" Margin="0" Canvas.Top="252" Header="Modifiers" Canvas.Left="55" >
<GroupBox.IsEnabled>
<MultiBinding Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" Converter="{StaticResource MDNS}">
<Binding Path="SearchFound" />
<Binding Path="CurrentRec.Current_Selected_Category"/>
<Binding Path="Command" FallbackValue="1" />
</MultiBinding>
</GroupBox.IsEnabled>
</GroupBox>
C#:
public class ModifierCategoryEnableDisable : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string Command = values[2].ToString();
bool Retval1 = false;
string Retval2 = "";
switch(Command)
{
case "1":
bool SearchFound = (bool)values[0];
DM_CategoryData CurrentSelectedItemCategory = (DM_CategoryData)(values[1]);
Retval1 = SearchFound && (CurrentSelectedItemCategory == null ? true : CurrentSelectedItemCategory.IsModifier.Equals("1") ? false : true);
break;
case "2":
Retval2 = "0";
break;
}
if(Command.Equals("1"))
{
return Retval1;
}
else
{
return Retval2;
}
}
}
In order to provide additional static data to a multibinding converter, use a ConverterParameter:
<MultiBinding Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" Converter="{StaticResource MDNS}" ConverterParameter="1">
<Binding Path="SearchFound" />
<Binding Path="CurrentRec.Current_Selected_Category"/>
</MultiBinding>
And check the parameter in the Convert method:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string Command = parameter as string;
// ...
}
You are trying to set the fallbackvalue for GroupBox.IsEnabled property and it is a bool type. But you are setting the value as 1. So only Values[2] returns the UnsetValue. Try to set bool value as Fallbackvalue.

Binding a bool property to a bool expression [duplicate]

I have a WPF control that has a Message property.
I currently have this:
<dxlc:LayoutItem >
<local:Indicator Message="{Binding PropertyOne}" />
</dxlc:LayoutItem>
But i need that Message property to be bound to two properties.
Obviously can't be done like this, but this can help explain what it is I want:
<dxlc:LayoutItem >
<local:Indicator Message="{Binding PropertyOne && Binding PropertyTwo}" />
</dxlc:LayoutItem>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="FirstName"/>
<Binding Path="LastName"/>
</MultiBinding>
</TextBlock.Text>
Try use the MultiBinding:
Describes a collection of Binding objects attached to a single binding target property.
Example:
XAML
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource myNameConverter}"
ConverterParameter="FormatLastFirst">
<Binding Path="FirstName"/>
<Binding Path="LastName"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Converter
public class NameConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string name;
switch ((string)parameter)
{
case "FormatLastFirst":
name = values[1] + ", " + values[0];
break;
case "FormatNormal":
default:
name = values[0] + " " + values[1];
break;
}
return name;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
string[] splitValues = ((string)value).Split(' ');
return splitValues;
}
}
You can't do And operation in XAML.
Create wrapper property in your view model class which will return and of two properties and bind with that property instead.
public bool UnionWrapperProperty
{
get
{
return PropertyOne && PropertyTwo;
}
}
XAML
<local:Indicator Message="{Binding UnionWrapperProperty}" />
Another approach would be to use MultiValueConverter. Pass two properties to it and return And value from the converter instead.

Why MVVM/WPF ComboBox SelectedItem is null in multibinding to other control's Visibility?

EDIT: I bound to same property (SearchType) that combobox binds to -> works fine. I still would like to know why my first solution described here does not work.
I have
public enum SearchType
{
NetworkObjects,
Customers
}
In ViewModel contructor:
public SearchViewModel()
{
SearchType = Panels.SearchType.NetworkObjects;
In xaml:
<UserControl.Resources>
<xpui:ConvertSearchTypeToVisibility x:Key="searchtypetovisibilityconverter" />
</UserControl.Resources>
<ComboBox
Name="SearchTypeComboBox"
ItemsSource="{Binding Path=SearchTypes}"
SelectedItem="{Binding Path=SearchType, Mode=TwoWay}">
...
<DataGrid.Visibility>
<MultiBinding Converter="{StaticResource searchtypetovisibilityconverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="Name"/>
<Binding ElementName="SearchTypeComboBox" Path="SelectedItem" />
</MultiBinding>
</DataGrid.Visibility>
Converter:
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string gridName = (string)values[0];
SearchType searchType = (SearchType)values[1];
In Convert-method values have 2 items and values[1]==null. Also if I take away the binding SelectedItem is SearchType.NetworkObjects as set in ViewModel constructor. What do I do wrong?
I expect that there is something going wrong in code that you haven't posted. I wrote up a solution with very similar behavior using the provided code and there was no case where values[1] == null unless I removed the ComboBox.SelectedItem binding.
Here is the working sample.
The problem is that in platform InitializeComponent in code behind is called before the DataContext is set by the platform I use. Therefore the Converter is called with the unbound (default) values, which in this case is null for the SelectedItem. The solution is to check the values-array and especially values[1] and return the Bindin.DoNothing if the value is null (or anything but SearchType).
Guess this is good practice in general. Thanks to #Neil and #NETScape to point this out.
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values != null && values.Length == 2 && values[0] is string && values[1] is SearchType)
{
string gridName = (string)values[0];
SearchType searchType = (SearchType)values[1];
if ((gridName == "ObjectSearchResults" && searchType == SearchType.NetworkObjects) ||
(gridName == "CustomerSearchResults" && searchType == SearchType.Customers))
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
return Binding.DoNothing;
}

WPF ListBox Binding ItemsSource

In a related question I asked about binding to a particular element of an array indexed by another property. The answer provided worked really well for the sample code example provided.
Where I'm running into trouble is that I specify an ItemSource for a ListBox and I get a DependencyProperty.UnsetValue in my converter when I step through it. No doubt this is a problem with my understanding of Binding.
My ListBox looks like so:
<ListBox ItemsSource="{Binding Path=MyList}">
<ListBox.Resources>
<local:FoodIndexConverter x:Key="indexConverter" />
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource indexConverter}">
<Binding Path="MyIndex" />
<Binding Path="Fields" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and the code behind looks as so:
public MainWindow()
{
InitializeComponent();
MyList.Add(new SomeData() { Fields = new object[] {"liver", "onions", "cake" } } );
MyList.Add(new SomeData() { Fields = new object[] {"liver", "onions", "candy" } } );
MyList.Add(new SomeData() { Fields = new object[] {"liver", "onions", "pie" } } );
DataContext = this;
}
MyList is a List.
MyIndex is an int.
The converter code is
public class FoodIndexConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values == null || values.Length != 2)
return null;
int? idx = values[0] as int?;
object[] food = values[1] as object[];
if (!idx.HasValue || food == null)
return null;
return food[idx.Value];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
When I step through the debugger in the converter code the MyIndex (value[0]) is DependencyProperty.UnsetValue - the object array is as I'd expect.
I'm assuming it is a Binding issue with:
in that it doesn't know what MyIndex is.
If MyIndex were a property of the SomeData class, it works as I would expect but it's not, it's a property of the class MainWindow, just like MyList.
How do I specify that I'm interested in the MyIndex property that is part of my DataContext and not the MyData List?
It is looking for the MyIndex property on the ListBoxItem (in this case, a SomeData class) and that property doesn't exist.
Set the ElementName in your binding to the window name to get it to look for the property elsewhere.
<Binding ElementName=RootWindow, Path=DataContext.MyIndex />

Databinding to the value of a multiBinding

Maybe I am not quite grasping multibindings.
I have a property on my viewmodel called OfficeDisplayName that is written to the database.
This is a concatenated field based on a person's FirstName, Lastname, and office location.
So I have a multibinding on a textBlock...no biggie...works beautifully...but how do I bind the full value of this concatenation to the OfficeDisplayName property? Do I have to have a hidden element that binds to the multibound textbox? I have seen several examples that are almost what I need, but just dont answer the concat databinding question.
One way is to let the textblock bind directly to OfficeDisplayName and then put the concatenation logic in the OfficeDisplayName property on your viewmodel instead of in the MultiValueConverter. So when ever one of the properties FirstName, LastName, or office location change you'd fire the PropertyChanged event for OfficeDisplayName - i.e. something along the following lines. This way you will not need a converter at all:
class YourViewModel : ViewModel
{
string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
if (_firstName != value)
{
_firstName = value;
OnPropertyChanged("FirstName");
OnPropertyChanged("OfficeDisplayName");
}
}
}
// More properties here
// ...
public string OfficeDisplayName
{
get { return String.Join(" ", new string[] { _firstName, _lastName, _officeLocation}); }
}
}
Another way is to pass your viewmodel itself as a parameter to your MultiValueConverter. In your converter you can set the value of OfficeDisplayName directly. I think this way is a bit "hack-ish" but it is a matter of taste. Your code would look like the following:
The binding in XAML:
<MultiBinding Converter="{StaticResource theConverter}" Mode="OneWay">
<Binding /> <!-- Pass the datacontext as the first parameter -->
<Binding Path="FirstName" />
<Binding Path="LastName" />
</MultiBinding>
The converter:
class TheMultiValueConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var viewModel = values[0] as TheViewModel;
var ret = String.Join(" ", values.Skip(1).Cast<string>().ToArray());
viewModel.OfficeDisplayName = ret;
return ret;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}

Resources