【问题标题】:WPF Image with two sources具有两个来源的 WPF 图像
【发布时间】:2013-08-19 13:48:18
【问题描述】:

我想通过添加第二个来源来扩展 Image 类。我想在 XAML 中定义第二个源(如原始源)并在鼠标进入/离开此图像时更改这些图像。

我自己尝试过:

class MainMenuImageButton : Image
    {
        public static readonly DependencyProperty Source2Property;
        public ImageSource Source2 
        {
            get { return Source2; }
            set
            {
                this.MouseEnter+=new System.Windows.Input.MouseEventHandler(MainMenuImageButton_MouseEnter);
            }
        }
        public void MainMenuImageButton_MouseEnter(object sender, MouseEventArgs e)
        {
            this.Source = Source2;
        }
    }

但它不起作用,我认为我这样做完全错误。有人可以帮忙吗?

[更新]

这是我写的:

class MainMenuImageButton : Image
{
    protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
    {
        var source = (BitmapSource)Source;
        var x = (int)(hitTestParameters.HitPoint.X / ActualWidth * source.PixelWidth);
        var y = (int)(hitTestParameters.HitPoint.Y / ActualHeight * source.PixelHeight);
        var pixels = new byte[4];
        source.CopyPixels(new Int32Rect(x, y, 1, 1), pixels, 4, 0);
        if (pixels[3] < 10) return null;
        return new PointHitTestResult(this, hitTestParameters.HitPoint);
    }
    public ImageSource Source1
    {
        get { return GetValue(ImageSourceProperty) as ImageSource; }
        set { base.SetValue(ImageSourceProperty, value); }
    }
    public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register("Source1", typeof(ImageSource), typeof(MainMenuImageButton));
    public ImageSource Source2
    {
        get { return GetValue(ImageSource2Property) as ImageSource; }
        set { base.SetValue(ImageSource2Property, value); }
    }
    public static readonly DependencyProperty ImageSource2Property = DependencyProperty.Register("Source2", typeof(ImageSource), typeof(MainMenuImageButton));
    public MainMenuImageButton() : base() 
    {
        this.MouseEnter += new MouseEventHandler(MainMenuImageButton_MouseEnter);
        this.MouseLeave += new MouseEventHandler(MainMenuImageButton_MouseLeave);
    }

    void MainMenuImageButton_MouseLeave(object sender, MouseEventArgs e)
    {
        this.Source = this.Source1;
    }

    void MainMenuImageButton_MouseEnter(object sender, MouseEventArgs e)
    {
        this.Source = this.Source2;
    }
}

但有时有效,有时会出现异常:“PresentationCore.dll 中发生'System.ArgumentException' 类型的未处理异常

附加信息:该值超出预期范围。”


我不确定我是否理解,但我试过这个:

class MainMenuImageButton : Image
{
    public static readonly DependencyProperty Source2Property = DependencyProperty.Register("Source2", typeof(ImageSource), typeof(MainMenuImageButton), new PropertyMetadata(true));
    public ImageSource Source2 
    {
        get { return (ImageSource)GetValue(Source2Property); }
        set
        {
            BitmapImage logo = new BitmapImage(new Uri(value.ToString(), UriKind.Relative));
            SetValue(Source2Property, logo); 
            this.MouseEnter+=new System.Windows.Input.MouseEventHandler(MainMenuImageButton_MouseEnter);
        }
    }
    public void MainMenuImageButton_MouseEnter(object sender, MouseEventArgs e)
    {
        this.Source = Source2;
    }
}

还是什么都没有。我哪里做错了?

【问题讨论】:

标签: c# wpf image


【解决方案1】:

请参阅Custom Dependency Properties article on MSDN。事件连接属于您的依赖属性的PropertyChangedCallback

我还建议使用触发器而不是事件处理。但是,这并不意味着您需要在任何想要使用它的地方复制 XAML。您可以使用默认样式的图像切换触发器定义自定义控件(请参阅Control Authoring Overview 中的“在主题级别定义资源”)。其中MouseOverImage 是具有“Source”和“Source2”依赖属性的Control,您可以定义此默认样式:

