67019


**やりたいこと**  
2画面に共通して存在するユーザーコントロールへ画像をバインドしたい。

**現状**  
バインドしている画像が表示されない

**ソース**  
MainWindow.xamlとSubWindow.xaml

[code]

    <window 省略>
        <Grid>
            <local:ImageUserControl />
        </Grid>
    </window>
    
[/code]

ImageUserControl.xaml

[code]

    <UserControl 省略>
        <Grid> 
            <Image Source="{Binding ImageSource}" />
        </Grid>
    </UserControl>
    
[/code]

ImageUserControl.xaml.cs

[code]

    public partial class CameraImage : UserControl
    {
        public CameraImage()
        {
            InitializeComponent();
            this.DataContext = new ImageBinding();
        }
    }
    
[/code]

ImageBinding.cs

[code]

    public class ImageBinding : BindableBase
    {
        private static ImageSource _imageSource;
        public ImageSource ImageSource
        {
            get { return _imageSource; }
            set { this.SetProperty(ref _imageSource, value); }
        }
    
        public void start(){
            flag = true
            Task.Run(() =>
                {
                    loop();
                });
        }
    
        public void stop(){
            flag = false
        }
    
        public async void loop(){
            while(flag){
                getimg();
                await Task.Delay(1000);
            }
        }
    
        public void getimg(){
            Application.Current.Dispatcher.Invoke((Action)(delegate
            {
                ImageSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(省略)
            }));
        }
    }
    
[/code]

**ソースについて補足**  
BindablebaseはとあるサイトのWin8のストアアプリ用のBindableBaseをWPF用に書き換えたものを利用しています。  
ImageSourceはSystem.Windows.Mediaのものを用いています。  
画像が動的に変わる部分は簡単に書くと  
System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap  
を用いて作成したBMP画像を入れています。  
こちらとほぼ同じソースで、単一画面、ユーザーコントロール無しのアプリでは動作確認が出来ています。

**全体の流れ**  
不要な部分は省略しているので、流れがおかしかったら申し訳ございません。  
言葉で説明させていただきます。  
①アプリをスタートするとメインウィンドウが表示されます。  
②ソースには記載していませんが、スタートボタンを押下するとImageBinding.csのstart()が動作し始めます。この時flagがtrueになり画像を取得し始めます。  
③スタートボタンを押すと同時にmainwindowが消えサブウィンドウが表示されます。（Mainwindowの画像表示が意味のないものになっていますが、ここではスルーでお願いします。）  
④サブウィンドウが開くと、Stopが実行されるまで数秒おきに切り替わる画像がImageUserControlへとバインドされます。

拙い文章で大変申し訳ございませんが、ご回答お待ちしております。


CC BY-SA 4.0
2020-05-26T08:51:12.377
67003
2020-05-27T01:55:14.640
2020-05-27T01:55:14.640
40279
40279
question
c#
C# 別スレッドからユーザーコントロールに画像をバインドする


提示されたソースの不要な部分などを補完して手元の端末で試したところ、正常に動作しました。  
カレントディレクトリのpngファイルを1秒ごとに切り替えて表示するサンプルコードを置いておきます。  
省略した不要な部分に不具合の原因がないかを確認してみてください。

※サンプルコードのBindableBaseは
<http://sourcechord.hatenablog.com/entry/20130303/1362315081> からお借りしました。

  * BindableBaseが問題なく実装されているか 
    * PropertyChanged イベントは呼び出されていますか
  * Bindingが正しく行われているか 
    * コンストラクタやロードイベントなど適切なタイミングでBindngしていますか
  * その他に不具合はないか 
    * start()ではなくgetimg()メソッドを単体で呼び出した場合は画像が表示されますか

MainWindow.xaml.cs

