DependencyProperty
のカスタム wpf コントロールにを実装するにはどうすればよいImageSource
ですか?
とりわけ画像を表示するカスタム コントロール (ボタン) を作成しました。コントロールの外側から画像の ImageSource を設定できるようにしたいので、DependencyProperty を実装しました。ただし、ImageSource を変更しようとすると、SystemInvalidOperationException
「別のスレッドが所有しているため、呼び出し元のスレッドはこのオブジェクトにアクセスできません。」というメッセージが表示されます。
OK、メイン スレッドはイメージ コントロールにアクセスできないので、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" />
私の進歩を反映するために上記のソースコードを適応させました..
解決:
上記のコードは正常に動作しています。初期バージョンからの重要な変更点は、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;
}