How to make Converters with bindable ConverterParameters? - wpf

Most converters take no parameters, or one fixed parameter, and are easy to bind to:
<local:MyConverter x:Key="MyConverterInstance" />
<TextBox Text="{Binding Path=MyTime,
Converter={StaticResource MyConverterInstance},
ConverterParameter='yyyy/MM/dd'}" />
But if I want that format to be a dynamic property that the user can change, I can't do something like this, right?:
<TextBox Text="{Binding Path=MyTime,
Converter={StaticResource MyConverterInstance},
ConverterParameter={Binding Path=UserFormat}}" />
So my only option is to define a DependencyProperty on MyConverter for binding. But my converter definition is a StaticResource. I can't go
<local:MyConverter x:Key="MyConverterInstance"
Format="{Binding Path=UserFormat}"/>
because there's no DataContext on StaticResources. How can I set this up?

You cannot bind to a converterparameter but you can use Multibinding instead.
For example: http://www.switchonthecode.com/tutorials/wpf-tutorial-using-multibindings
or How to simply bind this to ConverterParameter?
(Alain) So just to translate that linked answer into something that matches this question:
<TextBlock>
<TextBlock.Resources>
<local:MyConverter x:Key="MyConverterInstance" />
</TextBlock.Resources>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource converter}">
<Binding Path="MyTime" />
<Binding Path="UserFormat" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
public class MyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
DateTime time = (DateTime)values[0];
string format = values[1].ToString();
return time.ToString(format);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

Related

PropertyChangedTrigger For Multiple Properties?

Is there a way to create PropertyChangedTrigger for multiple properties that all share the same conditions, for the sake of redundancy, without specifying the conditions repeatedly for each PropertyChangedTrigger?
For example, when PropertyA, PropertyB and PropertyC change, I want a command to execute in my ViewModel.
I was thinking of something like extending the PropertyChangedClass and adding a dependency property of a an observable collection of Bindings. But it turned out I'm not very knowledgeable in how Bindings are monitored.
Then I saw some of old code and saw Multibinding. I thought it could work. And it did with a MultiValueConverter that just increments a static field. I'm not sure if this is the best solution but this worked.
First the MultiValueConverter:
public class IncrementOnPropertyChangedMultiConverter:IMultiValueConverter
{
static uint _counter=0;
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
_counter = _counter < uint.MaxValue ? _counter + 1 : 0;
return _counter;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
Then in XAML you just add a MultiBinding instead of Binding in the PropertyChangedTrigger.
<i:Interaction.Triggers>
<ei:PropertyChangedTrigger>
<ei:PropertyChangedTrigger.Binding>
<MultiBinding Converter="{StaticResource IncrementOnPropertyChangedMultiConverter}">
<Binding Path="Property1" />
<Binding Path="Property2" />
<Binding Path="PropertyN" />
</MultiBinding>
</ei:PropertyChangedTrigger.Binding>
<i:Interaction.Behaviors>
<ei:ConditionBehavior>
<ei:ConditionalExpression>
<ei:ComparisonCondition LeftOperand="{Binding Property1}"
Operator="Equal"
RightOperand="{Binding Property2}" />
</ei:ConditionalExpression>
</ei:ConditionBehavior>
</i:Interaction.Behaviors>
<ei:ChangePropertyAction PropertyName="Background"
Value="Transparent" />
</ei:PropertyChangedTrigger>
<i:Interaction.Triggers>

Adding two Binding in WPF

I have been playing with wpf lately and got stuck at a point where I would like to use two, binding path values sum as the binding of a third control. I tried using value converter but to no success. Would appreciate if any one can guide me to the right path.
Here is what I have so far:
I have two encoders like
<my3:NVAngle Canvas.Left="400" Canvas.Top="610" BindableValueExtended="120" BindableValueRetracted="0" AnimationDuration="2" x:Name="nvAArm3Rotate" BindableValue="0" InvertExtendedTag="False" InvertRetractedTag="False" />
<my3:NVAngle Canvas.Left="400" Canvas.Top="610" BindableValueExtended="50" BindableValueRetracted="0" AnimationDuration="2" x:Name="nvAArm2Rotate" BindableValue="0" InvertExtendedTag="False" InvertRetractedTag="False" />
and I would like to rotate an image with an angle which is the sum of above two angle encoders. Something like:
<Image Canvas.Left="100" Canvas.Top="200" Source="/Images/ExtraInter.png" Stretch="Fill" Width="117" Height="15" RenderTransformOrigin="0.95, 0.5">
<Image.RenderTransform>
<RotateTransform>
<RotateTransform.Angle>
<MultiBinding Converter="{StaticResource BundleArm3Full}">
<Binding ElementName="nvABundleArm2Rotate" Path="BindableValue" />
<Binding ElementName="nvABundleArm3Rotate" Path="BindableValue" />
</MultiBinding>
</RotateTransform.Angle>
</RotateTransform>
<!--120-->
</Image.RenderTransform>
</Image>
and my converter is:
public class MultipleBindingAddConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var doubleValues = values.Cast<double>().ToArray();
var resultSum = doubleValues.Sum().ToString();
return resultSum;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I still see no rotation of the Image although I can guarantee that the BindableValues are changing. Would appreciate any help.
Thanks!
You could use Multibinding with a Converter that does the sum.
<MultiBinding Converter="{StaticResource sumConverter}" >
<Binding Path="FirstValue"/>
<Binding Path="SecondValue"/>
</MultiBinding>

how to edit a WPF textbox which uses multibinding and string.format?

I have the following code.
This displays data in following format H:M:S. I would like to edit these values...and wanted to be notified in viewmodel.
How do I achieve that ?
Any help would be appreciated. Thanks
<TextBox DataContext="{Binding UpdateSourceTrigger=PropertyChanged}" >
<TextBox.Text>
<MultiBinding StringFormat=" {0}:{1}:{2}">
<Binding Path="ValueH" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" />
<Binding Path="ValueM" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" />
<Binding Path="ValueS" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" />
</MultiBinding>
</TextBox.Text>
</TextBox>
StringFormat binding is oneway
What you will need to do is write your own multivalue converter that implements the ConvertBack method as well.
A very simplistic converter would be something like below. You will need to add error checking and there is no doubtly a better way to convert back (possibly with a regex). Plus I'm not sure that I got the DateTime bit right but it gives you a starting point.
public class TimeConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return string.Format("{0}:{1}:{2}",values[0],values[1],values[2]); }
public object[] ConvertBack(object value, Type[] targetTypes, object parameter,
System.Globalization.CultureInfo culture)
{
var date=DateTime.Parse((string)value);
return new object[] { date.Hours,date.Minutes,date.Seconds };
}
}

