HorizontalAlignment=Stretch, MaxWidth и Left выровнены одновременно?

Кажется, это должно быть легко, но я в тупике. В WPF мне бы хотелось, чтобы TextBox растягивался до ширины родительского элемента, но только до максимальной ширины. Проблема в том, что я хочу, чтобы это оставалось обоснованным внутри его родителя. Чтобы растянуть его, вы должны использовать HorizontalAlignment="Stretch", но тогда результат центрируется. Я экспериментировал с HorizontalContentAlignment, но он, похоже, ничего не делает.

Как заставить это синее текстовое поле расти с размером окна, иметь максимальную ширину 200 пикселей и быть выровненным по левому краю?

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <StackPanel>  
    <TextBox Background="Azure" Text="Hello" HorizontalAlignment="Stretch" MaxWidth="200" />
  </StackPanel>
</Page>

В чем подвох?

9 ответов

Решение

Вы можете установить HorizontalAlignment влево, установите ваш MaxWidth а затем связать Width к ActualWidth родительского элемента:

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <StackPanel Name="Container">   
    <TextBox Background="Azure" 
    Width="{Binding ElementName=Container,Path=ActualWidth}"
    Text="Hello" HorizontalAlignment="Left" MaxWidth="200" />
  </StackPanel>
</Page>
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" MaxWidth="200"/>
    </Grid.ColumnDefinitions>

    <TextBox Background="Azure" Text="Hello" />
</Grid>

Оба ответа помогли решить поставленную мною проблему. Спасибо!

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

Техника Нира работала хорошо и не имела проблем с ScrollViewer, хотя есть одна небольшая вещь, на которую следует обратить внимание. Вы хотите убедиться, что правое и левое поля в TextBox установлены в 0, иначе они будут мешать. Я также изменил привязку, чтобы использовать ViewportWidth вместо ActualWidth, чтобы избежать проблем при появлении вертикальной полосы прокрутки.

Вы можете использовать это для ширины вашего DataTemplate:

Width="{Binding ActualWidth,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollContentPresenter}}}"

Убедитесь, что ваш корень DataTemplate имеет Margin="0" (вы можете использовать некоторую панель в качестве корня и установить Margin для дочерних элементов этого корня)

Может быть, я все еще могу помочь кому-то, кто сталкивается с этим вопросом, потому что это очень старая проблема.

Я также нуждался в этом и написал поведение, чтобы позаботиться об этом. Итак, вот поведение:

public class StretchMaxWidthBehavior : Behavior<FrameworkElement>
{        
    protected override void OnAttached()
    {
        base.OnAttached();
        ((FrameworkElement)this.AssociatedObject.Parent).SizeChanged += this.OnSizeChanged;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        ((FrameworkElement)this.AssociatedObject.Parent).SizeChanged -= this.OnSizeChanged;
    }

    private void OnSizeChanged(object sender, SizeChangedEventArgs e)
    {
        this.SetAlignments();
    }

    private void SetAlignments()
    {
        var slot = LayoutInformation.GetLayoutSlot(this.AssociatedObject);
        var newWidth = slot.Width;
        var newHeight = slot.Height;

        if (!double.IsInfinity(this.AssociatedObject.MaxWidth))
        {
            if (this.AssociatedObject.MaxWidth < newWidth)
            {
                this.AssociatedObject.HorizontalAlignment = HorizontalAlignment.Left;
                this.AssociatedObject.Width = this.AssociatedObject.MaxWidth;
            }
            else
            {
                this.AssociatedObject.HorizontalAlignment = HorizontalAlignment.Stretch;
                this.AssociatedObject.Width = double.NaN;
            }
        }

        if (!double.IsInfinity(this.AssociatedObject.MaxHeight))
        {
            if (this.AssociatedObject.MaxHeight < newHeight)
            {
                this.AssociatedObject.VerticalAlignment = VerticalAlignment.Top;
                this.AssociatedObject.Height = this.AssociatedObject.MaxHeight;
            }
            else
            {
                this.AssociatedObject.VerticalAlignment = VerticalAlignment.Stretch;
                this.AssociatedObject.Height = double.NaN;
            }
        }
    }
}

Тогда вы можете использовать это так:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>

    <TextBlock Grid.Column="0" Text="Label" />
    <TextBox Grid.Column="1" MaxWidth="600">
          <i:Interaction.Behaviors>                       
               <cbh:StretchMaxWidthBehavior/>
          </i:Interaction.Behaviors>
    </TextBox>
</Grid>

