i'm facing a problem using MultiBinding and my custom IMulitValueConverter (using wpf and c#). First i'll show you my ViewModel and some xaml code and then i'll be describing my problem.
My ViewModel:
public class ParametersViewModel
{
private ICollection<ParameterValue> _paramValues;
public ICollection<ParameterValue> ParameterValues
{
get => _paramValues;
set
{
if (_paramValues != value)
{
_paramValues = value;
OnPropertyChanged(nameof(ParameterValues));
}
}
}
private ICollection<Parameter> _parameters;
public ICollection<Parameter> Parameters
{
get => _parameters;
set
{
if (value != _parameters)
{
_parameters= value;
OnPropertyChanged(nameof(Parameters));
}
}
}
Model:
public class Parameter : IModelBase
{
[Key]
public Guid ID { get; set; }
[StringLength(50)]
public string Name { get; set; }
}
public class ParameterValue : IModelBase
{
[Key]
public Guid ID { get; set; }
public Guid ParameterID { get; set; }
}
The converter:
public class ParameterVisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length != 2 || !(values[0] is Guid) || !(values[1] is ICollection<ParameterValue>))
return null;
Guid parameterID = (Guid)values[0];
ICollection<ParameterValue> valueInstances = (ICollection<ParameterValue>)values[1];
if (!(valueInstances ?.Any() ?? false)) return null;
// Find the SecondObject with the corresponding ID
ParameterValue parameterValue = valueInstances
.FirstOrDefault(c => c.ParameterID == parameterID);
if (parameterValue is null) return null;
// Return the Name property of the found SecondObject
return parameterValue.Visible;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
The XAML:
<DataGrid x:Name="dataGrid" Height="auto" Width="auto" AutoGenerateColumns="False"
ItemsSource="{Binding Parameters}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding ID}" Width="Auto" Header="ID" IsReadOnly="True" />
<DataGridCheckBoxColumn Header="Visibility" Width="Auto" MinWidth="0" >
<DataGridCheckBoxColumn.Binding>
<MultiBinding Converter="{StaticResource ParameterVisibilityConverter}" UpdateSourceTrigger="PropertyChanged">
<Binding Path="ID"/>
<Binding Path="DataContext.ParameterValues" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGrid}"/>
</MultiBinding>
</DataGridCheckBoxColumn.Binding>
</DataGridCheckBoxColumn>
The CheckBox will be shown and also its state but there is no possibility to change the state from the View. I tried several ways of implementing a ConvertBack method but all have failed.
How could a proper way look like to implement this functionality?
Thanks for your Input!
Tried to implement a ConvertBack method but there is no access to the ID of the parameter and the collection of parameter values.
Most multibindings are not meant to go two ways. But there are cases where you need it. There is a way to do it, but it is a hack which I recommend avoiding if you can. It goes like this
x:Shared="False"
attribute when you declare it in your resources or b) making it a MarkupExtension
object and always creating an instance in the binding (rather than using StaticResource
)So your converter might change to look like something like this
public class ParameterVisibilityConverter : IMultiValueConverter
{
private object[] Values { get; set; }
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length != 2 || !(values[0] is Guid) || !(values[1] is ICollection<ParameterValue>))
return null;
Guid parameterID = (Guid)values[0];
ICollection<ParameterValue> valueInstances = (ICollection<ParameterValue>)values[1];
if (!(valueInstances ?.Any() ?? false)) return null;
Values = values;
// Find the SecondObject with the corresponding ID
ParameterValue parameterValue = valueInstances
.FirstOrDefault(c => c.ParameterID == parameterID);
if (parameterValue is null) return null;
// Return the Name property of the found SecondObject
return parameterValue.Visible;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
// We remembered the values. Just return them.
// If more complicated logic is required, do that instead
return Values;
}
}
But avoid this if you can. There are sometimes ways. For example, if your ICollection<ParameterValue>
argument were some never-changing value that you could declare as a in XAML as a static resource then you could instead supply it to a single-value (IValueConverter.Convert
) method as the ConverterParameter
argument. That is preferrable, IMHO.