Switching Binding Sources using a MultiBinding

I have a DataBinding with a MultiBinding of two ObservableCollections, and i want to switch between them on a condition with a MultiConverter.
So the converter gives the right collection, but the binding doesn't seem to be updated.
Any Ideas??
Greets,
Jürgen
This is the converter you need:
public class SwitchCollectionsConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool b = (bool)values[2];
if (b)
return values[0];
else
return values[1];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
registering the converter:
<local:SwitchCollectionsConverter x:Key="TheConverter" />
usage of the binding:
<ItemsControl>
<ItemsControl.ItemsSource>
<MultiBinding Converter="{StaticResource TheConverter}">
<Binding Path="FirstCollection" />
<Binding Path="SecondCollection" />
<Binding Path="IsFirst" />
</MultiBinding>
</ItemsControl.ItemsSource>
</ItemsControl>
under the assumption that you have a FirstCollection, a SecondCollection, and an IsFirst Properties in the DataContext
Do you need the view to update the source lists?
If so, your binding should be in TwoWay mode:
<TextBox Text="{Binding Source, Mode="TwoWay"}" />

Can't access datacontext in multivalueconverter

I have a usercontrol which I need to set a specific DataContext on. The usercontrol uses multivalueconverters. However, the binding in multivalueconverter fails to use the datacontext. It works fine for regular value converters. Anyone know what is going on? To clarify, below code is not similar to my real code, it's just a sample reproducing my issue. FOr example, my multivalueconverters take more than one argument.
XAML:
<Window x:Class="MultConvTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:MultConvTest="clr-namespace:MultConvTest"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<MultConvTest:MyValueConverter x:Key="MyValueConverter" />
<MultConvTest:MyMultiConverter x:Key="MyMultiConverter" />
</Window.Resources>
<StackPanel>
<!--This works, output is Formatted: 7-->
<TextBlock
DataContext="{Binding Path=Data}"
Text="{Binding Path=Length, Converter={StaticResource MyValueConverter}}" />
<!--This works, output is Formatted: 7-->
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource MyMultiConverter}">
<Binding Path="Data.Length" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<!--This fails, the converter never access the DataContext of the textbox-->
<TextBlock
DataContext="{Binding Path=Data}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource MyMultiConverter}">
<Binding Path="Length" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</Window>
Code behind:
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;
namespace MultConvTest
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = this;
}
public string Data
{
get { return "My Text"; }
}
}
public class MyValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int number = (int)value;
return string.Format("Formatted: {0}", number);
}
public object ConvertBack(object value, Type targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class MyMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// This will fail when DataContext is set. values[0] is of type Ms.Internal.NamedObject
// with value DependencyProperty.UnsetValue.
int number = (int)values[0];
return string.Format("Formatted: {0}", number);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
You are not going to like this ;-)
Simply put the line DataContext=this before InitializeComponent()
It seems the MultiConverter for the third textbox is triggered before the global DataContext is set, thus in effect producing a null reference error...

Resources