让TextBox拉伸以填充StackPanel中的宽度

时间:2012-06-21 12:13:20

标签: xaml textbox windows-runtime microsoft-metro stackpanel

我的WinRT C#Metro应用中有StackPanel我希望用作标签/值对的容器(TextBlockTextBox),如下所示:

<StackPanel Orientation="Horizontal" Width="400">
    <TextBlock Text="Label" Width="150" />
    <TextBox Text="Value" />
</StackPanel>

现在我想要的是让TextBox自动填充StackPanel的剩余水平空间。这有可能吗? HorizontalAlignment / HorizontalContentAlignment不起作用。

我知道另一种方法是为此定义一个Grid。问题是我有几次构造,并希望在Style定义中使用它。我不想用行和列x次定义Grid的定义......

也许替代方法是定义一个自定义用户控件,但我希望很容易让TextBox伸展。

5 个答案:

答案 0 :(得分:24)

不幸的是,替代方案(DockPanel)不适用于Metro。你可以试试WrapGrid,但我不知道它是否会解决你的问题(我从来没用过它)。

这样做的唯一真正方法是使用您描述的网格:

<Grid Width="400">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <TextBlock Text="Label" Width="150" />
    <TextBox Text="Value" Grid.Column="1" />
</Grid>

答案 1 :(得分:20)

我不确定你将HorizontalAlignment放到哪个元素,但这对我有用:

<StackPanel>
    <TextBlock Text="Title" Style="..."/>
    <TextBlock Margin="0,0,0,10" Text="Description" Style="..."/>
    <TextBox HorizontalAlignment="Stretch" />
    <Button Margin="0" Content="Save"/>
</StackPanel>

这会将TextBox拉伸到StackPanel的整个宽度。

答案 2 :(得分:4)

在查看是否存在WinRT DockPanel时遇到了这个问题。

我最后稍微修改了Silverlight Toolkit附带的DockPanel,它似乎有效。

要使用,请将xmlns:toolkit="using:System.Windows.Controls"添加到顶部,并将上述问题作为方案,我们可以写道:

<toolkit:DockPanel Width="400">
    <TextBlock Text="Label" Width="150" toolkit:DockPanel.Dock="Left" />
    <TextBox Text="Value" />
</toolkit:DockPanel>

DockPanel.cs:

// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

