using System; using System.Collections.Generic; using System.Text; using System.Drawing; using BrightIdeasSoftware; using System.Drawing.Drawing2D; using System.Windows.Forms; using System.IO; using System.Net; using System.Security.Cryptography; using System.Threading; using System.Diagnostics; namespace MovieFinder.Client { /// /// Hackish renderer that draw a fancy version of a person for a Tile view. /// /// This is not the way to write a professional level renderer. /// It is hideously inefficient (we should at least cache the images), /// but it is obvious internal class BusinessCardRenderer : AbstractRenderer { #region Fields private static Queue imagedUrls = new Queue(); private static Thread workerThread = new Thread(ImageDowloadWorker); private static volatile bool stopThread; #endregion #region Constructor static BusinessCardRenderer() { if (!Directory.Exists(Movie.ImgDirectory)) Directory.CreateDirectory(Movie.ImgDirectory); workerThread.IsBackground = true; workerThread.Start(); } #endregion #region Methods public override bool RenderItem(DrawListViewItemEventArgs e, Graphics g, Rectangle itemBounds, object rowObject) { // If we're in any other view than Tile, return false to say that we haven't done // the rendereing and the default process should do it's stuff ObjectListView olv = e.Item.ListView as ObjectListView; if (olv == null || olv.View != View.Tile) return false; // Use buffered graphics to kill flickers BufferedGraphics buffered = BufferedGraphicsManager.Current.Allocate(g, itemBounds); g = buffered.Graphics; g.Clear(olv.BackColor); g.SmoothingMode = ObjectListView.SmoothingMode; g.TextRenderingHint = ObjectListView.TextRenderingHint; if (e.Item.Selected) { this.BorderPen = Pens.Blue; this.HeaderBackBrush = new SolidBrush(olv.HighlightBackgroundColorOrDefault); } else { this.BorderPen = new Pen(Color.FromArgb(0x33, 0x33, 0x33)); this.HeaderBackBrush = new SolidBrush(Color.FromArgb(0x33, 0x33, 0x33)); } DrawBusinessCard(g, itemBounds, rowObject, olv, (OLVListItem)e.Item); // Finally render the buffered graphics buffered.Render(); buffered.Dispose(); // Return true to say that we've handled the drawing return true; } internal Pen BorderPen = new Pen(Color.FromArgb(0x33, 0x33, 0x33)); internal Brush TextBrush = new SolidBrush(Color.FromArgb(0x22, 0x22, 0x22)); internal Brush HeaderTextBrush = Brushes.AliceBlue; internal Brush HeaderBackBrush = new SolidBrush(Color.FromArgb(0x33, 0x33, 0x33)); internal Brush BackBrush = Brushes.LemonChiffon; public void DrawBusinessCard(Graphics g, Rectangle itemBounds, object rowObject, ObjectListView olv, OLVListItem item) { try { const int spacing = 8; // Allow a border around the card itemBounds.Inflate(-2, -2); // Draw card background const int rounding = 20; GraphicsPath path = this.GetRoundedRect(itemBounds, rounding); g.FillPath(this.BackBrush, path); g.DrawPath(this.BorderPen, path); g.Clip = new Region(itemBounds); // Draw the photo Rectangle photoRect = itemBounds; photoRect.Inflate(-spacing, -spacing); Movie movie = rowObject as Movie; if (movie != null) { photoRect.Width = 200; if (!File.Exists(movie.ImagePath)) DownloadImage(movie); if (IsFileExists(movie.ImagePath)) { try { using (var photo = Image.FromFile(movie.ImagePath)) { if (photo.Width > photoRect.Width) photoRect.Height = (int)(photo.Height * ((float)photoRect.Width / photo.Width)); else photoRect.Height = photo.Height; g.DrawImage(photo, photoRect); } } catch { g.DrawRectangle(Pens.DarkGray, photoRect); File.Delete(movie.ImagePath); } } else { g.DrawRectangle(Pens.DarkGray, photoRect); } } // Now draw the text portion RectangleF textBoxRect = photoRect; textBoxRect.X += (photoRect.Width + spacing); textBoxRect.Width = itemBounds.Right - textBoxRect.X - spacing; using (StringFormat fmt = new StringFormat()) { fmt.Trimming = StringTrimming.EllipsisCharacter; fmt.Alignment = StringAlignment.Center; fmt.LineAlignment = StringAlignment.Near; String txt = item.Text; using (Font font = new Font("Tahoma", 11)) { // Measure the height of the title SizeF size = g.MeasureString(txt, font, (int)textBoxRect.Width, fmt); // Draw the title RectangleF r3 = textBoxRect; r3.Height = size.Height; path = this.GetRoundedRect(r3, 15); g.FillPath(this.HeaderBackBrush, path); g.DrawString(txt, font, this.HeaderTextBrush, textBoxRect, fmt); textBoxRect.Y += size.Height + spacing; } // Draw the other bits of information using (Font font = new Font("Tahoma", 8)) { for (int i = 0; i < olv.Columns.Count; i++) { OLVColumn column = olv.GetColumn(i); if (column.IsTileViewColumn) { SizeF size = g.MeasureString(column.GetStringValue(movie), font, (int)textBoxRect.Width, fmt); textBoxRect.Height = size.Height; fmt.Alignment = StringAlignment.Near; txt = column.GetStringValue(rowObject); g.DrawString(txt, font, this.TextBrush, textBoxRect, fmt); textBoxRect.Y += size.Height ; } } } } } catch (Exception ex) { Debug.WriteLine("This exception: " + ex.Message); } } private GraphicsPath GetRoundedRect(RectangleF rect, float diameter) { GraphicsPath path = new GraphicsPath(); RectangleF arc = new RectangleF(rect.X, rect.Y, diameter, diameter); path.AddArc(arc, 180, 90); arc.X = rect.Right - diameter; path.AddArc(arc, 270, 90); arc.Y = rect.Bottom - diameter; path.AddArc(arc, 0, 90); arc.X = rect.Left; path.AddArc(arc, 90, 90); path.CloseFigure(); return path; } private bool IsFileExists(string file) { try { if (File.Exists(file)) { using (var currentWriteableFile = File.OpenWrite(file)) { } } } catch { return false; } return true; } private void DownloadImage(Movie movie) { if (String.IsNullOrEmpty(movie.ImagePath)) return; if (!File.Exists(movie.ImagePath)) { lock (imagedUrls) { imagedUrls.Enqueue(movie); } } } private static void ImageDowloadWorker() { while (!stopThread) { try { Movie movie = null; lock (imagedUrls) { if (imagedUrls.Count > 0) movie = imagedUrls.Dequeue(); else Thread.Sleep(100); } if (movie != null && !String.IsNullOrEmpty(movie.ImagePath) && !File.Exists(movie.ImagePath)) { using (var client = new WebClient()) { try { client.DownloadFile(movie.ImageUrl, movie.ImagePath); } catch { } } } } catch { } } } public static void StopThread() { stopThread = true; workerThread.Join(5000); } #endregion } /// /// This simple class just shows how an overlay can be drawn when the hot item changes. /// internal class BusinessCardOverlay : AbstractOverlay { public BusinessCardOverlay() { businessCardRenderer.HeaderBackBrush = Brushes.DarkBlue; businessCardRenderer.BorderPen = new Pen(Color.DarkBlue, 2); this.Transparency = 255; } #region IOverlay Members public override void Draw(ObjectListView olv, Graphics g, Rectangle r) { if (olv.HotRowIndex < 0) return; if (olv.View == View.Tile) return; OLVListItem item = olv.GetItem(olv.HotRowIndex); if (item == null) return; Size cardSize = new Size(400, 250); Rectangle cardBounds = new Rectangle( r.Right - cardSize.Width - 8, r.Bottom - cardSize.Height - 20, cardSize.Width, cardSize.Height); businessCardRenderer.DrawBusinessCard(g, cardBounds, item.RowObject, olv, item); } #endregion private readonly BusinessCardRenderer businessCardRenderer = new BusinessCardRenderer(); } }