如何使 RadioButton 的项目符号顶部对齐?

发布于 2024-07-13 09:35:35 字数 77 浏览 3 评论 0原文

我有一个多行单选按钮,我希望项目符号位于内容的左侧(默认情况下)与单选按钮控件的顶部对齐。 在 XAML 中执行此操作的最简单方法是什么?

I have a multiline radio button and I want the bullet to be to the left of the content (as default) aligned to the top of the radio button control. What's the easiest way to do this in XAML?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

纵情客 2024-07-20 09:35:36

我基于 Simon Weaver 的答案构建了一个相对通用的模板,可以在大多数情况下使用,而无需记住自定义始终是您的 RadioButton.Content

<ControlTemplate x:Key="MultiLineRadioButtonTemplate" TargetType="{x:Type RadioButton}">
    <RadioButton IsChecked="{TemplateBinding IsChecked}">
        <TextBlock>
            <LineBreak />
            <InlineUIContainer>
                <ContentPresenter Margin="0,-21,0,8" 
                                  Content="{TemplateBinding ContentPresenter.Content}"
                                  ContentTemplate="{TemplateBinding ContentPresenter.ContentTemplate}"/>
            </InlineUIContainer>
        </TextBlock>
    </RadioButton>
</ControlTemplate>

解释模板的工作原理:

  • TextBlock 之所以存在,是因为默认情况下,如果内容是 Text 对象(Label),RadioButton 项目符号会与 Text 的第一行对齐code> 不起作用)

  • LineBreak 是将内容换行,因此创建第一行

  • InlineUIContainer,以便我们可以将非文本内容放入TextBlock

  • ContentPresenter 是用来保存实际内容的,它有一个负数上边距用于删除 LineBreak 对象留下的空格。

这是一些示例内容:

<StackPanel>
    <RadioButton Template="{StaticResource MultiLineRadioButtonTemplate}">
        <StackPanel>
            <Label Content="Option 1" />
            <StackPanel>
                <CheckBox Content="Some setting" />
                <CheckBox Content="Some other setting" />
            </StackPanel>
        </StackPanel>
    </RadioButton>

    <RadioButton Template="{StaticResource MultiLineRadioButtonTemplate}">
        <StackPanel>
            <Label Content="Option 2" />
            <DataGrid AutoGenerateColumns="False" Height="100">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Id" />
                    <DataGridTextColumn Header="Date" />
                    <DataGridTextColumn Header="Total" />
                    <DataGridTextColumn Header="Count" />
                </DataGrid.Columns>
            </DataGrid>
        </StackPanel>
    </RadioButton>


    <RadioButton Template="{StaticResource MultiLineRadioButtonTemplate}">
        <StackPanel>
            <Label Content="Option 3" />
            <TextBlock TextWrapping="WrapWithOverflow" Margin="2">
                Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
                sed do eiusmod tempor incididunt ut labore et dolore magna 
                aliqua. Ut enim ad minim veniam, quis nostrud exercitation 
                ullamco laboris nisi ut aliquip ex ea commodo consequat. 
                Duis aute irure dolor in reprehenderit in voluptate velit 
                esse cillum dolore eu fugiat nulla pariatur. Excepteur sint 
                occaecat cupidatat non proident, sunt in culpa qui officia 
                deserunt mollit anim id est laborum.
            </TextBlock>
        </StackPanel>
    </RadioButton>
</StackPanel>

以及它的外观:

在此处输入图像描述

我唯一真正不满意的是ContentPresenter 的负上边距被硬编码,因为如果您更改字体大小,则必须手动调整该边距。

Margin 属性构建一个转换器来计算换行符的高度({TemplateBinding FontSize}?),甚至是扩展默认情况下具有此行为的 RadioButton 控件的版本,但现在我可以根据应用程序的默认字体大小硬编码 -21。

另外,如果您想从原始 RadioButton 继承其他属性,您可能需要向模板中的 RadioButton 添加一些 TemplateBindings,例如边距、填充、对齐等。为了保持简单,我只绑定到 IsChecked

I've built a relatively generic template based on Simon Weaver's answer that can be used in most situations without having to remember to customize your RadioButton.Content all the time.

