2011-12-06 76 views
1

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

我创建了一个自定义控件(按钮),其中包括显示图像。我希望能够从控件的外部为图像设置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其中控件初始化lised:

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

改编上面的源代码,以反映我的进步..

解决方案:

上面的代码发布工作正常。最初版本的重要变化是添加了UI线程中的Freeze()方法(请参阅接受的答案)。 我的项目中的实际问题不是按钮不是在UI线程中被初始化,但是新图像是从另一个线程设置的。该图像设置在一个事件处理程序中,该程序本身由另一个线程触发。我使用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; 
} 
+0

wth class names? –

+0

你是对的,我很惭愧。特别是我倾向于遵循最佳实践和命名惯例。这些课程是前一段时间创建的,我甚至不记得为什么我这次没有正确地命名它们。这实际上使事情更糟糕o。O –

+0

至少你似乎没有混合德语,它可能更糟糕:P –

回答

1
  1. 如果您使用的图像内是有意义的重用Image.SourceProperty,而不是说的ImageBrush

  2. 确保创建UI线程上的所有线程仿射元素,或者您需要Freeze他们是否freezable你做任何事情跨线程与他们之前。 (ImageSourceFreezable

  3. 属性更改处理器没有挂接所以它永远不会被调用,它的内容是没有意义的,虽然无论如何,当处理器被称为属性已设置。

+0

1.使用ImageBrush,因此可以使用现有的DependencyProperty而不是创建一个新的,因为通过使用ImageBrush,可以使用ImageSourceProperty .AddOwner()方法。无论如何,现在我恢复到我的旧代码,它实现了一个类型为ImageSource的* new * DependencyProperty,就像你所建议的那样。 2)我似乎仍然很难获得这一点。 AFAIK Freeze使对象无法修改。但我想改变图像 - 我可能从错误的角度阅读了迄今为止我发现的示例代码,因此我没有真正了解如何以及为何实现它... –

+0

我没有建议从头开始实施,我刚才说过你选错了一个叫'AddOwner'的选项。如果您需要在创建后修改图像,则需要在UI线程上创建它。但为什么你需要这样做?如果您需要创建一个新映像,将其冻结并将其传递给属性,则freezables通常非常轻量级,以至于创建新实例所需的内容并不重要。 –

+0

3)没错。无论如何,即使有你提供的提示,我也缺乏以我想要的方式实施它的能力。我想我将不得不初始化控制本身中所有需要的图像,并暴露另一个属性,而不是决定哪个图像实际显示。这对这个控制来说很好(只是不能用于我能想到的其他用途,这让我困扰)。有必要看看Reflector是否会带来一些额外的英特尔工作。 –