[code]

    using System;
    using System.ComponentModel;
    using System.IO;
    using System.Runtime.CompilerServices;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    
    namespace WpfApplication1
    {
        public partial class MainWindow : Window
        {
            public ImageBinding ImgBinding { get; set; }
            private SubWindow Sub;
    
            public MainWindow()
            {
                InitializeComponent();
    
                ImgBinding = new ImageBinding();
                ImgBinding.start();
                Sub = new SubWindow();
                DataContext = ImgBinding;
                Sub.DataContext = ImgBinding;
                Sub.Show();
            }
        }
    
        public class ImageBinding : BindableBase
        {
            private static ImageSource _imageSource;
            public ImageSource ImageSource
            {
                get { return _imageSource; }
                set { this.SetProperty(ref _imageSource, value); }
            }
    
            private string[] ImagePaths;
            private int Index;
    
            public void start()
            {
                //カレントディレクトリからpngファイルのパスを取得する
                ImagePaths = Directory.GetFiles(Environment.CurrentDirectory, "*.png", SearchOption.TopDirectoryOnly);
                if (ImagePaths.Length == 0)
                {
                    return;
                }
                Index = 0;
                Task.Run(() =>
                {
                    loop();
                });
            }
    
            public async void loop()
            {
                while (true)
                {
                    Index++;
                    if (ImagePaths.Length <= Index)
                    {
                        Index = 0;
                    }
                    getimg(ImagePaths[Index]);
                    await Task.Delay(1000);
                }
            }
    
            public void getimg(string path)
            {
                Application.Current.Dispatcher.Invoke((Action)(delegate
                {
                    //ImageSource = 画像が動的に替わる
                    ImageSource = new BitmapImage(new Uri(path, UriKind.Absolute));
                }));
            }
        }
    
        /// <summary>
        /// モデルを簡略化するための <see cref="INotifyPropertyChanged"/> の実装。
        /// </summary>
        /// <remarks>http://sourcechord.hatenablog.com/entry/20130303/1362315081</remarks>
        public abstract class BindableBase : INotifyPropertyChanged
        {
            /// <summary>
            /// プロパティの変更を通知するためのマルチキャスト イベント。
            /// </summary>
            public event PropertyChangedEventHandler PropertyChanged;
    
            /// <summary>
            /// プロパティが既に目的の値と一致しているかどうかを確認します。必要な場合のみ、
            /// プロパティを設定し、リスナーに通知します。
            /// </summary>
            /// <typeparam name="T">プロパティの型。</typeparam>
            /// <param name="storage">get アクセス操作子と set アクセス操作子両方を使用したプロパティへの参照。</param>
            /// <param name="value">プロパティに必要な値。</param>
            /// <param name="propertyName">リスナーに通知するために使用するプロパティの名前。
            /// この値は省略可能で、
            /// CallerMemberName をサポートするコンパイラから呼び出す場合に自動的に指定できます。</param>
            /// <returns>値が変更された場合は true、既存の値が目的の値に一致した場合は
            /// false です。</returns>
            protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
            {
                if (object.Equals(storage, value)) return false;
    
                storage = value;
                this.OnPropertyChanged(propertyName);
                return true;
            }
    
            /// <summary>
            /// プロパティ値が変更されたことをリスナーに通知します。
            /// </summary>
            /// <param name="propertyName">リスナーに通知するために使用するプロパティの名前。
            /// この値は省略可能で、
            /// <see cref="CallerMemberNameAttribute"/> をサポートするコンパイラから呼び出す場合に自動的に指定できます。</param>
            protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                var eventHandler = this.PropertyChanged;
                if (eventHandler != null)
                {
                    eventHandler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    }
    
[/code]

MainWindow.xaml

[code]

    <Window x:Class="WpfApplication1.MainWindow" 
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
            xmlns:local="clr-namespace:WpfApplication1" 
            Height="350" Width="525">
        <Grid>
            <local:ImageUserControl />
        </Grid>
    </Window>
    
[/code]

SubWindow.xaml

[code]

    <Window x:Class="WpfApplication1.SubWindow" 
    <!-- 以下、MainWondow.xaml と同一 -->
    
[/code]

ImageUserControl.xaml

[code]

    <UserControl x:Class="WpfApplication1.ImageUserControl" 
                 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="300" d:DesignWidth="300">
        <Grid>
            <Image Source="{Binding ImageSource}" />
        </Grid>
    </UserControl>
    
[/code]

SubWindow.xaml.cs

[code]

    using System.Windows;
    
    namespace WpfApplication1
    {
        public partial class SubWindow : Window
        {
            public SubWindow()
            {
                InitializeComponent();
            }
        }
    }
    
[/code]

ImageUserControl.xaml.cs

[code]

    using System.Windows.Controls;
    
    namespace WpfApplication1
    {
        public partial class ImageUserControl : UserControl
        {
            public ImageUserControl()
            {
                InitializeComponent();
            }
        }
    }
    
[/code]


CC BY-SA 4.0
2020-05-27T01:33:06.077
67019
2020-05-27T01:33:06.077
9820
67003
answer