И, наконец, забыть использовать System.Windows.Interactivity Пространство имен, чтобы использовать поведение.

Функционально аналогичен принятому ответу, но не требует указания родительского элемента:

<TextBox
    Width="{Binding ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type FrameworkElement}}}"
    MaxWidth="500"
    HorizontalAlignment="Left" />

Эти ответы не сработали для меня, потому что мне нужно былоTextBoxрастягиваться (т.е. потреблять все доступное пространство), пока не достигнет своегоMaxWidthи, если доступно больше места, выровняйте по правому краю.

Я создал этот простой элемент управления, который работает в соответствии с ответом Y C, но не требуетSystem.Windows.Interactivity:

      public class StretchAlignmentPanel : ContentControl
{
    public StretchAlignmentPanel()
    {
        this.SizeChanged += StretchAlignmentPanel_SizeChanged;
    }

    public static readonly DependencyProperty HorizontalFallbackAlignmentProperty = DependencyProperty.Register(
        nameof(HorizontalFallbackAlignment), typeof(HorizontalAlignment), typeof(StretchAlignmentPanel), new PropertyMetadata(HorizontalAlignment.Stretch));

    public HorizontalAlignment HorizontalFallbackAlignment
    {
        get { return (HorizontalAlignment)GetValue(HorizontalFallbackAlignmentProperty); }
        set { SetValue(HorizontalFallbackAlignmentProperty, value); }
    }

    public static readonly DependencyProperty VerticalFallbackAlignmentProperty = DependencyProperty.Register(
        nameof(VerticalFallbackAlignment), typeof(VerticalAlignment), typeof(StretchAlignmentPanel), new PropertyMetadata(VerticalAlignment.Stretch));

    public VerticalAlignment VerticalFallbackAlignment
    {
        get { return (VerticalAlignment)GetValue(VerticalFallbackAlignmentProperty); }
        set { SetValue(VerticalFallbackAlignmentProperty, value); }
    }

    private void StretchAlignmentPanel_SizeChanged(object sender, System.Windows.SizeChangedEventArgs e)
    {
        var fe = this.Content as FrameworkElement;
        if (fe == null) return;
        
        if(e.WidthChanged) applyHorizontalAlignment(fe);
        if(e.HeightChanged) applyVerticalAlignment(fe);
    }

    private void applyHorizontalAlignment(FrameworkElement fe)
    {
        if (HorizontalFallbackAlignment == HorizontalAlignment.Stretch) return;

        if (this.ActualWidth > fe.MaxWidth)
        {
            fe.HorizontalAlignment = HorizontalFallbackAlignment;
            fe.Width = fe.MaxWidth;
        }
        else
        {
            fe.HorizontalAlignment = HorizontalAlignment.Stretch;
            fe.Width = double.NaN;
        }
    }

    private void applyVerticalAlignment(FrameworkElement fe)
    {
        if (VerticalFallbackAlignment == VerticalAlignment.Stretch) return;

        if (this.ActualHeight > fe.MaxHeight)
        {
            fe.VerticalAlignment = VerticalFallbackAlignment;
            fe.Height= fe.MaxHeight;
        }
        else
        {
            fe.VerticalAlignment = VerticalAlignment.Stretch;
            fe.Height= double.NaN;
        }
    }
}

Его можно использовать следующим образом:

      <controls:StretchAlignmentPanel HorizontalFallbackAlignment="Right">
    <TextBox MaxWidth="200" MinWidth="100" Text="Example"/>
</controls:StretchAlignmentPanel>

В моем случае мне пришлось поместить текстовое поле в панель стека, чтобы растянуть текстовое поле с левой стороны. Спасибо за предыдущий пост. Просто для примера я установил цвет фона, чтобы увидеть, что происходит при изменении размера окна.

<StackPanel Name="JustContainer" VerticalAlignment="Center" HorizontalAlignment="Stretch" Background="BlueViolet" >
    <TextBox 
       Name="Input" Text="Hello World" 
       MaxWidth="300"
       HorizontalAlignment="Right"
       Width="{Binding ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type FrameworkElement}}}">
    </TextBox>
</StackPanel>

Я хотел бы использовать SharedGroupSize

<Grid>
    <Grid.ColumnDefinition>
        <ColumnDefinition SharedGroupSize="col1"></ColumnDefinition>  
        <ColumnDefinition SharedGroupSize="col2"></ColumnDefinition>
    </Grid.ColumnDefinition>
    <TextBox Background="Azure" Text="Hello" Grid.Column="1" MaxWidth="200" />
</Grid>
Другие вопросы по тегам