Pass the value of one control to a Converter to set the width on another control

35,607

Solution 1

you're supposed to use the other control as the source, not the parameter. The parameter has to be a constant and in your case can be -5.

I'm not near VS at the moment so the syntax maybe inaccurate, however, it is something like:

Width="{Binding ElementName=LayoutRoot, Path=ActualWidth,
Converter={StaticResource PositionConverter}, ConverterParameter=-5}"

(The converter will receive -5 as a string and will have to convert it into a number before using it.)

From my experience it is better to use the OnXXXChanged callback of DependecyProperty XXX, and not bind controls within the same window/root control one to another. One of the reasons for this is that you may want to bind them to an external element later on.

Or alternatively, use multibinding:

<TextBlock>
    <TextBlock.Width>
        <MultiBinding Converter="{StaticResource yourConverter}">
            <MultiBinding.Bindings>
                <Binding /> <!-- Bind to parameter 1 here -->
                <Binding /> <!-- Bind to parameter 2 here -->
          </MultiBinding.Bindings>
        </MultiBinding>
    </TextBlock.Width>
</TextBlock>

and and a converter which converts the two parameters to the value you want.

Solution 2

Although I suspect there may be a better way to solve your problem, I think I have an answer for what you want to do. ( You didn't mention what type your container is. A StackPanel for instance takes care of the width calculation for you. See TextBox#2 below)

First the XAML

<Window x:Class="WpfApplication1.Window2" ...
    xmlns:local="clr-namespace:WpfApplication1"
    Title="Window2" Height="300" Width="300">
    <Window.Resources>
        <local:WidthSansMarginConverter x:Key="widthConverter" />
    </Window.Resources>
    <Grid>
        <StackPanel x:Name="stack">
            <TextBlock x:Name="txtStatusMessages" 
                    Width="{Binding ElementName=stack,Path=ActualWidth, 
                        Converter={StaticResource widthConverter}}"
                    TextWrapping="WrapWithOverflow" 
                    Background="Aquamarine" 
                    Margin="5,5,5,5">
                This is a message
            </TextBlock>
            <TextBlock x:Name="txtWhatsWrongWithThis" 
                    TextWrapping="WrapWithOverflow" 
                    Background="Aquamarine" 
                    Margin="5,5,5,5">
                This is another message
            </TextBlock>
        </StackPanel>
    </Grid>
</Window>

Next the Converter. We have a problem here.. since the ConverterParameter for the Convert methods cannot be a dynamic value for some reason. So we sneak in the Textbox Margin via a public property of the Converter that we set in Window's ctor. WidthSansMarginConverter.cs

public class WidthSansMarginConverter : IValueConverter
    {
        private Thickness m_Margin = new Thickness(0.0);

        public Thickness Margin
        {
            get { return m_Margin; }
            set { m_Margin = value; }
        }
        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (targetType != typeof(double)) { return null; }

            double dParentWidth = Double.Parse(value.ToString());
            double dAdjustedWidth = dParentWidth-m_Margin.Left-m_Margin.Right;
            return (dAdjustedWidth < 0 ? 0 : dAdjustedWidth);
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

Window2.xaml.cs

        public Window2()
        {
            InitializeComponent();

            WidthSansMarginConverter obConverter = this.FindResource("widthConverter") as WidthSansMarginConverter;
            obConverter.Margin = txtStatusMessages.Margin;
        }

HTH. Thanks for the exercise :)

Share:
35,607
Doug
Author by

Doug

Updated on March 24, 2020

Comments

  • Doug
    Doug about 4 years

    I want to set the width of a TextBlock based on the width of its container, minus the margins set on the TextBlock.

    Here is my code:

    <TextBlock x:Name="txtStatusMessages" 
               Width="{Binding ElementName=LayoutRoot,Path=ActualWidth }"
                       TextWrapping="WrapWithOverflow" 
               Foreground="White" 
               Margin="5,5,5,5">This is a message
    </TextBlock>
    

    And that works great except for the fact that the TextBlock is 10 units too big due to the Left and Right Margins bbeing set to 5.

    OK, so I thought... Let's use a Converter. But I don't know how to pass the ActualWidth of my container control (SEE ABOVE: LayoutRoot).

    I know how to use converters, and even converters with parameters, just not a parameter like... Binding ElementName=LayoutRoot,Path=ActualWidth

    For example, I can't make this work:

    Width="{Binding Converter={StaticResource PositionConverter},  
           ConverterParameter={Binding ElementName=LayoutRoot,Path=ActualWidth }}"
    

    I hope I made this clear enough and hope that you can help because Google is no help for me tonight.

  • Doug
    Doug over 15 years
    Thanks Danny. This worked just fine. But I was hoping to not have to hard code the Parameter value. I didn't know that the Parameter needed to be a constant. Thanks!
  • Doug
    Doug over 15 years
    Thnaks Gishu! Your sample helped me learn more about converters. Altough your answer worked, I ended marking danny's answer as THE answer because it was the solution that I used. I was tired and lazy and his, altough less flexible, was less code. Thanks!
  • JWL
    JWL over 10 years
    How I've been haunted by the many solutions that failed to work, til I found this one! Thanks
  • Jacob
    Jacob over 9 years
    Thank you very much, so helpful !