// Modified slightly for Windows Runtime support.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace System.Windows.Controls
{
    public enum Dock
    {
        // Summary:
        //     Specifies that the control should be positioned on the left of the control.
        Left = 0,
        //
        // Summary:
        //     Specifies that the control should be positioned on top of the control.
        Top = 1,
        //
        // Summary:
        //     Specifies that the control should be positioned on the right of the control.
        Right = 2,
        //
        // Summary:
        //     Specifies that the control should be positioned at the bottom of the control.
        Bottom = 3,
    }

    /// <summary>
    /// Arranges child elements around the edges of the panel.  Optionally, 
    /// last added child element can occupy the remaining space.
    /// </summary>
    /// <QualityBand>Stable</QualityBand>
    public class DockPanel : Panel
    {
        /// <summary>
        /// A value indicating whether a dependency property change handler
        /// should ignore the next change notification.  This is used to reset
        /// the value of properties without performing any of the actions in
        /// their change handlers.
        /// </summary>
        private static bool _ignorePropertyChange;

        #region public bool LastChildFill
        /// <summary>
        /// Gets or sets a value indicating whether the last child element
        /// added to a <see cref="T:System.Windows.Controls.DockPanel" />
        /// resizes to fill the remaining space.
        /// </summary>
        /// <value>
        /// True if the last element added resizes to fill the remaining space,
        /// false to indicate the last element does not resize. The default is
        /// true.
        /// </value>
        public bool LastChildFill
        {
            get { return (bool)GetValue(LastChildFillProperty); }
            set { SetValue(LastChildFillProperty, value); }
        }

        /// <summary>
        /// Identifies the
        /// <see cref="P:System.Windows.Controls.DockPanel.LastChildFill" />
        /// dependency property.
        /// </summary>
        public static readonly DependencyProperty LastChildFillProperty =
            DependencyProperty.Register(
                "LastChildFill",
                typeof(bool),
                typeof(DockPanel),
                new PropertyMetadata(true, OnLastChildFillPropertyChanged));

        /// <summary>
        /// LastChildFillProperty property changed handler.
        /// </summary>
        /// <param name="d">DockPanel that changed its LastChildFill.</param>
        /// <param name="e">Event arguments.</param>
        private static void OnLastChildFillPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DockPanel source = d as DockPanel;
            source.InvalidateArrange();
        }
        #endregion public bool LastChildFill

        #region public attached Dock Dock
        /// <summary>
        /// Gets the value of the
        /// <see cref="P:System.Windows.Controls.DockPanel.Dock" /> attached
        /// property for the specified element.
        /// </summary>
        /// <param name="element">
        /// The element from which the property value is read.
        /// </param>
        /// <returns>
        /// The <see cref="P:System.Windows.Controls.DockPanel.Dock" /> property
        /// value for the element.
        /// </returns>
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "DockPanel only has UIElement children")]
        public static Dock GetDock(UIElement element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            return (Dock)element.GetValue(DockProperty);
        }

        /// <summary>
        /// Sets the value of the
        /// <see cref="P:System.Windows.Controls.DockPanel.Dock" /> attached
        /// property for the specified element to the specified dock value.
        /// </summary>
        /// <param name="element">
        /// The element to which the attached property is assigned.
        /// </param>
        /// <param name="dock">
        /// The dock value to assign to the specified element.
        /// </param>
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "DockPanel only has UIElement children")]
        public static void SetDock(UIElement element, Dock dock)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            element.SetValue(DockProperty, dock);
        }

        /// <summary>
        /// Identifies the
        /// <see cref="P:System.Windows.Controls.DockPanel.Dock" />
        /// attached property.
        /// </summary>
        public static readonly DependencyProperty DockProperty =
            DependencyProperty.RegisterAttached(
                "Dock",
                typeof(Dock),
                typeof(DockPanel),
                new PropertyMetadata(Dock.Left, OnDockPropertyChanged));

        /// <summary>
        /// DockProperty property changed handler.
        /// </summary>
        /// <param name="d">UIElement that changed its Dock.</param>
        /// <param name="e">Event arguments.</param>
        [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "Almost always set from the attached property CLR setter.")]
        private static void OnDockPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Ignore the change if requested
            if (_ignorePropertyChange)
            {
                _ignorePropertyChange = false;
                return;
            }

            UIElement element = (UIElement)d;
            Dock value = (Dock)e.NewValue;

            // Validate the Dock property
            if ((value != Dock.Left) &&
                (value != Dock.Top) &&
                (value != Dock.Right) &&
                (value != Dock.Bottom))
            {
                // Reset the property to its original state before throwing
                _ignorePropertyChange = true;
                element.SetValue(DockProperty, (Dock)e.OldValue);

                string message = string.Format(
                    CultureInfo.InvariantCulture,
                    "InvalidValue",
                    value);
                throw new ArgumentException(message, "value");
            }

            // Cause the DockPanel to update its layout when a child changes
            DockPanel panel = VisualTreeHelper.GetParent(element) as DockPanel;
            if (panel != null)
            {
                panel.InvalidateMeasure();
            }
        }
        #endregion public attached Dock Dock

        /// <summary>
        /// Initializes a new instance of the
        /// <see cref="T:System.Windows.Controls.DockPanel" /> class.
        /// </summary>
        public DockPanel()
        {
        }

        /// <summary>
        /// Measures the child elements of a
        /// <see cref="T:System.Windows.Controls.DockPanel" /> in preparation
        /// for arranging them during the
        /// <see cref="M:System.Windows.Controls.DockPanel.ArrangeOverride(System.Windows.Size)" />
        /// pass.
        /// </summary>
        /// <param name="constraint">
        /// The area available to the
        /// <see cref="T:System.Windows.Controls.DockPanel" />.
        /// </param>
        /// <returns>
        /// The desired size of the
        /// <see cref="T:System.Windows.Controls.DockPanel" />.
        /// </returns>
        [SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Justification = "Compat with WPF.")]
        protected override Size MeasureOverride(Size constraint)
        {
            double usedWidth = 0.0;
            double usedHeight = 0.0;
            double maximumWidth = 0.0;
            double maximumHeight = 0.0;

            // Measure each of the Children
            foreach (UIElement element in Children)
            {
                // Get the child's desired size
                Size remainingSize = new Size(
                    Math.Max(0.0, constraint.Width - usedWidth),
                    Math.Max(0.0, constraint.Height - usedHeight));
                element.Measure(remainingSize);
                Size desiredSize = element.DesiredSize;

                // Decrease the remaining space for the rest of the children
                switch (GetDock(element))
                {
                    case Dock.Left:
                    case Dock.Right:
                        maximumHeight = Math.Max(maximumHeight, usedHeight + desiredSize.Height);
                        usedWidth += desiredSize.Width;
                        break;
                    case Dock.Top:
                    case Dock.Bottom:
                        maximumWidth = Math.Max(maximumWidth, usedWidth + desiredSize.Width);
                        usedHeight += desiredSize.Height;
                        break;
                }
            }

            maximumWidth = Math.Max(maximumWidth, usedWidth);
            maximumHeight = Math.Max(maximumHeight, usedHeight);
            return new Size(maximumWidth, maximumHeight);
        }

        /// <summary>
        /// Arranges the child elements of the
        /// <see cref="T:System.Windows.Controls.DockPanel" /> control.
        /// </summary>
        /// <param name="arrangeSize">
        /// The area in the parent element that the
        /// <see cref="T:System.Windows.Controls.DockPanel" /> should use to
        /// arrange its child elements.
        /// </param>
        /// <returns>
        /// The actual size of the
        /// <see cref="T:System.Windows.Controls.DockPanel" /> after the child
        /// elements are arranged. The actual size should always equal
        /// <paramref name="arrangeSize" />.
        /// </returns>
        [SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Justification = "Compat with WPF.")]
        protected override Size ArrangeOverride(Size arrangeSize)
        {
            double left = 0.0;
            double top = 0.0;
            double right = 0.0;
            double bottom = 0.0;

            // Arrange each of the Children
            UIElementCollection children = Children;
            int dockedCount = children.Count - (LastChildFill ? 1 : 0);
            int index = 0;
            foreach (UIElement element in children)
            {
                // Determine the remaining space left to arrange the element
                Rect remainingRect = new Rect(
                    left,
                    top,
                    Math.Max(0.0, arrangeSize.Width - left - right),
                    Math.Max(0.0, arrangeSize.Height - top - bottom));

                // Trim the remaining Rect to the docked size of the element
                // (unless the element should fill the remaining space because
                // of LastChildFill)
                if (index < dockedCount)
                {
                    Size desiredSize = element.DesiredSize;
                    switch (GetDock(element))
                    {
                        case Dock.Left:
                            left += desiredSize.Width;
                            remainingRect.Width = desiredSize.Width;
                            break;
                        case Dock.Top:
                            top += desiredSize.Height;
                            remainingRect.Height = desiredSize.Height;
                            break;
                        case Dock.Right:
                            right += desiredSize.Width;
                            remainingRect.X = Math.Max(0.0, arrangeSize.Width - right);
                            remainingRect.Width = desiredSize.Width;
                            break;
                        case Dock.Bottom:
                            bottom += desiredSize.Height;
                            remainingRect.Y = Math.Max(0.0, arrangeSize.Height - bottom);
                            remainingRect.Height = desiredSize.Height;
                            break;
                    }
                }

                element.Arrange(remainingRect);
                index++;
            }

            return arrangeSize;
        }
    }
}

答案 3 :(得分:1)

如何使用RelativePanel。

<RelativePanel Width="320" Height="180" BorderBrush="Black" BorderThickness="1">
  <TextBox RelativePanel.AlignTopWithPanel="True"
           RelativePanel.AlignBottomWithPanel="True"
           RelativePanel.AlignLeftWithPanel="True"
           RelativePanel.AlignRightWithPanel="True"
           TextWrapping="Wrap"/>
</RelativePanel>

答案 4 :(得分:0)

我走过这片沙漠10分钟或者几个小时,并且发现最好的本地控制来完成内容是网格看我的snipet工作正常:

<Grid >
   <TextBox x:Name="txtCountry" Margin="0,8,41,8" />
   <Button x:Name="btnGetCountry" Content="..." Margin="0,8,0,8"  HorizontalAlignment="Right"  Click="btnGetCountry_Click"> </Button>
</Grid>

这将产生一个很好的适应性布局,文本框劈开,并在它的一侧保持他的方面