如何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;
}