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 единиц, слишком больших из-за того, что значение Left и Right Margins установлено равным 5.

ОК, поэтому я подумал... Позвольте использовать конвертер. Но я не знаю, как передать ActualWidth моего элемента управления контейнером (СМ. ВЫШЕ: LayoutRoot).

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

Например, я не могу сделать эту работу...

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

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

ТИА!

Дуга

Ответ 1

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

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

Ответ 2

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

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

<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>

В окне resize мой первый gridviewcolumn должен быть изменен, а не два других gridviewcolumns. я передал Actualwidth списка, а также общий объект 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
}

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

Ответ 3

Хотя я подозреваю, что может быть лучший способ решить вашу проблему, я думаю, что у меня есть ответ на то, что вы хотите сделать. (Вы не указали, какой тип вашего контейнера. Например, StackPanel выполняет расчет ширины для вас. См. TextBox № 2 ниже)

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

Далее конвертер. У нас здесь проблема. Поскольку то почему-то преобразователь для методов Convert не может быть динамическим значением. Итак, мы крадуемся в поле "Текстовое поле" через общедоступное свойство конвертера, которое мы установили в Window 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;
        }

НТН. Спасибо за упражнение:)

Ответ 4

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

HorizontalAlignment="Stretch"

Ответ 5

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

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