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>
Related
I have a multilanguage application which contains two dictionary en.xaml and it.xaml, suppose that I have the following situation:
I need to display a default text in a TextBlock such as: Contact saved 0 in memory using a DynamicResource key that contains the text above.
Unfortunately xaml doesn't allow to use DynamicResource in a StringFormat or as FallbackValue so I used this code:
<TextBlock Tag="{DynamicResource defaultContactStr}">
<TextBlock.Text>
<PriorityBinding>
<Binding Path="ContactSaved" />
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
</PriorityBinding>
</TextBlock.Text>
this will display only 0 which is the default value of the proeprty ContactSaved, but I need to display: Contact saved 0 in memory, or if the value change: Contact saved 5 in memory etc...
how can I manage this situation?
Using a StringFormat works only if the format (the translation in your case) is known at compile-time:
<TextBlock>
<TextBlock.Text>
<Binding Path="ContactSaved" StringFormat="{}Contact saved {0} in memory" />
</TextBlock.Text>
</TextBlock>
Othwerwise you may handle this using a converter and a MultiBinding:
public class CustomConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string resource = values[0] as string;
string contactSaved = values[1].ToString();
return string.Format(resource, contactSaved);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
XAML:
<TextBlock Tag="{DynamicResource defaultContactStr}">
<TextBlock.Text>
<MultiBinding>
<MultiBinding.Converter>
<local:CustomConverter />
</MultiBinding.Converter>
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
<Binding Path="ContactSaved" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
en.xaml:
<system:String x:Key="defaultContactStr">Contact saved {0} in memory</system:String>
Remember that XAML is nothing but a markup language.
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>
I'm attempting to use Multibinding in a ControlTemplate to draw some Lines. My XAML:
<Line X1="{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}}" Y1="0"
X2="{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}}" Stroke="Red" StrokeThickness="1">
<Line.Y2>
<MultiBinding Converter="{StaticResource XAMLResourceAddConverter}">
<Binding Source="-15"></Binding>
<Binding Path="ActualHeight" RelativeSource="{RelativeSource TemplatedParent}"></Binding>
</MultiBinding>
</Line.Y2>
</Line>
And my convertor:
public class AddConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
int result =
Int32.Parse(values[0].ToString()) + Int32.Parse(values[1].ToString());
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("Cannot convert back");
}
}
The Line is not actually drawing. Stepping through the Convertor, I found that the value[1] (which should be the ActualHeight) is always coming through as 0.0. How do I fix this?
You can use FindAncestor in this case as TemplatedParent is not being resolved in binding
<Binding Path="ActualHeight" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type MyType}}"></Binding>
secondly as you are using a hardcoded value -15 for first binding in the multibinding, perhaps you can bind the Y2 inline using converter parameter
so if you can change the converter XAMLResourceAddConverter to a IValueConveter then perhaps you can use it as
Y2="{Binding ActualHeight, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource XAMLResourceAddConverter}, ConverterParameter=-15}"
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();
}
}
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 };
}
}