PropertyChangedTrigger For Multiple Properties? - wpf

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>

Related

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>

DataTrigger for ItemsControl's ItemTemplate triggering on the last item in the list

I Have an ItemsControl with a defined ItemTemplate.
The ItemTemplate contains an image.
The Image has a style trigger to set the image.
What binding can I use to trigger a different image to be displayed if the item is the last in the list?
While the simplest approach for this is still probably to add an indicator field at the VM / collection level and refer to that property in the Trigger, there are some alternatives too.
One such could be to use the AlternationCount of the ItemsControl. This approach will make sure you're last item's special style is retained when changes are made to the source collection like when adding/removing/ sorting and sorts.
In the ItemsControl set AlternationCount="{Binding Items.Count}" (Items is the collection being bound to)
As for the trigger in ItemTemplate to detect the last item, it's a bit more complicated.
xaml:
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource MyConverter}">
<Binding Path="(ItemsControl.AlternationIndex)"
RelativeSource="{RelativeSource FindAncestor,
AncestorType=ContentPresenter}" />
<Binding Path="ItemsSource.Count"
RelativeSource="{RelativeSource FindAncestor,
AncestorType=ItemsControl}" />
</MultiBinding>
</DataTrigger.Binding>
<!-- Change the following setter to what you need for new image -->
<Setter Property="Foreground"
Value="Tomato" />
</DataTrigger>
and the converter used:
internal class MyConverter : IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
bool result;
try {
result = System.Convert.ToInt32(values[0]) == System.Convert.ToInt32(values[1]) - 1;
} catch (Exception) {
result = false;
}
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}

How to make Converters with bindable ConverterParameters?

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();
}
}

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"}" />

Resources