Làm cách nào để loại bỏ các phần tử con của StackPanel?


187

Đưa ra một StackPanel:

<StackPanel>
  <TextBox Height="30">Apple</TextBox>
  <TextBox Height="80">Banana</TextBox>
  <TextBox Height="120">Cherry</TextBox>
</StackPanel>

Cách tốt nhất để loại bỏ các phần tử con sao cho có những khoảng trống có kích thước bằng nhau giữa chúng, mặc dù bản thân các phần tử con có kích thước khác nhau? Nó có thể được thực hiện mà không thiết lập các thuộc tính trên từng đứa trẻ?


Thực sự chỉ cần thêm phần đệm vào các mục riêng lẻ dường như là lựa chọn tốt nhất.
zar

Câu trả lời:


278

Sử dụng Margin hoặc Padding, áp dụng cho phạm vi trong vùng chứa:

<StackPanel>
    <StackPanel.Resources>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Margin" Value="0,10,0,0"/>
        </Style>
    </StackPanel.Resources> 
    <TextBox Text="Apple"/>
    <TextBox Text="Banana"/>
    <TextBox Text="Cherry"/>
</StackPanel>

EDIT: Trong trường hợp bạn muốn sử dụng lại lề giữa hai container, bạn có thể chuyển đổi giá trị ký quỹ thành tài nguyên ở phạm vi bên ngoài,

<Window.Resources>
    <Thickness x:Key="tbMargin">0,10,0,0</Thickness>
</Window.Resources>

và sau đó tham khảo giá trị này trong phạm vi bên trong

<StackPanel.Resources>
    <Style TargetType="{x:Type TextBox}">
        <Setter Property="Margin" Value="{StaticResource tbMargin}"/>
    </Style>
</StackPanel.Resources>

5
Phong cách phạm vi là một cách tuyệt vời để làm điều đó - cảm ơn vì tiền boa!
Ana Betts

2
Nếu tôi muốn sử dụng nó cho toàn bộ dự án thì sao?
grv_9098

10
Ai đó có thể giải thích tại sao điều này chỉ hoạt động khi bạn xác định rõ ràng loại (ví dụ: TextBox) không? Nếu tôi thử điều này bằng cách sử dụng FrameworkE bổ sung để tất cả trẻ em được đặt cách nhau, nó không có hiệu lực.
Jack Ukleja

4
Điều này không hoạt động tốt nếu bạn đã xác định một phong cách cho Button.
Đánh dấu Ingram

1
Như một lưu ý phụ, nếu bạn muốn làm điều này với một thứ Labelbạn phải sử dụng Paddingthay vìMargin
Anthony Nichols

84

Một cách tiếp cận tốt đẹp khác có thể được nhìn thấy ở đây: http://bloss.microsoft.co.il/bloss/eladkatz/archive/2011/05/29/what-is-the-easiest-way-to-set-spaces-b between- các vật phẩm trong stackpanel.aspx Liên kết bị hỏng -> đây là webarchive của liên kết này.

Nó cho thấy cách tạo một hành vi đính kèm, để cú pháp như thế này sẽ hoạt động:

<StackPanel local:MarginSetter.Margin="5">
   <TextBox Text="hello" />
   <Button Content="hello" />
   <Button Content="hello" />
</StackPanel>

Đây là cách dễ nhất và nhanh nhất để đặt Margin cho một số con của bảng điều khiển, ngay cả khi chúng không cùng loại. (Nút Ie, TextBoxes, ComboBox, v.v.)


5
Đây là một cách khá thú vị để đi về điều này. Nó đưa ra rất nhiều giả định về chính xác cách bạn muốn sắp xếp mọi thứ, và thậm chí còn cho bạn cơ hội tự động điều chỉnh lề trên các mục đầu tiên / cuối cùng.
Armentage

2
+1 cho tính linh hoạt. Ngoài ra, để cải thiện bài đăng trên blog, thêm if (fe.ReadLocalValue(FrameworkElement.MarginProperty) == DependencyProperty.UnsetValue)trước khi thực sự thiết lập lề của con, nó cho phép chỉ định lề theo cách thủ công cho một số thành phần.
Xerillio

Lưu ý rằng điều này không hoạt động nếu các mục con được thêm / xóa một cách linh hoạt, chẳng hạn như trong ItemControl bị ràng buộc với một bộ sưu tập thay đổi.
Drew Noakes

1
Trong trường hợp nó không hoạt động: Xây dựng dự án, nếu không nó sẽ không hiển thị trang.
Trận chiến

2
Liên kết bị hỏng!
Dave

