【问题标题】:How do I implement a DependencyProperty on a custom wpf control for an ImageSource?如何在 ImageSource 的自定义 wpf 控件上实现 DependencyProperty?
【发布时间】:2011-12-06 11:31:16
【问题描述】:

如何在 ImageSource 的自定义 wpf 控件上实现 DependencyProperty

我创建了一个自定义控件(按钮),其中包括显示图像。我希望能够从控件外部为图像设置 ImageSource,所以我实现了一个 DependencyProperty。每当我尝试更改 ImageSource 时,我都会收到 SystemInvalidOperationException“调用线程无法访问此对象,因为不同的线程拥有它。”

好的,所以主线程无法访问图像控件,所以我需要使用 Dispatcher - 但是在哪里以及如何?显然在setter中抛出异常,执行SetValue(ImageProperty, value);

tv_CallStart.xaml:

<Button x:Class="EHS_TAPI_Client.Controls.tv_CallStart" x:Name="CallButton"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="24" d:DesignWidth="24" Padding="0" BorderThickness="0"
         Background="#00000000" BorderBrush="#FF2467FF" UseLayoutRounding="True">

         <Image x:Name="myImage"
                Source="{Binding ElementName=CallButton, Path=CallImage}" />
</Button>

tv_CallStart.xaml.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace EHS_TAPI_Client.Controls
{
    public partial class InitiateCallButton : Button
    {
        public ImageSource CallImage
        {
            get { return (ImageSource)GetValue(CallImageProperty); }
            set { SetValue(CallImageProperty, value); }
        }
        public static readonly DependencyProperty CallImageProperty =
            DependencyProperty.Register("CallImage", typeof(ImageSource), typeof(InitiateCallButton), new UIPropertyMetadata(null));

        public InitiateCallButton()
        {
            InitializeComponent();
        }
    }
}

从 UI 线程的代码隐藏设置图像:

BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.UriSource = new Uri("pack://application:,,,/EHS-TAPI-Client;component/Images/call-start.png");
bi.EndInit();
bi.Freeze();
CallButton.CallImage = bi;

以及初始化控件的 MainWindow.xaml:

<ctl:InitiateCallButton x:Name="CallButton" CallImage="/Images/call-start.png" />

改编了上面的源代码以反映我的进步..

解决方案:

上面发布的代码工作正常。与初始版本相比的重要变化是从 UI 线程中添加了 Freeze() 方法(请参阅接受的答案)。 my 项目中的实际问题不是按钮没有 在 UI 线程中被初始化,而是 是从另一个线程设置的新图像。图像设置在一个事件处理程序中,该处理程序本身是从另一个线程触发的。我使用Dispatcher解决了这个问题:

BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.UriSource = new Uri("pack://application:,,,/EHS-TAPI-Client;component/Images/call-start.png");
bi.EndInit();
bi.Freeze();
if (!Application.Current.Dispatcher.CheckAccess())
{
    Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate()
        {
            CallButton.CallImage = bi;
        });
}
else
{
    CallButton.CallImage = bi;
}

【问题讨论】:

  • 你是对的,我很惭愧。特别是因为我通常倾向于遵循最佳实践和命名约定。这些类是前一段时间创建的,我什至不记得为什么这次我没有正确命名它们。这实际上使事情变得更糟o.O
  • 至少你似乎没有把德语混进去,这可能更糟:P

标签: wpf dependency-properties imagesource


【解决方案1】:
  1. 如果您在内部使用图像,则重用 Image.SourceProperty 而不是 ImageBrush 是有意义的。

  2. 确保在 UI 线程上创建所有线程仿射元素,或者如果它们是 freezable,则需要 Freeze 它们,然后才能对它们进行任何跨线程操作。 (ImageSourceFreezable

  3. 属性更改的处理程序未连接,因此永远不会被调用,但无论如何它的内容毫无意义,调用处理程序时已设置属性。

【讨论】:

  • 1.使用 ImageBrush,因此可以使用现有的 DependencyProperty 而不是创建新的,因为通过使用 ImageBrush 可以使用其 ImageSourceProperty.AddOwner() 方法。无论如何,现在我恢复到我的旧代码,它确实实现了 ImageSource 类型的 new DependencyProperty,正如你所建议的那样。 2)我似乎仍然无法理解这一点。 AFAIK 冻结使对象不可修改。但是我想更改图像-我可能从错误的角度阅读了迄今为止发现的示例代码,因此我真的不知道如何以及为什么要实现它...
  • 我不建议从头开始实施,我只是说你选错了电话AddOwner on。如果您需要在创建图像后对其进行修改,则需要在 UI 线程上创建它。但是你为什么需要这样做呢?如果您需要一个新图像,只需创建一个,将其冻结并将其传递给属性,freezables 通常非常轻量级,因此您需要创建一个新实例并不重要。
  • 3) 是的。无论如何,即使有你给出的提示,我也无法按照我的意图实现它。我想我将不得不在控件本身中初始化所有需要的图像并公开另一个属性来确定实际显示的图像。这对这个控件很好(只是不适用于我能想到的其他用途,这让我很困扰)。我得看看Reflector是否会带来一些额外的英特尔来使用。
  • 那么冻结 ImageSource 有什么不好的呢?这不像你冻结财产或任何东西。
  • 我更改了最初的帖子以反映我所做的更改。在 UI 线程中创建 Image,将其冻结并设置控件的 ImageSource。显然我做错了,因为我仍然得到异常。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-06
  • 1970-01-01
  • 2020-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多