Silverlight 3: 3D ImageGrid erstellen

Ich habe vor einigen Tagen eine sehr nette Anwendung zum Betrachten von Fotos im Browser gesehen. Der Nachtteil bei der Anwendung war allerdings, das man vorher ein spezielles Plug-In (weder Flash noch Silverlight) installieren musste um sich das ganze zu Betrachten. Außerdem war es sehr groß … :(

Das geht leichter und einfacher und noch viel dynamischer dachte ich mir und habe deshalb mal ein eigenes Container-Control (ImageGrid) erstellt.

image

Schritt 1: Ableiten von der richtigen Klasse –> Panel

    1: public class ImageGrid : Panel
    2: {
    3:     protected override Size ArrangeOverride(Size finalSize)
    4:     {
    5:         double imageWidth = (double)this.GetValue(ImageWidthProperty);
    6:         double imageHeight = (double)this.GetValue(ImageHeightProperty);
    7:         int rowsPerColumn = (int)this.GetValue(RowsPerColumnProperty);
    8:         double imageMargin = (double)this.GetValue(ImageMarginProperty);
    9:  
   10:         int currentRow = 0;
   11:         int currentColumn = 0;
   12:  
   13:         foreach (Image image in this.Children.OfType<Image>())
   14:         {
   15:             Hover3DBehavior behavior = new Hover3DBehavior();
   16:             behavior.ZHoverValue = (double)this.GetValue(ZHoverValueProperty);
   17:             behavior.Attach(image);
   18:  
   19:             if ((bool)this.GetValue(ShowShadowProperty))
   20:             {
   21:                 DropShadowEffect ds = new DropShadowEffect();
   22:                 ds.Opacity = .3;
   23:                 ds.BlurRadius = 10.0;
   24:                 ds.Direction = 0.0;
   25:                 ds.ShadowDepth = 20.0;
   26:                 image.Effect = ds;
   27:             }
   28:  
   29:             Rect rect = new Rect(
   30:                 currentColumn * (imageWidth + imageMargin),
   31:                 currentRow * (imageHeight + imageMargin),
   32:                 imageWidth,
   33:                 imageHeight);
   34:  
   35:             image.Width = imageWidth;
   36:             image.Height = imageHeight;
   37:             image.Arrange(rect);
   38:             image.InvalidateArrange();
   39:  
   40:             currentRow++;
   41:             if (currentRow >= rowsPerColumn)
   42:             {
   43:                 currentRow = 0;
   44:                 currentColumn++;
   45:             }
   46:         }
   47:  
   48:         Size newSize = new Size((currentColumn+1) * (imageWidth + imageMargin), rowsPerColumn * (imageHeight+imageMargin));
   49:  
   50:         return base.ArrangeOverride(newSize);
   51:     }
   52: }

Schritt 2: Erweitern mit netten Eigenschaften zum Konfigurieren

    1: private const int C_DefaultRowsPerColumn = 5;
    2: private const double C_DefaultImageWidth = 160.0;
    3: private const double C_DefaultImageHeight = 120.0;
    4: private const double C_DefaultZHover = 200.0;
    5: private const bool C_DefaultShowShadow = true;
    6: private const double C_DefaultImageMargin = 8.0;
    7:  
    8: #region Images per column
    9:  
   10: public int RowsPerColumn
   11: {
   12:     get { return (int)GetValue(RowsPerColumnProperty); }
   13:     set { SetValue(RowsPerColumnProperty, value); }
   14: }
   15:  
   16: public static readonly DependencyProperty RowsPerColumnProperty =
   17:         DependencyProperty.Register("RowsPerColumn", typeof(int),
   18:         typeof(ImageGrid),
   19:         new PropertyMetadata(C_DefaultRowsPerColumn,
   20:         new PropertyChangedCallback(OnImageSizeChanged)));
   21:  
   22: #endregion
   23:  
   24: #region Image Width
   25:  
   26: public double ImageWidth
   27: {
   28:     get { return (double)GetValue(ImageWidthProperty); }
   29:     set { SetValue(ImageWidthProperty, value); }
   30: }
   31:  
   32: public static readonly DependencyProperty ImageWidthProperty =
   33:         DependencyProperty.Register("ImageWidth", typeof(double),
   34:         typeof(ImageGrid),
   35:         new PropertyMetadata(C_DefaultImageWidth,
   36:         new PropertyChangedCallback(OnImageSizeChanged)));
   37:  
   38: #endregion
   39:  
   40: #region Image Margin
   41:  
   42: public double ImageMargin
   43: {
   44:     get { return (double)GetValue(ImageMarginProperty); }
   45:     set { SetValue(ImageMarginProperty, value); }
   46: }
   47:  
   48: public static readonly DependencyProperty ImageMarginProperty =
   49:         DependencyProperty.Register("ImageMargin", typeof(double),
   50:         typeof(ImageGrid),
   51:         new PropertyMetadata(C_DefaultImageMargin,
   52:         new PropertyChangedCallback(OnImageSizeChanged)));
   53:  
   54: #endregion
   55:  
   56: #region Image Height
   57:  
   58: public double ImageHeight
   59: {
   60:     get { return (double)GetValue(ImageHeightProperty); }
   61:     set { SetValue(ImageHeightProperty, value); }
   62: }
   63:  
   64: public static readonly DependencyProperty ImageHeightProperty =
   65:         DependencyProperty.Register("ImageHeight", typeof(double),
   66:         typeof(ImageGrid),
   67:         new PropertyMetadata(C_DefaultImageHeight,
   68:         new PropertyChangedCallback(OnImageSizeChanged)));
   69:  
   70: protected static void OnImageSizeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
   71: {
   72:     ImageGrid i = obj as ImageGrid;
   73:     i.InvalidateArrange();
   74: }
   75:  
   76: #endregion
   77:  
   78: #region ZHoverValue 
   79:  
   80: public double ZHoverValue
   81: {
   82:     get { return (double)GetValue(ZHoverValueProperty); }
   83:     set { SetValue(ZHoverValueProperty, value); }
   84: }
   85:  
   86: public static readonly DependencyProperty ZHoverValueProperty =
   87:     DependencyProperty.Register("ZHoverValue", typeof(double), typeof(ImageGrid),
   88:     new PropertyMetadata((double)C_DefaultZHover, null));
   89:  
   90: #endregion
   91:  
   92: #region Show Shadow
   93:  
   94: public bool ShowShadow
   95: {
   96:     get { return (bool)GetValue(ShowShadowProperty); }
   97:     set { SetValue(ShowShadowProperty, value); }
   98: }
   99:  
  100: public static readonly DependencyProperty ShowShadowProperty =
  101:     DependencyProperty.Register("ShowShadow", typeof(bool), typeof(ImageGrid),
  102:     new PropertyMetadata((bool)C_DefaultShowShadow, new PropertyChangedCallback(OnImageSizeChanged)));
  103:  
  104: #endregion

Schritt 4: Erstellen eines Hover-Effektes mithilfe eines Behaviors

    1: public class Hover3DBehavior : Behavior<UIElement>
    2: {
    3:     PlaneProjection _planeProjection;
    4:  
    5:     public Hover3DBehavior()
    6:         : base()
    7:     {
    8:        
    9:     }
   10:  
   11:     Storyboard _hoverMe;
   12:     Storyboard _unhoverMe;
   13:  
   14:     private const double C_DefaultHoverValue = 500.0;
   15:  
   16:     #region ZHoverValue 
   17:  
   18:     public double ZHoverValue
   19:     {
   20:         get { return (double)GetValue(ZHoverValueProperty); }
   21:         set { SetValue(ZHoverValueProperty, value); }
   22:     }
   23:  
   24:     public static readonly DependencyProperty ZHoverValueProperty =
   25:         DependencyProperty.Register("ZHoverValue", typeof(double), typeof(Hover3DBehavior),
   26:         new PropertyMetadata((double)C_DefaultHoverValue, null));
   27:  
   28:     #endregion
   29:  
   30:     #region Overrides 
   31:  
   32:     protected override void OnAttached()
   33:     {
   34:         base.OnAttached();
   35:  
   36:         _planeProjection = new PlaneProjection();
   37:         this.AssociatedObject.Projection = _planeProjection;
   38:  
   39:         if (!DesignerProperties.GetIsInDesignMode(this))
   40:         {
   41:             _hoverMe = new Storyboard();
   42:  
   43:             DoubleAnimation da1 = new DoubleAnimation();
   44:  
   45:             _hoverMe.Children.Add(da1);
   46:             da1.Duration = new Duration(new System.TimeSpan(0, 0, 0, 0, 300));
   47:             da1.To = this.ZHoverValue;
   48:             Storyboard.SetTarget(da1, this.AssociatedObject);
   49:             Storyboard.SetTargetProperty(da1, new PropertyPath("(UIElement.Projection).(PlaneProjection.LocalOffsetZ)"));
   50:  
   51:             DoubleAnimation da2 = new DoubleAnimation();
   52:             BounceEase be2 = new BounceEase();
   53:             be2.EasingMode = EasingMode.EaseOut;
   54:             be2.Bounces = 3;
   55:             da2.EasingFunction = be2;
   56:  
   57:             _unhoverMe = new Storyboard();
   58:             _unhoverMe.Children.Add(da2);
   59:             da2.Duration = new Duration(new System.TimeSpan(0, 0, 0, 0, 1000));
   60:             da2.To = 0.0;
   61:             Storyboard.SetTarget(da2, this.AssociatedObject);
   62:             Storyboard.SetTargetProperty(da2, new PropertyPath("(UIElement.Projection).(PlaneProjection.LocalOffsetZ)"));
   63:  
   64:             if ((this.AssociatedObject as FrameworkElement).Resources.Contains("hoverme"))
   65:             {
   66:                 (this.AssociatedObject as FrameworkElement).Resources.Remove("hoverme");
   67:             }
   68:             (this.AssociatedObject as FrameworkElement).Resources.Add("hoverme", _hoverMe);
   69:             
   70:             if ((this.AssociatedObject as FrameworkElement).Resources.Contains("unhoverme"))
   71:             {
   72:                 (this.AssociatedObject as FrameworkElement).Resources.Remove("unhoverme");
   73:             }
   74:             (this.AssociatedObject as FrameworkElement).Resources.Add("unhoverme", _unhoverMe);
   75:         }
   76:         
   77:         this.AssociatedObject.MouseEnter += new MouseEventHandler(AssociatedObject_MouseEnter);
   78:         this.AssociatedObject.MouseLeave += new MouseEventHandler(AssociatedObject_MouseLeave);
   79:     }
   80:  
   81:     protected override void OnDetaching()
   82:     {
   83:         base.OnDetaching();
   84:  
   85:         this.AssociatedObject.Projection = null;
   86:         _planeProjection = null;
   87:  
   88:         this.AssociatedObject.MouseEnter -= new MouseEventHandler(AssociatedObject_MouseEnter);
   89:         this.AssociatedObject.MouseLeave -= new MouseEventHandler(AssociatedObject_MouseLeave);
   90:     }
   91:  
   92:     #endregion
   93:  
   94:     #region Events
   95:  
   96:     void AssociatedObject_MouseLeave(object sender, MouseEventArgs e)
   97:     {
   98:         _unhoverMe.Begin();
   99:     }
  100:  
  101:     void AssociatedObject_MouseEnter(object sender, MouseEventArgs e)
  102:     {
  103:         _hoverMe.Begin();
  104:     }
  105:  
  106:     #endregion
  107:  
  108: }

Schritt 3: Verwenden in Blend

Das ImageGrid nach dem ich einige Beispielbilder reingesetzt habe.

image

Der Arbeitsbereich

image

Der VisualTree.

 image

Die zusätzlichen Konfigurationsmöglichkeiten.