WPF CommandParameter MultiBinding values null - c#

I am simply trying to bind two controls as command parameters and pass them into my command as an object[].
XAML:
<UserControl.Resources>
<C:MultiValueConverter x:Key="MultiParamConverter"></C:MultiValueConverter>
</UserControl.Resources>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Button Name="Expander" Content="+" Width="25" Margin="4,0,4,0" Command="{Binding ExpanderCommand}">
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource MultiParamConverter}">
<Binding ElementName="Content"/>
<Binding ElementName="Expander"/>
</MultiBinding>
</Button.CommandParameter>
</Button>
<Label FontWeight="Bold">GENERAL INFORMATION</Label>
</StackPanel>
<StackPanel Name="Content" Orientation="Vertical" Visibility="Collapsed">
<Label>Test</Label>
</StackPanel>
</StackPanel>
Command:
public ICommand ExpanderCommand
{
get
{
return new RelayCommand(delegate(object param)
{
var args = (object[])param;
var content = (UIElement)args[0];
var button = (Button)args[1];
content.Visibility = (content.Visibility == Visibility.Visible) ? Visibility.Collapsed : Visibility.Visible;
button.Content = (content.Visibility == Visibility.Visible) ? "-" : "+";
});
}
}
Converter:
public class MultiValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return values;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException("No two way conversion, one way binding only.");
}
}
Basically what is happening is that the binding seems to be working fine and the converter is returning an object[] containing the correct values, however when the command executes the param is an object[] containing the same number of elements except they are null.
Can someone please tell me why the values of the object[] parameter are being set to null?
Thanks,
Alex.

This'll do it:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values.ToArray();
}
Take a look at this question for the explanation.

Related

WPF Binding to items within a dictionary by key

What I want it to bind TextBoxes to my dictionaries values.
I could find some posts about it already but :
One means having my dictionary as context :
XML :
<TextBox x:Name="FirstLine" Text="{Binding Path=[FirstLine]}"/>
XAML :
public ImportProfilesOptions()
{
InitializeComponent();
contexte = new ViewModelImportProfilesOptions();
DataContext = contexte.ParamsData;
}
The other one using Templates :
<ItemsControl x:Name="dictionaryItemsControl" ItemsSource="{Binding dictionary}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
But I would like to use it, without using Templates (I need to add in labels some translates I take from properties), and without setting my dictionary as context. Something like that :
XML
<TextBox x:Name="FirstLine" Text="{Binding Path=ParamsDate[FirstLine]}" />
XAML
contexte = new ViewModelImportProfilesOptions();
DataContext = contexte;
But then, binding is not working anymore.
You can't do this out of the box, though you could create your own converter I guess:
public class SomeFunkyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is Dictionary<string,string> dictionary))
return null;
if (!(parameter is string key))
return null;
return !dictionary.TryGetValue(key, out var result) ? null : result;
}
// ill leave this up to you
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
}
Usage
<TextBlock Text="{Binding Path=ParamsDate,
ElementName=TextBox,
Converter={StaticResource SomeFunkyConverter},
ConverterParameter='Bob'}"/>
Note : Completely untested

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.

System.Windows.Data.MultiBinding.Converter threw an exception

