Здравствуйте. Прошу помощи. Попытался тут разобраться с TPL библиотекой, но возникает проблема с одной деталью... Суть тестового приложения: создаю форму, после формы запускаю второй поток, который скачивает картинку, а по завершении скачивания - отобразит ее в Image, который находится на главной форме. Но что-то оно не хочет работать как надо, вываливаясь с исключением, что что-то там из одного потока не принадлежит второму. Класс изображения: Code: using System; using System.IO; using System.Net; using System.Threading; using System.Windows.Media.Imaging; namespace JPGTest { public class DownloadPicture { public BitmapImage Picture { get; set; } private string Url { get; set; } public DownloadPicture(string _url) { Url = _url; } /// <summary> /// Начинает загрузку изображения /// </summary> public void Load() { WebRequest request = WebRequest.Create(Url); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); Stream pic = response.GetResponseStream(); byte[] img; using (MemoryStream ms = new MemoryStream()) { short tmp = (short)pic.ReadByte(); while (tmp != -1) { ms.WriteByte((byte)tmp); tmp = (short)pic.ReadByte(); } img = ms.ToArray(); } using (MemoryStream ms = new MemoryStream(img)) { JpegBitmapDecoder decoder = new JpegBitmapDecoder(ms, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); BitmapSource bmpSource = decoder.Frames[0]; JpegBitmapEncoder encoder = new JpegBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(bmpSource)); using (MemoryStream ms2 = new MemoryStream()) { encoder.Save(ms2); Picture = new BitmapImage(); Picture.BeginInit(); Picture.StreamSource = new MemoryStream(ms2.ToArray()); Picture.EndInit(); } } } } } Главное окно: Code: using System.Threading; using System.Threading.Tasks; using System.Windows; namespace JPGTest { /// <summary> /// Логика взаимодействия для MainWindow.xaml /// </summary> public partial class MainWindow : Window { public DownloadPicture dp { get; set; } public MainWindow() { InitializeComponent(); dp = new DownloadPicture(@"http://i43.fastpic.ru/big/2012/0824/6d/3f13eaad98a8800fe71cc180915b4c6d.jpg"); } private void Window_Loaded_1(object sender, RoutedEventArgs e) { Task tsk = new Task(SetPicture); tsk.Start(); } public void SetPicture() { dp.Load(); this.Dispatcher.BeginInvoke((ThreadStart)delegate() { this.viewImg.Source = dp.Picture; }); Thread.Sleep(20); } } } XAML код: Code: <Window x:Class="JPGTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded_1"> <Grid> <Image HorizontalAlignment="Left" Height="291" Margin="10,10,0,0" VerticalAlignment="Top" Width="489" x:Name="viewImg"/> </Grid> </Window>
Для начала Exception в студио! Task? Почему Task,а не Thread? Code: this.Dispatcher.BeginInvoke((ThreadStart)delegate( ) { this.viewImg.Source = dp.Picture; }); Что за масло маслянное? Советую ознакомиться
Вот оно: Code: System.InvalidOperationException не обработано пользовательским кодом HResult=-2146233079 Message=Вызывающий поток не может получить доступ к данному объекту, так как владельцем этого объекта является другой поток. Source=WindowsBase StackTrace: в System.Windows.Threading.Dispatcher.VerifyAccess() в System.Windows.Freezable.get_IsFrozen() в System.Windows.Controls.Image.UpdateBaseUri(DependencyObject d, ImageSource source) в System.Windows.Controls.Image.OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) в System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e) в System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e) в System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args) в System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType) в System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal) в System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value) в System.Windows.Controls.Image.set_Source(ImageSource value) в JPGTest.MainWindow.<SetPicture>b__0() в c:\Users\Geass\Documents\Visual Studio 2012\Projects\JPGTest\JPGTest\MainWindow.xaml.cs:строка 28 InnerException: Task мне как то более удобным кажется. Вот чего не знаю, того не знаю.
кажется VY_CMa, привел верный пример - почитай. Если в кратце, то к UI елементам нельзя обращаться из второстепенных потоков, так как все гуй елементы находятся в главном. Для этого есть костыли - Backgroundworker P.S. смени язык, а то ссылка на немецкую версию.
Тем не менее простейшие приложения прекрасно работают и обращаются к контролам из других потоков. Пример компилирующегося и работающего приложения: XAML: Code: <Window x:Class="Test.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded_1"> <Grid> <TextBox x:Name="TextBox1" HorizontalAlignment="Left" Height="291" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="489"/> </Grid> </Window> Код главного окна программы: Code: using System.Threading; using System.Threading.Tasks; using System.Windows; namespace Test { /// <summary> /// Логика взаимодействия для MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Window_Loaded_1(object sender, RoutedEventArgs e) { Task tsk = Task.Factory.StartNew(() => { for (int i = 0; i < 100; i++) { Dispatcher.Invoke(() => TextBox1.Text += i + " "); Thread.Sleep(500); } }); } } } Вполне неплохо создается еще одна задача, которая дописывает циферки в контрол TextBox, созданный в главном потоке.
Потому что они используют синхронизацию для этого, все Invoke,BeginInvoke,Dispatcher фактически и есть синхронизация, использую их грубо говоря ты исполняешь код в контексте потока их создавшего. ПС и спасибо за пример я действительно не знал что так круто можно, вау, и на http://msdn.microsoft.com меня как и тебя тоже забанили.
А суть оказалась в том, что в классе DownloadPicture объект BitmapImage является Freezable объектом, и, соответственно, после его заполнения нужно было вызвать метод Freeze() для этого объекта. MSDN почему-то упустла из вида этот факт. Code: using (MemoryStream ms2 = new MemoryStream()) { encoder.Save(ms2); Picture = new BitmapImage(); Picture.BeginInit(); Picture.StreamSource = new MemoryStream(ms2.ToArray()); Picture.EndInit(); } [B]Picture.Freeze();[/B]