<ControlTemplate x:Key="MultiLineRadioButtonTemplate" TargetType="{x:Type RadioButton}">
    <RadioButton IsChecked="{TemplateBinding IsChecked}">
        <TextBlock>
            <LineBreak />
            <InlineUIContainer>
                <ContentPresenter Margin="0,-21,0,8" 
                                  Content="{TemplateBinding ContentPresenter.Content}"
                                  ContentTemplate="{TemplateBinding ContentPresenter.ContentTemplate}"/>
            </InlineUIContainer>
        </TextBlock>
    </RadioButton>
</ControlTemplate>

To explain how the template works:

  • The TextBlock is there because by default, the RadioButton bullet aligns with the first line of Text if the content is a Text object (a Label will not work)

  • The LineBreak is to wrap the content to a new line, so the first line is created

  • The InlineUIContainer is so we can place non-text content into a TextBlock

  • The ContentPresenter is to hold the actual content, and it has a negative top margin to remove the space left by the LineBreak object.

Here's some example content:

<StackPanel>
    <RadioButton Template="{StaticResource MultiLineRadioButtonTemplate}">
        <StackPanel>
            <Label Content="Option 1" />
            <StackPanel>
                <CheckBox Content="Some setting" />
                <CheckBox Content="Some other setting" />
            </StackPanel>
        </StackPanel>
    </RadioButton>

    <RadioButton Template="{StaticResource MultiLineRadioButtonTemplate}">
        <StackPanel>
            <Label Content="Option 2" />
            <DataGrid AutoGenerateColumns="False" Height="100">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Id" />
                    <DataGridTextColumn Header="Date" />
                    <DataGridTextColumn Header="Total" />
                    <DataGridTextColumn Header="Count" />
                </DataGrid.Columns>
            </DataGrid>
        </StackPanel>
    </RadioButton>


    <RadioButton Template="{StaticResource MultiLineRadioButtonTemplate}">
        <StackPanel>
            <Label Content="Option 3" />
            <TextBlock TextWrapping="WrapWithOverflow" Margin="2">
                Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
                sed do eiusmod tempor incididunt ut labore et dolore magna 
                aliqua. Ut enim ad minim veniam, quis nostrud exercitation 
                ullamco laboris nisi ut aliquip ex ea commodo consequat. 
                Duis aute irure dolor in reprehenderit in voluptate velit 
                esse cillum dolore eu fugiat nulla pariatur. Excepteur sint 
                occaecat cupidatat non proident, sunt in culpa qui officia 
                deserunt mollit anim id est laborum.
            </TextBlock>
        </StackPanel>
    </RadioButton>
</StackPanel>

And how it looks:

enter image description here

The only thing I'm really not happy about is the negative top margin for the ContentPresenter being hard-coded, because if you ever change your font size, you have to manually adjust that margin.

It probably wouldn't be that hard to build a converter for the Margin property that calculates the height of the line break ({TemplateBinding FontSize}?), or even an extended version of the RadioButton control which has this behavior by default, but for now I'm fine with hard-coding -21 based on my application's default font size.

Also, you might want to add some TemplateBindings to the RadioButton in the Template if you want to inherit other properties from the original RadioButton, such as margins, padding, alignment, etc. I only bound to IsChecked for the purpose of keeping it simple.

南冥有猫 2024-07-20 09:35:36

覆盖 RadioButton 的 Control.Template。 以下是来自 MSDN 单选按钮控件模板示例的示例,

如果您不这样做不想覆盖单选按钮的 Control.Template,您可以将内容设为 Wrapped TextBlock。 看这个样本

<RadioButton  Name="radioButton1">
    <TextBlock TextWrapping="Wrap">Here is some multiline text that does some wrapping</TextBlock>
</RadioButton>

Override the Control.Template for the RadioButton. Here is the example from MSDN Radio Button Control Template Example

If you don't want to override the Control.Template for the radio button you can make the content a Wrapped TextBlock. see this sample

<RadioButton  Name="radioButton1">
    <TextBlock TextWrapping="Wrap">Here is some multiline text that does some wrapping</TextBlock>
</RadioButton>
很快妥协 2024-07-20 09:35:35

注意:请务必查看Rachel 的回答 - 她将这一阶段进一步转化为通用的template