13

Tôi đã cải thiện câu trả lời của Elad Katz .

  • Thêm thuộc tính LastItemMargin vào MarginSetter để xử lý đặc biệt mục cuối cùng
  • Thêm thuộc tính đính kèm khoảng cách với thuộc tính dọc và ngang, thêm khoảng cách giữa các mục trong danh sách dọc và ngang và loại bỏ bất kỳ lề nào ở cuối danh sách

Mã nguồn trong ý chính .

Thí dụ:

<StackPanel Orientation="Horizontal" foo:Spacing.Horizontal="5">
  <Button>Button 1</Button>
  <Button>Button 2</Button>
</StackPanel>

<StackPanel Orientation="Vertical" foo:Spacing.Vertical="5">
  <Button>Button 1</Button>
  <Button>Button 2</Button>
</StackPanel>

<!-- Same as vertical example above -->
<StackPanel Orientation="Vertical" foo:MarginSetter.Margin="0 0 0 5" foo:MarginSetter.LastItemMargin="0">
  <Button>Button 1</Button>
  <Button>Button 2</Button>
</StackPanel>

Giống như câu trả lời của Elad, điều này không hoạt động nếu các mục con được thêm / xóa một cách linh hoạt, chẳng hạn như trong một ItemsControlbộ sưu tập thay đổi. Nó giả định các mục là tĩnh từ thời điểm Loadsự kiện của phụ huynh kích hoạt.
Drew Noakes

Ngoài ra, ý chính của bạn đưa ra 404.
Drew Noakes

Cập nhật liên kết đến ý chính.
angensesen

9

Điều bạn thực sự muốn làm là bao bọc tất cả các yếu tố con. Trong trường hợp này, bạn nên sử dụng một điều khiển vật phẩm và không dùng đến các thuộc tính khủng khiếp mà cuối cùng bạn sẽ có một triệu cho mỗi tài sản bạn muốn tạo kiểu.

<ItemsControl>

    <!-- target the wrapper parent of the child with a style -->
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="Control">
            <Setter Property="Margin" Value="0 0 5 0"></Setter>
        </Style>
    </ItemsControl.ItemContainerStyle>

    <!-- use a stack panel as the main container -->
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <!-- put in your children -->
    <ItemsControl.Items>
        <Label>Auto Zoom Reset?</Label>
        <CheckBox x:Name="AutoResetZoom"/>
        <Button x:Name="ProceedButton" Click="ProceedButton_OnClick">Next</Button>
        <ComboBox SelectedItem="{Binding LogLevel }" ItemsSource="{Binding LogLevels}" />
    </ItemsControl.Items>
</ItemsControl>

nhập mô tả hình ảnh ở đây


Mẹo hay, nhưng điều này hoạt động tốt hơn với Style TargetType là "FrameworkEuity" (ví dụ: nó không hoạt động với Image)
Puffin

1
Tôi thích ý tưởng này. Chỉ cần một bổ sung: trừ số lượng khoảng cách từ lề của StackPanel ( Margin="0 0 -5 0") cũng sẽ chống lại khoảng cách sau mục cuối cùng trong danh sách.
nhãn17

Vấn đề với điều này là kiểu bạn đặt sẽ ghi đè bất kỳ kiểu nào khác mà bạn có thể có trên Mục. Để khắc phục điều này, hãy xem câu hỏi liên quan / câu trả lời được chấp nhận tại đây
waxingsatirical

6

+1 cho câu trả lời của Serge. Và nếu bạn muốn áp dụng điều đó cho tất cả StackPanels của mình, bạn có thể làm điều này:

<Style TargetType="{x:Type StackPanel}">
    <Style.Resources>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Margin" Value="{StaticResource tbMargin}"/>
        </Style>
    </Style.Resources>
</Style>

Nhưng hãy cẩn thận: nếu bạn xác định một kiểu như thế này trong App.xaml của bạn (hoặc một từ điển khác được hợp nhất vào Application.Resource) thì nó có thể ghi đè lên kiểu mặc định của điều khiển. Đối với hầu hết các điều khiển trông như stackpanel, đó không phải là vấn đề, nhưng đối với các hộp văn bản, v.v. bạn có thể vấp phải vấn đề này , điều may mắn là có một số cách giải quyết.


<Style.Resource> Bạn có chắc chắn về nó không vì tôi đã thử nó vì nó hiển thị lỗi.
grv_9098

Xin lỗi, tôi không thể nhớ tay trái. Tôi sẽ phải thử nó để xem. Tôi sẽ lấy lại cho bạn.
Andre Luus

