1

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

我创建了一个自定义控件(按钮),其中包括显示图像。我希望能够从控件外部为图像设置 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() 方法(请参阅接受的答案)。项目中的实际问题不是按钮在 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;
}
4

1 回答 1

1
  1. 如果您在内部使用图像,则重用Image.SourceProperty而不是ImageBrush.

  2. 确保在 UI 线程上创建所有线程仿射元素,或者在对它们进行任何跨线程操作之前,Freeze如果它们是可冻结的,则需要它们。ImageSource是一个Freezable

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

于 2011-12-06T11:46:15.880 回答