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();
}
}
Related
Creating button which reacts to a dynamic resource (style for dark and light themes) is done like this:
<Button>
<Image Source="{DynamicResource IconId_12}" />
</Button>
The difficulty comes about when attempting the same concept for an ItemsControl of buttons with different icons for each button, each which have a key which refers to either a dark or light themed image source:
<ItemsControl ItemsSource="{Binding ButtonVMs}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type ButtonVM}">
<Button Command="{Binding ClickCommand}">
<Image Source="{DynamicResource {Binding IconKey}}" />
</Button>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
Where ButtonVM looks like this:
public class ButtonVM {
public Command ClickCommand { get; set; }
public string IconKey { get; set; }
}
How can I accomplish binding the resource key name into the dynamic binding?
I have noted that in code you can use <FrameworkElement>.SetResourceReference(SourceProperty, "IconKey"). (as suggested in this stackoverflow answer). But the problem here is that the VM is not a FrameworkElement.
Using a multi-value converter, I was able to access the FrameworkElement (Image) and utilize .SetResourceReferece() to achieve the effect I needed.
public class ImageSourceKeyToDynamicResourceConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var image = (Image)values[0];
var key = (string)values[1];
image.SetResourceReference(Image.SourceProperty, key);
return frameworkElement.GetValue(Image.SourceProperty);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The xaml sideof things:
<Image>
<Image.Source>
<MultiBinding Converter="{StaticResource KeyToDynamicResourceConverter}">
<Binding RelativeSource="{RelativeSource Self}" />
<Binding Path="IconKey" Mode="OneWay" />
</MultiBinding>
</Image.Source>
</Image>
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.
I'm trying to pass in an array of system:Object to my converter as a parameter:
Xaml: Doesn't work
<TextBlock.Text>
<Binding ElementName="MainGrid" Path="DataContext" Converter="{StaticResource TestConverter}">
<Binding.ConverterParameter>
<x:Array Type="system:Object">
<Binding ElementName="MainGrid" Path="DataContext" />
<Binding ElementName="SomeOtherElement" Path="DataContext" />
</x:Array>
</Binding.ConverterParameter>
</Binding>
</TextBlock.Text>
Following XAML Does work, I found a sample online that used an array of Brush:
<TextBlock.Text>
<Binding ElementName="MainGrid" Path="DataContext" Converter="{StaticResource TestConverter}">
<Binding.ConverterParameter>
<x:Array Type="Brush">
<SolidColorBrush Color="LawnGreen"/>
<SolidColorBrush Color="LightSkyBlue"/>
<SolidColorBrush Color="LightCoral"/>
</x:Array>
</Binding.ConverterParameter>
</Binding>
</TextBlock.Text>
I get a System.Windows.Markup.XamlParseException: A 'Binding' cannot be used within a 'ArrayList' collection. A 'Binding' can only be set on a DepedencyProperty or a DependencyObject.
I've tried one of the suggested answers e.g. adding the ViewModel as a Dependency Object to my converter but that isn't working
public class TestConverter : DependencyObject , IValueConverter
{
public static readonly DependencyProperty PropertyTypeProperty = DependencyProperty.Register(
"PropertyType", typeof (DerivedRacingViewModel), typeof (TestConverter), new PropertyMetadata(default(DerivedRacingViewModel)));
public DerivedRacingViewModel PropertyType
{
get { return (DerivedRacingViewModel) GetValue(PropertyTypeProperty); }
set { SetValue(PropertyTypeProperty, value); }
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var x = parameter;
return "Test";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var x = parameter;
var z = parameter;
throw new NotImplementedException();
}
}
Then changing my xaml to:
<converters:TestConverter x:Key="TestConverter" DerivedRacingViewModel="{Binding}" />
That give me compile time errors:
'DerivedRacingViewModel' was not found in type 'TestConverter'.
The reason behind doing this is I want to have 2 or 3 objects available when I'm doing my ConvertBack, e.g. I need the text that is entered into text box, the value that text box is bound to and the view model. This is where I'm having real difficulty. I've seen other people doing it by splitting strings and stuff but I really don't like that.
You should use an ItemsControl like below :
<TextBlock.Text>
<Binding ElementName="MainGrid" Path="DataContext" Converter="{StaticResource TestConverter}">
<Binding.ConverterParameter>
<ItemsControl>
<ItemsControl.Items>
<Label Content="{Binding ElementName=MainGrid, Path=DataContext}"/>
<Label Content="{Binding ElementName=SomeOtherElement, Path=DataContext}"/>
</ItemsControl.Items>
</ItemsControl>
</Binding.ConverterParameter>
</Binding>
</TextBlock.Text>
TestConverter
public class TestConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
ItemsControl ctrl = parameter as ItemsControl;
Label lbl = ctrl.Items[0] as Label;
var c = lbl.Content;
...
}
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.
I'm displaying a popup with the following code:
<Popup PlacementTarget="{Binding ElementName=categoryTagEditorControl}"
Placement="Bottom">
<Popup.IsOpen>
<MultiBinding Mode="OneWay" Converter="{StaticResource BooleanOrConverter}">
<Binding Mode="OneWay" ElementName="categoryTagEditorControl" Path="IsMouseOver"/>
<Binding RelativeSource="{RelativeSource Self}" Path="IsMouseOver" />
</MultiBinding>
</Popup.IsOpen>
<StackPanel>
<TextBox Text="Some Text.."/>
<DatePicker/>
</StackPanel>
</Popup>
Here's the code of BooleanOrConverter:
public class BooleanOrConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
foreach (object booleanValue in values)
{
if (booleanValue is bool == false)
{
throw new ApplicationException("BooleanOrConverter only accepts boolean as datatype");
}
if ((bool)booleanValue == true)
{
return true;
}
}
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
and its placed into PopupTest.InfoPanels.Windows namespace
when I run this, I'm getting following exception:
Cannot find resource named 'BooleanOrConverter'. Resource names are case sensitive.
What should I change for this to work?
It sounds like your Multibinding doesn't know where to look for the converter. Have you defined the converter as a staticresource? You can either specify the converter in the control's resources or in the included ResourceDictionary. Add a reference to the converter's namespace and then define a ResourceKey for it. Something like:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:MyConverters">
<UserControl.Resources>
<converters:BooleanOrConverter x:Key="BoolOrConverter"/>
</UserControl.Resources>
... // use converter as you were before
</UserControl>