Vâng, nó làm việc. Chỉ cần làm tương tự để đặt Textweight = "Bold" trên tất cả TextBlocks trong StackPanel. Khác biệt duy nhất là tôi thiết lập kiểu rõ ràng trên StackPanel.
Andre Luus

Cảm ơn bạn đã quan tâm nhưng tôi vẫn còn nghi ngờ. Tôi biết về điều đó được gọi là Scope Style. Tôi đoán đó sẽ là <StackPanel.Resource> chứ không phải <Style.Resource>. Sẽ tuyệt vời hơn nếu bạn có thể dán đoạn mã của ...
grv_9098

3

Theo gợi ý của Sergey, bạn có thể xác định và sử dụng lại toàn bộ Kiểu (với nhiều setters thuộc tính khác nhau, bao gồm cả Ký quỹ) thay vì chỉ một đối tượng Độ dày:

<Style x:Key="MyStyle" TargetType="SomeItemType">
  <Setter Property="Margin" Value="0,5,0,5" />
  ...
</Style>

...

  <StackPanel>
    <StackPanel.Resources>
      <Style TargetType="SomeItemType" BasedOn="{StaticResource MyStyle}" />
    </StackPanel.Resources>
  ...
  </StackPanel>

Lưu ý rằng mẹo ở đây là sử dụng Kiểu kế thừa kiểu cho kiểu ẩn, kế thừa từ kiểu trong một số từ điển tài nguyên bên ngoài (có thể được hợp nhất từ ​​tệp XAML bên ngoài).

Sidenote:

Lúc đầu, tôi ngây thơ cố gắng sử dụng kiểu ẩn để đặt thuộc tính Kiểu của điều khiển thành tài nguyên Kiểu bên ngoài đó (giả sử được xác định bằng khóa "MyStyle"):

<StackPanel>
  <StackPanel.Resources>
    <Style TargetType="SomeItemType">
      <Setter Property="Style" Value={StaticResource MyStyle}" />
    </Style>
  </StackPanel.Resources>
</StackPanel>

Điều này khiến Visual Studio 2010 ngừng hoạt động ngay lập tức với lỗi CATASTROPHIC FAILURE (HRESULT: 0x8000FFFF (E_UNEXPECTED)), như được mô tả tại https://connect.microsoft.com/VisualStudio/feedback/details/753211/xaml-ed- -with-catastrophic-fail-when-a-style-try-to-set-style-property #


3

Grid.ColumnSpaces , Grid.RowSpaces , StackPanel.Spaces hiện có trên bản xem trước UWP, tất cả sẽ cho phép hoàn thành tốt hơn những gì được yêu cầu ở đây.

Các thuộc tính này hiện chỉ khả dụng với SDK Insider Update của Windows 10 Fall Creators Update, nhưng sẽ làm cho nó đến các bit cuối cùng!



2

Cách tiếp cận của tôi kế thừa StackPanel.

Sử dụng:

<Controls:ItemSpacer Grid.Row="2" Orientation="Horizontal" Height="30" CellPadding="15,0">
    <Label>Test 1</Label>
    <Label>Test 2</Label>
    <Label>Test 3</Label>
</Controls:ItemSpacer>

Tất cả những gì cần thiết là lớp ngắn sau:

using System.Windows;
using System.Windows.Controls;
using System;

namespace Controls
{
    public class ItemSpacer : StackPanel
    {
        public static DependencyProperty CellPaddingProperty = DependencyProperty.Register("CellPadding", typeof(Thickness), typeof(ItemSpacer), new FrameworkPropertyMetadata(default(Thickness), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnCellPaddingChanged));
        public Thickness CellPadding
        {
            get
            {
                return (Thickness)GetValue(CellPaddingProperty);
            }
            set
            {
                SetValue(CellPaddingProperty, value);
            }
        }
        private static void OnCellPaddingChanged(DependencyObject Object, DependencyPropertyChangedEventArgs e)
        {
            ((ItemSpacer)Object).SetPadding();
        }

        private void SetPadding()
        {
            foreach (UIElement Element in Children)
            {
                (Element as FrameworkElement).Margin = this.CellPadding;
            }
        }

        public ItemSpacer()
        {
            this.LayoutUpdated += PART_Host_LayoutUpdated;
        }

        private void PART_Host_LayoutUpdated(object sender, System.EventArgs e)
        {
            this.SetPadding();
        }
    }
}

0

đôi khi bạn cần đặt Padding chứ không phải Margin để tạo khoảng trống giữa các mục nhỏ hơn mặc định

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.