如何在ImageSource的自定义wpf控件上实现DependencyProperty? [英] How do I implement a DependencyProperty on a custom wpf control for an ImageSource?

查看:139
本文介绍了如何在ImageSource的自定义wpf控件上实现DependencyProperty?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在ImageSource的自定义wpf控件上实现DependencyProperty?

How do I implement a DependencyProperty on a custom wpf control for an ImageSource?

我创建了一个自定义控件(按钮),该控件除其他功能外还显示图像.我希望能够从控件的外部设置图像的ImageSource,因此我实现了DependencyProperty.每当我尝试更改ImageSource时,都会得到一个SystemInvalidOperationException:调用线程无法访问该对象,因为另一个线程拥有该对象."

I created a custom control (button) which amongst other things does display an image. I want to be able to set the ImageSource for the image from the outside of the control, so I implemented a DependencyProperty. Whenever I try to change the ImageSource though, I am getting a SystemInvalidOperationException: "The calling thread cannot access this object because a different thread owns it."

好的,因此主线程无法访问图像控件,因此我需要使用Dispatcher-但是在哪里以及如何使用?显然,该异常是在setter中引发的,执行SetValue(ImageProperty, value);

OK, so the main thread cannot access the image control, so I need to use the Dispatcher - but where and how? Apparently the exception is thrown in the setter, executing SetValue(ImageProperty, value);

tv_CallStart.xaml:

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

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线程的代码后面设置图像:

setting the image from the code-behind of the UI-thread:

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:

and the MainWindow.xaml where the control is initialised:

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

修改了上面的源代码以反映我的进步.

Adapted the above source code to reflect my progress..

解决方案:

上面发布的代码可以正常工作.与初始版本相比,重要的变化是在UI线程中添加了Freeze()方法(请参见接受的答案). my 项目中的实际问题不是在UI线程中初始化按钮 not ,而是在另一个线程中设置了新图像.该图像在事件处理程序中设置,该事件处理程序本身是从另一个线程触发的.我通过使用Dispatcher解决了问题:

The code posted above is working fine. The important change from the initial version is the addition of the Freeze() method from the UI thread (see accepted answer). The actual problem in my project is not the button not being initialised in the UI thread, but the new image being set from another thread. The image is set in an event handler which itself is triggered from another thread. I resolved the problem by using the 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.

确保在UI线程上创建所有线程仿射元素,或者需要 Freeze ,如果它们是可冻结在对它们执行任何交叉线程之前. ( ImageSource Freezable)

Make sure to create all thread-affine elements on the UI-thread or you need to Freeze them if they are freezable before you do anything cross-thread with them. (ImageSource is a Freezable)

未更改属性更改处理程序,因此它将永远不会被调用,尽管它的内容仍然毫无意义,但在调用处理程序时已经设置了该属性.

The property changed handler is not hooked up so it will never be called, its content is pointless anyway though, the property has already been set when the handler is called.

这篇关于如何在ImageSource的自定义wpf控件上实现DependencyProperty?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