WPF-передать значение одного элемента управления в преобразователь, чтобы задать ширину другого элемента управления

Я хочу установить ширину TextBlock на основе ширины его контейнера, минус поля, установленные на TextBlock.

вот мой код

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

и это отлично работает, за исключением того, что TextBlock на 10 единиц слишком велик из-за левого и Правого полей, установленных на 5.

хорошо, так я и думал... Давайте используем конвертер. Но я не знаю, как передать фактическую ширину моего контейнерного контроля (см. выше: LayoutRoot).

Я знаю как использовать преобразователи, и даже преобразователи с параметрами, просто не параметр, как... Привязка ElementName=LayoutRoot, Path=ActualWidth

например, я не могу заставить это работать...

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

Я надеюсь, что я сделал это достаточно ясно и надеюсь, что вы можете помочь, потому что Google не поможет мне сегодня вечером.

ТИА!

Даг

5 ответов


вы должны использовать другой элемент управления в качестве источника, а не параметр. Параметр должен быть постоянным и в вашем случае может быть -5.

Я не рядом с VS на данный момент, поэтому синтаксис может быть неточным, однако, это что-то вроде:

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

(конвертер получит -5 в виде строки и должен будет преобразовать его в число перед его использованием.)

из моего опыта лучше использовать обратный вызов OnXXXChanged DependecyProperty XXX, и не привязывать элементы управления в одном окне / корневом элементе управления друг к другу. Одна из причин этого заключается в том, что вы можете привязать их к внешнему элементу позже.

или, альтернативно, используйте 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>

и и преобразователь, который преобразует два параметра в нужное значение.


да..multi binding работает для меня.. на самом деле я пытался отправить элемент как convereterparameter, но его не принимают. вот почему я передал элемент в качестве значения классу converter.

ниже мой пример..

<ListView ... >
<ListView.View>
<GridView>
    <GridViewColumn Header="xyz" >

        <GridViewColumn.Width>
            <MultiBinding Converter="{StaticResource GetWidthfromParentControl}">
                <MultiBinding.Bindings>
                    <Binding ElementName="lstNetwork" Path="ActualWidth"/>
                    <Binding ElementName="MyGridView"/>
                </MultiBinding.Bindings>
            </MultiBinding>
        </GridViewColumn.Width>
    ....
    </GridViewColumn>
    <GridViewColumn ...>
    ....
    </GridViewColumn>
</GridView>
</ListView.View>
</ListView>

в изменении размера окна мой первый gridviewcolumn должен быть изменен, а не два других gridviewcolumns.. я передал Actualwidth объекта listview, а также total GridView в качестве элемента.. если вы идете конвертер код...

class GetWidthfromParentControl : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        GridView view = values[1] as GridView;
        GridViewColumnCollection collc = view.Columns;
        double actualWidths = collc[1].ActualWidth + collc[2].ActualWidth;
        return ((double)values[0] - actualWidths );
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }

    #endregion
}

это работает для меня... :)


сначала 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>

далее преобразователь. У нас тут проблема.. с ConverterParameter для методов преобразования не может быть динамическим значением по какой-то причине. Поэтому мы крадемся. в поле текстового поля через публичное свойство конвертера, которое мы устанавливаем в 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. Спасибо за упражнение:)


Если текстовое поле является прямым дочерним элементом LayoutRoot, просто установите следующее свойство в текстовом поле

HorizontalAlignment="Stretch"

согласно http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/7298ceb5-bf56-47aa-a161-5dd99189408b, можно добавить свойство зависимостей в пользовательский преобразователь, если преобразователь является производным от DependencyObject.

в этом случае вы даже можете использовать привязку данных для передачи значений тем свойствам, где вы определяете конвертер (в словаре ресурсов) в XAML.