Multiple Converter based on same property updating undelrying value - c#

In the code below I'm trying to display "RateValue" as both a decimal and a percentage using a converter.
The below GridColumn code is within a ComboBoxEdit popout template.
What I'm seeing is that when all GridColumns are added the underlying "RateValue" ends up being the same in both cases. However when I only have one or the other they are showing the right values.
So having both appears to be changing the underlying source value.
Am I missing something obvious here?
Thanks
<dxg:GridColumn MinWidth="80" Header="Rate (%)">
<dxg:GridColumn.Binding>
<Binding Path="RateValue" Converter="{StaticResource DecimalToFourDecimalPlacesPercentageConverter}" Mode="OneWay" UpdateSourceTrigger="PropertyChanged"/>
</dxg:GridColumn.Binding>
</dxg:GridColumn>
<dxg:GridColumn Header="Rate (Decimal)">
<dxg:GridColumn.Binding>
<Binding Path="RateValue" Converter="{StaticResource DecimalToFourDecimalPlacesConverter}" Mode="OneWay" UpdateSourceTrigger="PropertyChanged"/>
</dxg:GridColumn.Binding>
</dxg:GridColumn>
<converters1:NumericToStringConverter x:Key="DecimalToFourDecimalPlacesPercentageConverter" Format="0:N4" Multiplier="100"/>
<converters1:NumericToStringConverter x:Key="DecimalToFourDecimalPlacesConverter" Format="0:N4" Multiplier="1"/>
public class NumericToStringConverter : IValueConverter
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(NumericToStringConverter));
public string Format { get; set; }
public int Multiplier { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is decimal)) return Binding.DoNothing;
try
{
var v = (decimal?) value;
return string.Format("{" + Format + "}", Multiplier*v);
}
catch (FormatException ex)
{
Logger.Error(string.Format("Failed to format '{0}'", value), ex);
}
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

I'm not familiar with DevExpress's WPF control, but assume they work like official one, there's no need for converter, StringFormat should be enough.
<dxg:GridColumn MinWidth="80" Header="Rate (%)">
<dxg:GridColumn.Binding>
<Binding Path="RateValue" StringFormat="P4"/>
</dxg:GridColumn.Binding>
</dxg:GridColumn>
<dxg:GridColumn Header="Rate (Decimal)">
<dxg:GridColumn.Binding>
<Binding Path="RateValue" StringFormat="N4"/>
</dxg:GridColumn.Binding>
</dxg:GridColumn>
If that doesn't work, change your converter to
public class NumericToStringConverter : IValueConverter
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(NumericToStringConverter));
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
return string.Format(parameter.ToString(), value);
}
catch (FormatException ex)
{
Logger.Error($"Failed to format {value}", ex);
}
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
then change other parts to
<dxg:GridColumn MinWidth="80" Header="Rate (%)">
<dxg:GridColumn.Binding>
<Binding Path="RateValue" Converter="{StaticResource MyDecimalToStringConverter}"
ConverterParameter="P4"/>
</dxg:GridColumn.Binding>
</dxg:GridColumn>
<dxg:GridColumn Header="Rate (Decimal)">
<dxg:GridColumn.Binding>
<Binding Path="RateValue" Converter="{StaticResource MyDecimalToStringConverter}"
ConverterParameter="N4"/>
</dxg:GridColumn.Binding>
</dxg:GridColumn>
<converters1:NumericToStringConverter x:Key="MyDecimalToStringConverter"/>

Related

WPF Binding to dictionary with x:Name as a key

Please correct my question if it's not clear. What I'm looking for is..
Here's a sample binding for a dictionary... it works:
<TextBlock Text="{Binding Path=MyDictionary[ThisIsKeyInMyDict]}" />
I'm looking for:
<TextBlock x:Name="Id" Text="{Binding Path=MyDictionary[x:Name]}" />
You see? I want to look in dictionary for a key, the same as "Name" of this control.
Thanks for help!
You can use a MultiBinding for it:
<Window.Resources>
<local:DictValueConverter x:Key="dictValCnv"/>
</Window.Resources>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource dictValCnv}">
<Binding Path="MyDictionary"/>
<Binding RelativeSource="{RelativeSource Self}" Path="Name"/>
</MultiBinding>
</TextBlock.Text>
using System;
using System.Globalization;
using System.Linq;
using System.Windows.Data;
public class DictValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values==null || values.Length<2 )
{
return false;
}
var dict = values[0] as IDictionary;
if(dict.Contains(values[1]))
{
return dict[values[1]];
}
return "KeyNotFound";
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Binding error with Converter Parameter, when uses an array of system:Object, works with array of system:Brush

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

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.

XAML can't find the converter class

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>

Categories