trying to implement some code behind in my xaml document. The idea here is that the code behind will compare two values, and return True if they strings are equal, but i keep getting System.Windows.Data.MultiBinding.Converter threw an exception
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cvm="clr-namespace:Mashup;assembly=CompareValuesMashup"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:dat="clr-namespace:System.Windows.Data;assembly=PresentationFramework"
xmlns:xlink="http://www.w3.org/1999/xlink">
<Grid.Resources>
<cvm:CompareTwoValues x:Key="CompareValues" />
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<StackPanel Width="200" HorizontalAlignment="Left" Margin="5">
<!-- values to be processed by the converter -->
<TextBox Name="value1" Height="50" Margin="0,0,0,0" />
<TextBox Name="value2" Height="50" Margin="0,15,0,0" />
<!-- Compare value1 and value2-->
<TextBox Name="result1" Height="50" Margin="0,15,0,0">
<TextBox.Text>
<MultiBinding Converter="{StaticResource CompareValues}">
<Binding ElementName="value1" Path="Text" />
<Binding ElementName="value2" Path="Text" />
</MultiBinding>
</TextBox.Text>
</TextBox>
</StackPanel>
</Grid>
here's the code behind. I'm not sure where the exception is coming from.
namespace Mashup
{
public class CompareTwoValues
{
public CompareTwoValues()
{
base..ctor();
}
public bool Execute(string value1, string value2)
{
return value1 == value2;
}
}
}
CompareTwoValues doesn't implement IMultiValueConverter. It doesn't implement any interface or base class, so I wouldn't expect it to be usable by the framework in similar scenarios either.
It needs to implement that interface to be used by the framework:
public class CompareTwoValues : IMultiValueConverter
{
public CompareTwoValues()
{
base..ctor();
}
public bool Execute(string value1, string value2)
{
return value1 == value2;
}
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
// convert the values to the target type
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
// convert the value back to the set of target types
}
}
Currently the class compares values, but it doesn't convert anything. It's not entirely clear from the existing class logic what that conversion would look like, but it needs to happen in order to be used in this context.
An example of converting multiple values to a single value might be something like:
return String.Concat(values[0], " ", values[1]);
And converting back might be something like:
return (value as string).Split(' ');
(If, for example, the values were strings and the desired result is a concatenated space-delimited string.)
Wouldn't it be much better to implement a single ValueConverter?
like this:
public class CompareValues: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value == parameter;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and then use it
<!-- Compare value1 and value2-->
<TextBox Name="result1" Height="50" Margin="0,15,0,0">
<TextBox.Text>
<Binding Converter="{StaticResource CompareValues}" ElementName="value1" Path="Text">
<Binding.ConverterParameter>
<Binding Path="Text" ElementName="value2"/>
</Binding.ConverterParameter>
</MultiBinding>
</TextBox.Text>
</TextBox>
#david This worked or my purposes. I needed to return a string that i can use to trigger a style.
public class IsEqual : IMultiValueConverter
{
public object Convert(object[] values, Type targetTypes, object parameter, CultureInfo culture)
{
if (values[0] == "")
return string.Format("blank");
// returns yes if equal
else if (string.Equals(values[0], values[1]))
return string.Format("yes");
//returns no if not equal
else
return String.Format("no");
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}

Multibinding generates "Cannot set MultiBinding because MultiValueConverter must be specified"

I have a button with binding which works fine, see below:
<Button x:Name="licenceFilterSet" Content="Search" Command="{Binding searchCommand}" CommandParameter="{Binding Path=Text, ElementName=licenseTextBox}" />
Now I have realized that I need yet another piece of information, so I need to send the value of a check-box as well.
I modified the VM like this:
<Button x:Name="licenceFilterSet" Content="Search" Command="{Binding licenseSearchCommand}">
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource searchFilterConverter}">
<Binding Path="Text" ElementName="licenseTextBox" />
<Binding Path="IsEnabled" ElementName="regularExpressionCheckBox" />
</MultiBinding>
</Button.CommandParameter>
</Button>
Below is my multi-converter:
/// <summary>
/// Converter Used for combining license search textbox and checkbox
/// </summary>
public class SearchFilterConverter : IMultiValueConverter
{
public object Convert(object[] values)
{
return new Tuple<String, bool>((String)values[0], (bool)values[1]);
}
}
What am I doing wrong. I am getting the following error, (which is pointing to my MultiBinding-tag in XAML):
Cannot set MultiBinding because MultiValueConverter must be specified.
you have to implement IMultiConverter
public class SearchFilterConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return new Tuple<String, bool>((String)values[0], (bool)values[1]);;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
then create the resource in xaml
<Converter:SearchFilterConverter x:Key="searchFilterConverter" />
then it should work
<Button x:Name="licenceFilterSet" Content="Search" Command="{Binding licenseSearchCommand}">
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource searchFilterConverter}">
<Binding Path="Text" ElementName="licenseTextBox" />
<Binding Path="IsEnabled" ElementName="regularExpressionCheckBox" />
</MultiBinding>
</Button.CommandParameter>
</Button>
I know this thread is old, but I've faced the same problem yesterday where everything was written correctly yet the WPF was still refusing to locate the converter. What helped me was assigning the converter in the following manner:
<MultiBinding Converter="{local:ButtonParametersMultiValueConverter}">
That solved the issue.
That is not the correct implementation of the IMultiValueConverter interface.
The correct one is:
public class SearchFilterConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
....
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
}
}
Reference here.

Change Binding value in XAML

I need to make some complex binding in XAML. I have a DependencyProperty typeof(double); let's name it SomeProperty. Somewhere in XAML code of my control, I need to use the whole SomeProperty value, somewhere only a half, somewhere SomeProperty/3, and so on.
How can I do something like:
<SomeControl Value="{Binding ElementName=MyControl, Path=SomeProperty} / 3"/>
:)
Looking forward.
Use a division ValueConverter:
public class DivisionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int divideBy = int.Parse(parameter as string);
double input = (double)value;
return input / divideBy;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
<!-- Created as resource -->
<local:DivisionConverter x:Key="DivisionConverter"/>
<!-- Usage Example -->
<TextBlock Text="{Binding SomeProperty, Converter={StaticResource DivisionConverter}, ConverterParameter=1}"/>
<TextBlock Text="{Binding SomeProperty, Converter={StaticResource DivisionConverter}, ConverterParameter=2}"/>
<TextBlock Text="{Binding SomeProperty, Converter={StaticResource DivisionConverter}, ConverterParameter=3}"/>

Categories