Как реализовать 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 ответ

Решение
  1. Если вы используете изображение внутри, имеет смысл повторно использовать Image.SourceProperty а не что из ImageBrush,

  2. Обязательно создайте все аффинно-зависимые элементы в потоке пользовательского интерфейса или вам нужно Freeze их, если они замораживаются, прежде чем делать что-либо с ними. (ImageSource это Freezable)

  3. Обработчик измененного свойства не подключен, поэтому он никогда не будет вызываться, его содержимое в любом случае бессмысленно, свойство уже было установлено при вызове обработчика.

Другие вопросы по тегам