Как реализовать DependencyProperty
в пользовательском элементе управления wpf для ImageSource
?
Я создал пользовательский элемент управления (кнопку), который, помимо прочего, отображает изображение. Я хочу иметь возможность установить ImageSource для изображения снаружи элемента управления, поэтому я реализовал свойство DependencyProperty. Однако всякий раз, когда я пытаюсь изменить ImageSource, я получаю SystemInvalidOperationException
: "Вызывающий поток не может получить доступ к этому объекту, потому что другой поток владеет им."
ОК, поэтому основной поток не может получить доступ к элементу управления изображениями, поэтому мне нужно использовать диспетчер - но где и как? Видимо, исключение выдается в установщик, выполняя 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" />
Адаптировал приведенный выше исходный код, чтобы отразить мой прогресс.
Решение:
Код, указанный выше, работает нормально. Важным изменением по сравнению с первоначальной версией является добавление метода Freeze () из потока пользовательского интерфейса (см. Принятый ответ). Фактическая проблема в моем проекте не в том, что кнопка не инициализируется в потоке пользовательского интерфейса, , но новое изображение устанавливается из другого потока. Изображение устанавливается в обработчике событий, который сам запускается из другого потока. Я решил проблему с помощью 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;
}