2011-12-06 84 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是否會帶來一些額外的英特爾工作。 –

相關問題