如何在 WPF 按钮内绘制尺寸适合控件的形状或线条,而不导致按钮永远扩展?

发布于 2024-10-01 01:07:20 字数 3042 浏览 3 评论 0原文

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

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

这是我的按钮的 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>

我创建了我的按钮,只是作为测试,如下所示:

<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>

这就是一切出现严重错误的地方。当您运行应用程序时,按钮将水平和垂直无限扩展,一次一个像素,直到达到窗口的高度。

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

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

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

谢谢!

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

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.

Here's my XAML style for the button:

<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.

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.

Thanks!

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

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

发布评论

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

评论(3

酒儿 2024-10-08 01:07:20

您不必仅仅为了进行布局而诉诸绑定。正如您所看到的,绑定和布局系统不能协同工作(我认为数据绑定优先于布局,但在您的情况下,布局会导致另一个数据绑定)

您应该能够获得一个非常漂亮的、可拉伸的 X a 只是一个路径:

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

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

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:

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

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

满身野味 2024-10-08 01:07:20

我建议使用 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

因此,您的示例的问题似乎是最小高度和最小高度。宽度。如果你不能只使用高度和宽度,我建议如下

<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>

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

<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>
我的奇迹 2024-10-08 01:07:20

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

这是我修改后的风格:

<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 everyone. Rob's solution with the Viewbox turned out to be the answer. The button behaves perfectly, even if I remove the MidWidth and MinHeight attributes from the Button declaration in MainWindow.xaml.

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!

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