3

I would like to bind a scaleTransform to a converter passing the ActualWidth or ActualHeight.

Here what I want to do :

<Canvas x:Name="Canevas">
   <Canvas.RenderTransform>
      <TransformGroup>
         <ScaleTransform 
              ScaleX="{Binding RelativeSource={RelativeSource FindAncestor, 
                               AncestorType={x:Type UIElement}}, 
                               Path=ActualWidth, Mode=OneWay, 
                               Converter={StaticResource ScaleConverter}, 
                               ConverterParameter={Binding Path=ActualWidth}"

              ScaleY="{Binding RelativeSource={RelativeSource FindAncestor,
                               AncestorType={x:Type UIElement}}, 
                               Path=ActualHeight, Mode=OneWay, 
                               Converter={StaticResource ScaleConverter},  
                               ConverterParameter={Binding Path=ActualHeight}}"
         />
      </TransformGroup>
   </Canvas.RenderTransform>

   <Ellipse Canvas.Left="47" Canvas.Top="48" Height="155" 
            Name="ellipse1" Stroke="Black" Width="174" Fill="#FF00C6C3" />

The problem is this don't compile :

ConverterParameter={Binding Path=ActualHeight}

So I want to know how to move these properties as parameters for my converter? Is it possible to resolve in full Xaml ?

Many thanks for your Help !

Converter source code :

public class ScaleConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter,
                                        System.Globalization.CultureInfo culture)
    {
        double v = (double)value;
        var actualSize = (double)parameter; //ActualWidth, ActualHeight

        var vScale = v * (1 + (v / actualSize));

        return vScale;
    }

    public object ConvertBack(object value, Type targetType, object parameter, 
                                        System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
Indy9000
  • 8,651
  • 2
  • 32
  • 37
  • 1
    Could you show the code of your converter ? Why do you need the ConverterParameter ? Perhaps you shoud use a MultiBinding with a IMultiValueConverter instead... – Thomas Levesque Jun 27 '09 at 19:01
  • I added Converter source code –  Jun 27 '09 at 20:15
  • I'm not sure I understand what you're trying to do... the converter is supposed to return a scale factor, but according to your formula it will return a width (or height). What exactly are you trying to achieve ? – Thomas Levesque Jun 27 '09 at 20:29

3 Answers3

4

I think you could probably figure out a way to make this work, by making ScaleConverter descend from DependencyObject, giving it an ActualSize dependency property, putting two instances of it in your resources instead of one, and doing a one-way-to-source binding to bind one instance's ActualSize to the canvas's ActualWidth and the other to its ActualHeight (so the instance's ActualSize is always kept in sync with the canvas's width/height as the canvas resizes).

However, it kind of looks like you may actually want something simple: to stretch the canvas to fill its parent. If that's the case, look at Viewbox:

<Viewbox Stretch="Fill">
    <Canvas Width="221" Height="203">
        <Ellipse Canvas.Left="47" Canvas.Top="48" Height="155"
                 Stroke="Black" Width="174" Fill="#FF00C6C3" />
    </Canvas>
</Viewbox>

I included Stretch="Fill" to stretch both X and Y without preserving aspect ratio, since that looked like what you were after. You also need to assign a Width and Height to the Canvas to get that to work, since Canvas doesn't have a natural size by default. The above snippet sets a Width and Height that would put your ellipse at the lower-right corner of the stretched Viewbox; you might want something different than that.

One possible downside to a Viewbox would be that the stroke thicknesses are scaled too, so if you made the window short and wide, the horizontal strokes would be skinny and the vertical strokes would be thick. If that's a problem, you may need to stick with databinding.

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Joe White
  • 94,807
  • 60
  • 220
  • 330
3

EDIT: All my answer does is refactor your code so that it compiles. If your solution doesn't work then neither will this.

Instead of using an IValueConverter, use IMultiValueConverter and MultiBinding. Instead of passing that extra value as a ConverterParameter, pass it as an extra binding.

    <Canvas x:Name="Canvas">
        <Canvas.RenderTransform>
            <TransformGroup>
                <!--<ScaleTransform ScaleX="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UIElement}}, Path=ActualWidth, Mode=OneWay, Converter={StaticResource ScaleConverter}, ConverterParameter={Binding Path=ActualWidth}"
                           ScaleY="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UIElement}}, Path=ActualHeight, Mode=OneWay, Converter={StaticResource ScaleConverter}, ConverterParameter={Binding Path=ActualHeight}}" />-->
                <ScaleTransform>
                    <ScaleTransform.ScaleX>
                        <MultiBinding Converter="{StaticResource ScaleConverter}">
                            <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type UIElement}}" Path="ActualWidth" Mode="OneWay"/>
                            <Binding Path="ActualWidth"/>
                        </MultiBinding>
                    </ScaleTransform.ScaleX>
                    <ScaleTransform.ScaleY>
                        <MultiBinding Converter="{StaticResource ScaleConverter}">
                            <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type UIElement}}" Path="ActualHeight" Mode="OneWay"/>
                            <Binding Path="ActualHeight"/>
                        </MultiBinding>
                    </ScaleTransform.ScaleY>
                </ScaleTransform>
            </TransformGroup>
        </Canvas.RenderTransform>
        <Ellipse Canvas.Left="47" Canvas.Top="48" Height="155" Name="ellipse1" Stroke="Black" Width="174" Fill="#FF00C6C3" />
    </Canvas>

And your converter:

public class ScaleConverter : IMultiValueConverter
{

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double v = (double)values[0];
        var actualSize = (double)values[1]; //ActualWidth, ActualHeight

        var vScale = v * (1 + (v / actualSize));

        return vScale;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Paymahn Moghadasian
  • 9,301
  • 13
  • 56
  • 94
0

Unfortunately, I do not think that this is possible. Since Binding.ConverterParameter isn't a DependencyProperty, you can't use bindings to populate its value. I've tried to do this before (using a binding for the ConverterParameter) and ran into the same problems that you did.

Andy
  • 30,088
  • 6
  • 78
  • 89