首先,不要在 VerticalAlignmentVerticalContentAlignment(甚至 ControlTemplate)上浪费时间。 他们不会做你想要或可能期望的事。

MSDN 一个 BulletDecorator (这是 CheckBox 和 RadioButton 用于呈现单选/复选按钮的控件)将自动设置图标的位置。 您对此没有额外的控制权:

当子项出现时,项目符号始终与文本的第一行对齐
对象是文本对象。 如果子对象不是文本对象,则
项目符号与子对象的中心对齐。

除非您更改控件模板(不必要),否则您只能在内容是文本的情况下将单选/复选图标放置在顶部。

因此,如果您执行类似的操作,它看起来不会很好,因为无论您尝试设置多少个 VerticalAlignment 属性,您都将无法移动图标。

<RadioButton>
    <StackPanel>
        <TextBlock Text="First line"/>
        <TextBlock Text="Something else"/>
    </StackPanel>
</RadioButton>

但是幸运的是,您可以使用InlineUIContainer将几乎所有您想要的东西放入TextBlock中。 第一行中的文本(或内容)将自动指示图标的位置。 如果您想要第一行下面的内容不是文本,只需使用 ,然后使用

这里有一个示例,其中包含超大的 TextBox 可以更清楚地显示正在发生的情况。

<RadioButton>

    <TextBlock VerticalAlignment="Top" TextWrapping="Wrap">

        <TextBlock Text="Products with <" VerticalAlignment="Center" Margin="0,0,5,0"/>

        <InlineUIContainer BaselineAlignment="Center">
            <TextBox FontSize="30" Width="25" Text="10" Margin="0,0,5,0"/>          
        </InlineUIContainer>

        <TextBlock VerticalAlignment="Center" Margin="0,0,5,0">
            <Run Text="days" FontWeight="Bold"/>
            <Run Text="inventory" />
        </TextBlock>

        <LineBreak/>    

        <InlineUIContainer>
            <StackPanel>
                <CheckBox Content="Include unsold products" />
                <CheckBox Content="Include something else" />
            </StackPanel>
        </InlineUIContainer>

    </TextBlock>
</RadioButton>

Note: Be sure to check out Rachel's answer - she takes this one stage further into a generic template


First of all don't waste your time with VerticalAlignment or VerticalContentAlignment (or even ControlTemplate). They're not going to do what you want or might expect.

As described on MSDN a BulletDecorator (which is the control that CheckBox and RadioButton uses to render a radio/check button) will set the position of the icon automatically. You have no additional control over this:

A Bullet always aligns with the first line of text when the Child
object is a text object. If the Child object is not a text object, the
Bullet aligns to the center of the Child object.

Unless you change the control template (unnecessary) you'll only be able to position the radio/check icon at the top if the content is text.

So if you do something like this it won't look good because you won't be able to move the icon no matter how many VerticalAlignment properties you try to set.

<RadioButton>
    <StackPanel>
        <TextBlock Text="First line"/>
        <TextBlock Text="Something else"/>
    </StackPanel>
</RadioButton>

BUT fortunately you can put pretty much anything you want in a TextBlock using InlineUIContainer. The text (or content) in the first line will dictate the position of the icon automatically. If you want something underneath the first line that isn't text, just use <Linebreak/> and then <InlineUIContainer/>

Here's an example that has an oversized TextBox to show more clearly what's happening.

<RadioButton>

    <TextBlock VerticalAlignment="Top" TextWrapping="Wrap">

        <TextBlock Text="Products with <" VerticalAlignment="Center" Margin="0,0,5,0"/>

        <InlineUIContainer BaselineAlignment="Center">
            <TextBox FontSize="30" Width="25" Text="10" Margin="0,0,5,0"/>          
        </InlineUIContainer>

        <TextBlock VerticalAlignment="Center" Margin="0,0,5,0">
            <Run Text="days" FontWeight="Bold"/>
            <Run Text="inventory" />
        </TextBlock>

        <LineBreak/>    

        <InlineUIContainer>
            <StackPanel>
                <CheckBox Content="Include unsold products" />
                <CheckBox Content="Include something else" />
            </StackPanel>
        </InlineUIContainer>

    </TextBlock>
</RadioButton>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文