OSDN Git Service

ErrorImage/InitialImageの表示にはPictureBoxSizeMode.CenterImageを使う
[opentween/open-tween.git] / OpenTween / OTPictureBox.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2013 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
4 //
5 // This file is part of OpenTween.
6 //
7 // This program is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General Public License as published by the Free
9 // Software Foundation; either version 3 of the License, or (at your option)
10 // any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 // for more details.
16 //
17 // You should have received a copy of the GNU General Public License along
18 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
19 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 // Boston, MA 02110-1301, USA.
21
22 using System;
23 using System.Collections.Generic;
24 using System.Linq;
25 using System.Text;
26 using System.Windows.Forms;
27 using System.ComponentModel;
28 using System.Drawing;
29 using System.Threading.Tasks;
30 using System.Threading;
31 using System.Net;
32 using System.IO;
33
34 namespace OpenTween
35 {
36     public class OTPictureBox : PictureBox
37     {
38         [Localizable(true)]
39         public new Image Image
40         {
41             get { return base.Image; }
42             set
43             {
44                 base.Image = value;
45                 this.SizeMode = this._SizeMode;
46                 if (this.memoryImage != null)
47                 {
48                     this.memoryImage.Dispose();
49                     this.memoryImage = null;
50                 }
51             }
52         }
53
54         [Localizable(true)]
55         public new string ImageLocation
56         {
57             get { return this._ImageLocation; }
58             set
59             {
60                 if (value == null)
61                 {
62                     this.Image = null;
63                     return;
64                 }
65                 this.LoadAsync(value);
66             }
67         }
68         private string _ImageLocation;
69
70         /// <summary>
71         /// 画像に応じた SizeMode を取得・設定する
72         /// </summary>
73         /// <remarks>
74         /// ErrorImage と InitialImage は SizeMode の値に依らず中央等倍に表示する必要があるため、
75         /// 画像に応じて SizeMode の状態を弄る
76         /// </remarks>
77         public new PictureBoxSizeMode SizeMode
78         {
79             get { return this._SizeMode; }
80             set
81             {
82                 this._SizeMode = value;
83                 if (base.Image == null || (base.Image != base.ErrorImage && base.Image != base.InitialImage))
84                 {
85                     base.SizeMode = value;
86                 }
87                 else
88                 {
89                     base.SizeMode = PictureBoxSizeMode.CenterImage;
90                 }
91             }
92         }
93         private PictureBoxSizeMode _SizeMode;
94
95         /// <summary>
96         /// ImageLocation によりロードされた画像
97         /// </summary>
98         private MemoryImage memoryImage = null;
99
100         private Task loadAsyncTask = null;
101         private CancellationTokenSource loadAsyncCancelTokenSource = null;
102
103         public new Task LoadAsync(string url)
104         {
105             this._ImageLocation = url;
106
107             if (this.loadAsyncTask != null && !this.loadAsyncTask.IsCompleted)
108                 this.CancelAsync();
109
110             this.Image = base.InitialImage;
111
112             Uri uri;
113             try
114             {
115                 uri = new Uri(url);
116             }
117             catch (UriFormatException)
118             {
119                 uri = new Uri(Path.GetFullPath(url));
120             }
121
122             var client = new OTWebClient();
123
124             client.DownloadProgressChanged += (s, e) =>
125             {
126                 this.OnLoadProgressChanged(new ProgressChangedEventArgs(e.ProgressPercentage, e.UserState));
127             };
128
129             this.loadAsyncCancelTokenSource = new CancellationTokenSource();
130             var cancelToken = this.loadAsyncCancelTokenSource.Token;
131             var loadImageTask = client.DownloadDataAsync(uri, cancelToken);
132
133             // UnobservedTaskException イベントを発生させないようにする
134             loadImageTask.ContinueWith(t => { var ignore = t.Exception; }, TaskContinuationOptions.OnlyOnFaulted);
135
136             var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
137
138             return loadImageTask.ContinueWith(t => {
139                 client.Dispose();
140
141                 if (t.IsFaulted) throw t.Exception;
142
143                 return MemoryImage.CopyFromBytes(t.Result);
144             }, cancelToken)
145             .ContinueWith(t =>
146             {
147                 if (!t.IsCanceled)
148                 {
149                     if (t.IsFaulted)
150                     {
151                         this.Image = base.ErrorImage;
152                     }
153                     else
154                     {
155                         this.Image = t.Result.Image;
156                         this.memoryImage = t.Result;
157                     }
158                 }
159
160                 var exp = t.Exception != null ? t.Exception.Flatten() : null;
161                 this.OnLoadCompleted(new AsyncCompletedEventArgs(exp, t.IsCanceled, null));
162             }, uiScheduler);
163         }
164
165         public new void CancelAsync()
166         {
167             if (this.loadAsyncTask == null || this.loadAsyncTask.IsCompleted)
168                 return;
169
170             this.loadAsyncCancelTokenSource.Cancel();
171
172             try
173             {
174                 this.loadAsyncTask.Wait();
175             }
176             catch (AggregateException ae)
177             {
178                 ae.Handle(e =>
179                 {
180                     if (e is OperationCanceledException)
181                         return true;
182                     if (e is WebException)
183                         return true;
184
185                     return false;
186                 });
187             }
188         }
189
190         protected override void Dispose(bool disposing)
191         {
192             base.Dispose(disposing);
193
194             if (this.memoryImage != null)
195                 this.memoryImage.Dispose();
196
197             if (this.loadAsyncCancelTokenSource != null)
198                 this.loadAsyncCancelTokenSource.Dispose();
199         }
200     }
201 }