¿Cómo implemento una DependencyProperty en un control WPF personalizado para una imagen de ImageSource?

StackOverflow https://stackoverflow.com/questions/8399210

Pregunta

¿Cómo implemento un DependencyProperty en un control WPF personalizado para un ImageSource?

Creé un control personalizado (botón) que entre otras cosas muestra una imagen. Quiero poder establecer la imagen para la imagen del exterior del control, por lo que implementé una DependencyProperty. Sin embargo, cada vez que trato de cambiar la imagen de Imageurce, estoy obteniendo un SystemInvalidOperationException: "El hilo de llamadas no puede acceder a este objeto porque un hilo diferente lo posee".

Ok, entonces el hilo principal no puede acceder al control de la imagen, por lo que necesito usar el despachador, pero ¿dónde y cómo? Aparentemente, la excepción se lanza en el setter, ejecutando 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();
        }
    }
}

Configuración de la imagen del código-behind del hilo 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;

y el mainwindow.xaml donde se inicializa el control:

<ctl:InitiateCallButton x:Name="CallButton" CallImage="/Images/call-start.png" />

Adaptado el código fuente anterior para reflejar mi progreso.

Solución:

El código publicado anteriormente funciona bien. El cambio importante de la versión inicial es la adición del método Freeze () desde el hilo de la interfaz de usuario (ver respuesta aceptada). El problema real en mi El proyecto no es el botón no siendo inicializado en el hilo de la interfaz de usuario, pero La nueva imagen que se establece desde otro hilo. La imagen se establece en un controlador de eventos que se activa desde otro hilo. Resolví el problema usando el 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;
}
¿Fue útil?

Solución

  1. Si usa una imagen internamente, tiene sentido reutilizar el Image.SourceProperty Y no el de ImageBrush.

  2. Asegúrese de crear todos los elementos afinos de hilos en el subproceso UI o necesita Freeze ellos si son congelable Antes de hacer cualquier cosa cruzada con ellos. (ImageSource es un Freezable)

  3. El controlador cambiado de propiedad no está conectado, por lo que nunca se llamará, sin embargo, su contenido no tiene sentido, la propiedad ya se ha establecido cuando se llama al controlador.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top