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