【问题标题】:WPF: Create Image Button click only on non-transparent portion using XAMLWPF:使用 XAML 创建图像按钮仅单击不透明部分
【发布时间】:2019-07-10 12:45:56
【问题描述】:

好的,伙计们,我正在尝试使用 .png 图像制作一个图像按钮,但我只需要使用 XAML 制作一个可点击的非透明部分。

我尝试了很多使用自定义图像类,但这些尝试都不适合我。

这是我正在使用的 png 图像的示例。

这是我在 xaml 中的图像按钮的示例代码。

<Style x:Key="Home1_Unlocked" TargetType="Button">
        <Setter Property="Width" Value="611"/>
        <Setter Property="Height" Value="541"/>
        <Setter Property="ClickMode" Value="Press"/>
        <Setter Property="Focusable" Value="False"/>
        <Setter Property="HorizontalAlignment" Value="Left"/>
        <Setter Property="VerticalAlignment" Value="Top"/>
        <Setter Property="Margin" Value="100,50,0,0"/>
        <Setter Property="UIElement.SnapsToDevicePixels" Value="True"/>
        <Setter Property="Control.Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Grid Name="WholeButton" Background="Transparent">
                        <ui:MapObject x:Name="Home" Stretch="Uniform" Source="/Resources/Game/map/but1_dim.png">
                            <!--<ui:MapObject.RenderTransformOrigin>
                                <Point X="0.5" Y="0.5"/>
                            </ui:MapObject.RenderTransformOrigin>
                            <ui:MapObject.RenderTransform>
                                <ScaleTransform ScaleX="0.5" ScaleY="0.5" />
                            </ui:MapObject.RenderTransform>-->
                        </ui:MapObject>
                        <Grid Margin="0,0,0,150" HorizontalAlignment="Center" VerticalAlignment="Bottom">
                            <TextBlock Text="0 of 0 STARS" Style="{DynamicResource Riffic}" Margin="130,0,00,0" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Right" FontSize="40">
                                <TextBlock.Effect>
                                    <DropShadowEffect Color="Black"/>
                                </TextBlock.Effect>
                            </TextBlock>
                            <Image Grid.Column="0" Source="/Resources/Game/map/wmapenable.png" HorizontalAlignment="Left" Margin="10,0,0,0" Stretch="None"/>
                            <Image Grid.Column="0" Source="/Resources/Game/map/big1.png" HorizontalAlignment="Left" Margin="40,0,0,0" Stretch="None"/>
                        </Grid>
                        <ContentPresenter Name="MyContentPresenter" Content="{TemplateBinding ContentControl.Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="Home" Property="Source" Value="/Resources/Game/map/but1.png"/>
                        </Trigger>
                        <Trigger Property="ButtonBase.IsPressed" Value="True">
                            <Setter TargetName="WholeButton" Property="RenderTransform">
                                <Setter.Value>
                                    <ScaleTransform ScaleX="1.01" ScaleY="1.01" />
                                </Setter.Value>
                            </Setter>
                            <Setter TargetName="WholeButton" Property="RenderTransformOrigin">
                                <Setter.Value>
                                    <Point X="0.5" Y="0.5"/>
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

这是自定义图像类...

public class MapObject : Image
    {
        protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
        {
            var source = (BitmapSource)Source;

            // Get the pixel of the source that was hit
            var x = (int)(hitTestParameters.HitPoint.X / ActualWidth * source.PixelWidth);
            var y = (int)(hitTestParameters.HitPoint.Y / ActualHeight * source.PixelHeight);

            // Copy the single pixel into a new byte array representing RGBA
            var pixel = new byte[4];
            source.CopyPixels(new Int32Rect(x, y, 1, 1), pixel, 4, 0);

            // Check the alpha (transparency) of the pixel
            // - threshold can be adjusted from 0 to 255
            if (pixel[3] < 10)
                return null;

            return new PointHitTestResult(this, hitTestParameters.HitPoint);
        }
    }

有人可以帮我做这个吗?

【问题讨论】:

  • 如果图片只是一个图标,最好使用 Path 元素而不是 Image。
  • 不,它不是图标,我将图像添加到要澄清的问题中
  • 我认为最简单的方法是绘制基于矢量图的图形。您也可以将 PNG 转换为矢量图形,但这需要一些返工和一些好的算法(Adobe Ilustrator)。一旦它是基于矢量的图形(不是基于像素),您就可以将其转换为 XAML 资源(由几何图形组成的图形)。这样可以很容易地完成您所需要的。
  • 我能想到的所有其他选项都相当复杂或不准确。例如。使用边缘检测算法在图像周围画一个Path。或者在图像周围绘制一个与图像尽可能接近的矩形。目标是覆盖图像以消除命中测试。
  • 使用矢量而不是 png 图像不能解决问题。我可以通过制作自己的自定义按钮并使用代码隐藏按钮效果和其他东西而不是使用 XAML 来做到这一点。

标签: c# wpf button


【解决方案1】:

有几件事我会调查:

  • 确认检测到的像素是您认为的像素。看起来您知道 DPI 缩放,但即使这样也很容易遗漏某些东西(例如,倒置的 Y 轴、从 WPF 坐标到像素坐标的不正确映射......)。
  • 是否还有其他东西也在处理鼠标点击。可能图片上的命中测试正常,但是后面的按钮还在处理透明区域的鼠标点击?
  • 如果您不能让它在图像级别工作,您可以定义一个与图像覆盖相同区域的几何图形,并针对该几何图形进行命中测试。当然,如果您有很多图片,这可能不是一个合适的解决方案。
  • 考虑 WPF 是否是适合这项工作的工具。 WPF 主要是一个用户界面框架。虽然您绝对可以使用它来制作较低级别的图形,但使用 Button 意味着默认情况下您最终会获得许多您可能不想要的 UI 功能:用户能够通过 Tab 选择按钮或使用键盘提交它,视觉亮点,低帧率(因为所有类型的事件都发生在你并不真正关心但使用宝贵资源的背景中)等。可能有一些更适合你想要做的事情的工具.我建议查看 UnityMonoGame 之类的游戏框架,它们都可以与 C# 一起使用,并且会为您提供许多开箱即用的游戏特定功能,而无需 UI 框架带来的所有包袱用它。

【讨论】:

  • 如果我制作了自己的自定义按钮来处理我现在面临的问题等等,我想要做的是可以在 WPF 中完成,我也可以在代码中处理标准或自定义效果-behinde 而不是 Xaml 之一。
猜你喜欢
  • 2017-01-05
  • 2011-06-15
  • 2019-09-21
  • 1970-01-01
  • 2013-08-04
  • 1970-01-01
  • 1970-01-01
  • 2012-01-03
  • 1970-01-01
相关资源
最近更新 更多