<Style TargetType="local:MouseOverImage">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:MouseOverImage">
                <Grid>
                    <Image Name="SourceImage" Source="{TemplateBinding Source}" />
                    <Image Name="Source2Image" Source="{TemplateBinding Source2}" Visibility="Hidden" />
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="SourceImage" Property="Visibility" Value="Hidden" />
                        <Setter TargetName="Source2Image" Property="Visibility" Value="Visible" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

如果您使用事件处理程序,则需要存储 Source 的原始值,添加一个将其还原的MouseLeave 处理程序,并考虑用户随时重新分配SourceSource2 的情况。使用带有两个单独的“Source”和“Source2”绑定的触发器解决方案,所有这些都是自动处理的。

编辑

但有时它会起作用,有时会出现异常:“未处理的 “System.ArgumentException”类型的异常发生在 PresentationCore.dll

附加信息:该值超出预期范围。”

我的猜测是HitTestCore 在源更改之后但在应用到布局之前触发,因此ActualWidthsource.PixelWidth 之间存在差异。我不确定将这些包含在计算中的理由(它们不应该总是相同的吗?)尝试使用以下内容:

var x = (int)hitTestParameters.HitPoint.X;
var y = (int)hitTestParameters.HitPoint.Y; 

【讨论】:

  • 但我也想重写 HitTestCore 方法,只对非透明字段做出反应,并像按钮一样使用它。
  • @petros 那么扩展 Image 可能更容易。我将使用单独的属性来定义“正常”源,并在MouseEnter / MouseLeave 处理程序中使用SetBindingSource 绑定到Source1Source2。这样,两个属性可以独立绑定,而不会在鼠标移动时覆盖另一个。
  • 我试过你的版本,但它只在左下角有反应,所以我退回了我的版本。但你是对的,它与 HitTestCore 相关。我使用了 try-catch,它现在不会抛出异常。但我知道 try-catch 解决方案不是最好的。
  • @petros 异常将由 x 或 y 超出范围(0 到 (PixelWidth - 1) 和 0 到 (PixelHeight - 1))引起。您可以自行检查并在 x 或 y 无效时将其调整为最接近的有效值(最小值或最大值)。
  • 我试过 source.PixelWidth-1 和 source.PixelHeight-1 ,它似乎工作。调试器还没有丰富 catch 块中的断点。感谢您的帮助
【解决方案2】:

扩展图像有点过头了,你所要做的就是定义一个样式,它将使用触发器来交换源

<Image>
  <Image.Style>
    <Style TargetType="{x:Type Image}">
      <Setter Property="Source" Value="Image1"/>
      <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
          <Setter Property="Source" Value="Image2"/>
        </Trigger>
      </Style.Triggers>
    </Style>
  </Image.Style>
</Image>

【讨论】:

  • 但是在哪里选择 Image1 和 Image2?
  • @petros 您以您的风格提供Image1Image2 的路径。如果您不想硬编码图像资源路径,也可以在视图模型或代码隐藏上绑定到 ImageSource
  • 如果我想在多个控件中使用这种样式?我必须为每个人写不同的风格吗? (例如,我有 2 个图像控件,每个控件都有 2 个不同的来源)
  • @petros 转到 msdn.microsoft.com 并搜索 wpf 样式。你试过什么?
  • @Blam,在您的解决方案中,我必须在样式中选择 Image2(我认为是这样),但我想像这样选择它:
【解决方案3】:

您无需扩展 Image 类即可执行此操作。 Image 类上有一个名为 IsMouseOver 的属性,您可以触发它来切换图像的 Source。把它放在你的视图中,你就准备好了。

【讨论】:

  • 是的,但是我会经常使用这个类,所以我只想通过定义图像fila来做到这一点
【解决方案4】:

您需要将新属性添加为Dependency Property。您可以从 MSDN 的DependencyProperties Overview 页面了解更多信息,但基本思路是这样的:

你首先创建Dependency Property:

public static readonly DependencyProperty IsSpinningProperty = DependencyProperty.Register(
"IsSpinning", typeof(Boolean), typeof(YourClassName), new PropertyMetadata(true));

然后您可以选择使用标准 CLR 属性添加包装器(仅供您自己使用):

public bool IsSpinning
{
    get { return (bool)GetValue(IsSpinningProperty); }
    set { SetValue(IsSpinningProperty, value); }
}

(代码示例取自链接文章)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-05
    • 2013-11-20
    相关资源
    最近更新 更多