如何在 WPF 按钮内绘制与控件大小相同的形状或线条,而不会导致按钮永远展开?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4109625/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 22:16:32  来源:igfitidea点击:

How to draw shape or lines within a WPF button that size to the control without cause the button to expand forever?

wpfxamlbuttonsize

提问by Bart Read

I had a quick Google, and a quick look around StackOverflow, but couldn't find anyone else who'd run into this problem.

我有一个快速的谷歌,并快速浏览了 StackOverflow,但找不到其他人遇到这个问题。

I want to create a close button with a little X in it. The close button will do things like fade in and out when you're hovering over the item containing it, change colour when you mouse over, and all the usual WPF loveliness. Bottom line, it doesn't seem like it should be that difficult, but I'm running into one of the weirdest issues ever before I've even got to this stage.

我想创建一个带有小 X 的关闭按钮。当您将鼠标悬停在包含它的项目上时,关闭按钮将执行淡入和淡出、鼠标悬停时更改颜色以及所有常见的 WPF 可爱功能。最重要的是,它似乎不应该那么困难,但在我到达这个阶段之前,我遇到了最奇怪的问题之一。

Here's my XAML style for the button:

这是我的按钮 XAML 样式:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style x:Key="TabCloseButtonStyle" TargetType="{x:Type Button}">
        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="Content">
            <Setter.Value>
                <Grid>
                    <Line Stroke="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Foreground}"
                            X1="0"
                            Y1="0"
                            X2="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Path=ActualWidth, Mode=OneWay}"
                            Y2="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Path=ActualHeight, Mode=OneWay}"/>
                    <Line Stroke="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Foreground}"
                            X1="0"
                            Y1="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Path=ActualHeight, Mode=OneWay}"
                            X2="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Path=ActualWidth, Mode=OneWay}"
                            Y2="0"/>
                </Grid>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

And I create my button, just as a test, like this:

我创建了我的按钮,作为一个测试,就像这样:

<Window x:Class="WpfTestApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="124" Width="569">
    <Grid Background="#2b3c59">
        <StackPanel Orientation="Horizontal">

            <!-- Other controls removed for clarity -->

            <Button Style="{DynamicResource TabCloseButtonStyle}"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Bottom"
                    Padding="2"
                    Margin="0, 10, 0, 0"
                    MinWidth="50"
                    MinHeight="50"></Button>
        </StackPanel>
    </Grid>
</Window>

And here's where it all goes horribly wrong. When you run the application the button will expand infinitely, one pixel at a time, horizontally, and vertically until it hits the height of the window.

这就是一切都发生了可怕错误的地方。当您运行应用程序时,按钮将无限扩展,一次一个像素,水平和垂直,直到它达到窗口的高度。

Now I can understand why this is happening: the Lines actually go one unit beyond the width and height of the Grid causing the Grid to expand by one unit, then there's a relayout, the data binding causes the lines to redraw, ad infinitum.

现在我可以理解为什么会发生这种情况:线条实际上超出了网格的宽度和高度一个单位,导致网格扩展了一个单位,然后进行了重新布局,数据绑定导致线条无限重绘。

So, to try and deal with this I decided to put a in the grid but then, no matter what I do with HorizontalAlignment and VerticalAlignment, I end up with both the Canvas and the Grid having zero width and height, which means I don't get my cross, which is massively irritating. If I bind to the ActualWidth and ActualHeight properties of the button I just end up with an X that has the top-left corner centred on the centre of the button.

因此,为了尝试解决这个问题,我决定在网格中放置一个,但是,无论我如何使用 Horizo​​ntalAlignment 和 VerticalAlignment,我最终都会使 Canvas 和 Grid 的宽度和高度都为零,这意味着我不没有得到我的十字架,这是非常令人恼火的。如果我绑定到按钮的 ActualWidth 和 ActualHeight 属性,我最终会得到一个 X,它的左上角位于按钮的中心。

Does anybody have any idea at all what I can do to fix this? I'd be extremely grateful for any pointers - WPF is still a rather new beast to me.

有没有人知道我能做些什么来解决这个问题?我将非常感谢任何指点 - WPF 对我来说仍然是一个相当新的野兽。

Thanks!

谢谢!

回答by Rob Fonseca-Ensor

You shouldn't have to resort to bindings just to do layout. As you've seen, the bindings and the layout systems don't work in concert (I think databinding gets priority over layout, but in your case, layout causes another databinding)

你不应该仅仅为了布局而求助于绑定。如您所见,绑定和布局系统无法协同工作(我认为数据绑定优先于布局,但在您的情况下,布局会导致另一个数据绑定)

You should be able get quite a nice looking, stretchable X with a just a Path:

你应该可以得到一个漂亮的、可拉伸的 X,只需一条 Path:

<Path Data="M0,0 L1,1 M0,1 L1,0" Stretch="Uniform" Stroke="Red" />

If you want it to scalewith the button size instead of stretch (scale affects apparent stroke width), then just use a ViewBox

如果您希望它使用按钮大小而不是拉伸来缩放(缩放会影响明显的笔画宽度),那么只需使用ViewBox

回答by Thomas

Instead of binding, I'd suggest using the Stretch and Margin or Padding properties, like the following

我建议使用 Stretch 和 Margin 或 Padding 属性,而不是绑定,如下所示

<Grid Margin="2">
    <Line X1="0" Y1="0" Y2="1" X2="1" Stroke="Black" Stretch="Fill" />
    <Line Y1="1" X2="1" Stroke="Black" Stretch="Fill" />
</Grid>


Update

更新

So the problem with your example seems to be the min height & width. If you can't just use height & width, I'd suggest something like the following

所以你的例子的问题似乎是最小高度和宽度。如果您不能只使用高度和宽度,我建议您使用以下内容

<Grid MinHeight="{TemplateBinding MinHeight}" MinWidth="{TemplateBinding MinWidth}">
    <Line X1="0" Y1="0" Y2="1" X2="1" Stroke="Black" Stretch="Fill" />
    <Line Y1="1" X2="1" Stroke="Black" Stretch="Fill" />
</Grid>

回答by Bart Read

Thanks everyone. Rob's solution with the Viewboxturned out to be the answer. The button behaves perfectly, even if I remove the MidWidthand MinHeightattributes from the Button declaration in MainWindow.xaml.

谢谢大家。Viewbox事实证明,Rob 的解决方案就是答案。即使我从MainWindow.xaml的 Button 声明中删除MidWidthMinHeight属性,该按钮也能正常运行。

Here's my modified style:

这是我修改后的样式:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style x:Key="TabCloseButtonStyle" TargetType="{x:Type Button}">
        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="Content">
        <Setter.Value>
            <Grid>
                <Viewbox>
                    <Path Data="M0,0 L10,10 M0,10 L10,0"
                          Stretch="Uniform"
                          Stroke="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Foreground, Mode=OneWay}" />
                </Viewbox>
            </Grid>
        </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Thanks again for all the suggestions!

再次感谢所有的建议!