DOWLOAD LINK http://turbobit.net/i2ddhdnostop.html
System Requirements
If you already have Visual Studio 2010, that's enough run the application. If you don't, you can download the following 100% free development tool directly from Microsoft:
The Bricks Solution
Figure 1: Solution structure
The Visual Studio 2010 solution is made up by three projects: Bricks.Silverlight, Bricks.Silverlight.Core andBricks.Silverlight.Web, as we can see in the following table:
| Project | Description | 
| Bricks.Silverlight | This is the Silverlight project itself, containing the UI and the Model-View-ViewModel logic. | 
| Bricks.Silverlight.Core | This Silverlight Class Library project holds the core of the Bricks!funcionality. | 
| Bricks.Silverlight.Web | This project is the start up project that contains the application's entry page, besides some resource files. | 
Intro Menu
The Intro Menu consists of the Bricks "logo" and some game instructions.Figure 2: Intro Menu
The MVVM Pattern
In this project I used the Model-View-ViewModel (MVVM) pattern. As some of the readers may know, this pattern originated with WPF and Silverlight technologies, and, as a rough explanation, in MVVM the user interface (composed by "views", residing in the .XAML/.XAML.cs files) "hands over" control to a set of generic classes, called "ViewModels", so that any interacions from the user part on the View side reflects on the underlying ViewModel classes, and vice-versa.As we can see from the Figure 3 below, the View doesn't access the data directly, because instead it relies on the bindings provided by its ViewModel counterpart. The binding is the glue that holds View and ViewModel together. As an example, the score
TextBlock which represents the Score value and is named "txtScore" in a View has a binding to a Int32 property named "Score" in the ViewModel. Any changes to the "Score" property reflects back in the txtScoreelement on the View side. On the other hand, a Button element named "Start" has a binding to an ICommandproperty named "StartCommand" in the ViewModel, so that any time the user clicks the button would automatically invoke the DoStart() method on the ViewModel side.Figure 3: Basic structure of the MVVM Pattern
The complete binding mappings are described in the table below:
| View (BricksView) | ViewModel (BricksViewModel) | ||||
| Visual Element | Type | Property | Bound Property | Type | Converter | 
| pnlIntro | StackPanel | Visibility | IsIntroVisible | bool | BooleanToVisibilityConverter | 
| pnlGameOver | StackPanel | Visibility | IsGameOverVisible | bool | BooleanToVisibilityConverter | 
| pnlGamePaused | StackPanel | Visibility | IsGamePausedVisible | bool | BooleanToVisibilityConverter | 
| txtScore | TextBlock | Text | Score | int | (none) | 
| txtHiScore | TextBlock | Text | HiScore | int | (none) | 
| txtLines | TextBlock | Text | Lines | int | (none) | 
| txtLevel | TextBlock | Text | Level | int | (none) | 
| lstBoard | ListBox | ItemsSource | Bricks | ObservableNotifiableCollection<IBrick> | (none) | 
| lstNext | ListBox | ItemsSource | Level | ObservableNotifiableCollection<IBrick> | (none) | 
| btnStart | Button | ButtonService.Command | StartCommand | ICommand | (none) | 
| btnStart | Button | IsEnabled | StartCanExecute | bool | (none) | 
| btnPause | Button | ButtonService.Command | PauseCommand | ICommand | (none) | 
| btnPause | Button | IsEnabled | PauseCanExecute | bool | (none) | 
| btnStop | Button | ButtonService.Command | StopCommand | ICommand | (none) | 
| btnStop | Button | IsEnabled | StopCanExecute | bool | (none) | 
- Bindings to panel visibility: there are 3 panels, pnlIntro,pnlGameOverandpnlGamePausedwhich can be visible or invisible, depending on the values of the correspondingboolproperties on the ViewModel side. The problem is that theVisibilityproperty is not a boolean, but rather an enumeration, which can beVisibleorCollapsed, and there is no automatic conversion betweenboolandVisibility. This is why we use theBooleanToVisibilityConverter, to map the values correctly. And here comes the main function of theBooleanToVisibilityConverterhelper class:public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { Visibility rv = Visibility.Visible; try { var x = bool.Parse(value.ToString()); if (x) { rv = Visibility.Visible; } else { rv = Visibility.Collapsed; } } catch (Exception) { } return rv; } 
- Bindings to int: In this case the conversion of int values to the Text properties of the txtScore, txtHiScore, txtLines, txtLevel visual elements is done automatically. Notice that in the setter below we have to use theOnPropertyChanged("Score") call, so that the View knows that the property was updated, and then updates itself accordingly.public int Score { get { return score; } set { score = value; OnPropertyChanged("Score"); } } 
- Bindings to list boxes: The Listboxclass has an ItemsSource property, that could be bound to aCollectionproperty. But instead, we bind it to aObservableNotifiableCollectionproperty, so that the Listbox can both listen to changes in the collection item list and changes in the item properties inside the list. The snippet below shows that the implementation on the ViewModel side is quite simple:public ObservableNotifiableCollection<IBrick>> Bricks { get { return bricks; } set { bricks = value; } } 
- Bindings to buttons: In this case, we bind 2 properties: One of them is the IsEnabledproperty, bound to simpleboolproperties in the ViewModel side, and the second is theButtonService.Commandattached property. As some readers may know, there are some techniques that could be used to bind commands, but I prefer to use theButtonServicehelper class, proposed initially by Patrick Cauldwell in his blog. I like it specially because of its simplicity, as we can see in the example below:public ICommand StartCommand { get { return new RelayCommand(() => DoStart()); } } public bool StartCanExecute { get { return startCanExecute; } set { startCanExecute = value; OnPropertyChanged("StartCanExecute"); } } 
View and ViewModel interact with each other: some of the visual elements in the View have bound properties in the ViewModel side:Figure 4: How the View and ViewModel counterparts interact with each other.
But wait a moment! Have you noticed that those 2 boards are, in fact, 2 listboxes? Have you also noticed that these listboxes are mapped to collections? More specifically, they are bound to properties in the ViewModel, of the type
ObservableNotifiableCollection<IBrick>.The Power of Styles and Templates
At the core of the game UI engine lays the MVVM pattern with all the binding mechanism. But what really really enables the MVVM with this game is the ability to transform an ordinaryListbox into a 10 x 16 rectangular board to hold our bricks.The template I used here was inspired by Beatriz Stollnitz's very cool example of transforming a simple
Listbox into a solar system.Figure 5: How Beatriz Stollnitz managed to transform a WPF Listbox into a Solar System through Styles and templating.
After seen this, I realized that it would be much easier to program the game using MVVM (through bindings) than using traditional UI Elements manipulations. Simply put: I wouldn't need to care about positioning the visual elements that represents the bricks (in the view part). I would care only about the underlying, abstract model representation of those bricks. Then the MVVM would do the rest, by updating the view (and hence the Listbox) automatically for me.
The real problem was that Bea Stollnitz had originally written that for WPF, while I wanted to use the same techniques in Silverlight. I reached a point where I couldn't tell if it was feasible or not. So I put my hands on it. The great problem while porting Bea's XAML example to Silverlight was this part:
    <Style TargetType="ListBoxItem">
        <Setter Property="Canvas.Left" Value="{Binding Path=Orbit, Converter={StaticResource convertOrbit}, ConverterParameter=0.707}"/>
        <Setter Property="Canvas.Bottom" Value="{Binding Path=Orbit, Converter={StaticResource convertOrbit}, ConverterParameter=0.707}"/>
        (…)
    </Style>
The XAML above describe the styling/templating for each Planet in Bea's solar system. All I wanted to do was to do the same for Bricks! game, so instead of Planets positioning, I would have Bricks positioning, which in turn would bind to the Left and Top properties on the ViewModel side, like this:    <Style TargetType="ListBoxItem">
        <Setter Property="Canvas.Left" Value="{Binding Path=Left}"/>
        <Setter Property="Canvas.Top" Value="{Binding Path=Top}"/>
        (…)
    </Style>
But have you noticed those "Canvas.Left" and "Canvas.Top" properties inside the ListBoxItem tag? Although these properties are inside the ListBoxItem element, they are the so-called attached properties: actually they are defined in the parent element (that is, Canvas element. The bad news is that, as I had found out, this kind of templating using attached properties simply doesn't work with Silverlight 4. But fortunately for me, after some hours researching the issue, I found a very nice blog post from David Anson of Microsoft, describing a workaround using theSetterValueBindingHelper, that definitely solved te problem!Now that I had the workaround, the solution for the problem above was the snippet below:
            <Setter Property="local:SetterValueBindingHelper.PropertyBinding">
                <Setter.Value>
                    <local:SetterValueBindingHelper>
                        <local:SetterValueBindingHelper
                                Type="System.Windows.Controls.Canvas, System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
                                Property="Left"
                                Binding="{Binding Path=Left, Mode=TwoWay}"/>
                        <local:SetterValueBindingHelper
                                Type="Canvas"
                                Property="Top"
                                Binding="{Binding Path=Top, Mode=TwoWay}"/>
                    </local:SetterValueBindingHelper>
                </Setter.Value>
            </Setter>
        </Style>
Now pay attention to how templating and styling turns a boring Listbox into an exciting, colorful game board:
 Figure 6: How I managed to transform a Silverlight Listbox into the game board through Styles and templating.
Again, many thanks to Bea Stollnitz and David Anson for this!
Figure 6: How I managed to transform a Silverlight Listbox into the game board through Styles and templating.
Again, many thanks to Bea Stollnitz and David Anson for this!
The Bricks Sheet Background
 Figure 7: Bricks Sheet Background
Here you can see the nice background of our game. It's pretty easy to do this with XAML with Visual Studio 2010. In fact, so easy that I didn't feel any need to use Expression Blend in this project. The more you practice with XAML, the more you feel natural to create your designs with XAML, and more productive you become.
Each sheet perforation is represented by an
Figure 7: Bricks Sheet Background
Here you can see the nice background of our game. It's pretty easy to do this with XAML with Visual Studio 2010. In fact, so easy that I didn't feel any need to use Expression Blend in this project. The more you practice with XAML, the more you feel natural to create your designs with XAML, and more productive you become.
Each sheet perforation is represented by an Ellipse element, and each sheet line is made by a cyan Border element with no defined height:
            <StackPanel Width="400" Background="White" HorizontalAlignment="Left" Margin="0,20,0,0">
                <Border BorderBrush="Cyan" BorderThickness="0.5" Margin="0,5"/>
                <Border BorderBrush="Cyan" BorderThickness="0.5" Margin="0,5"/>
                <Border BorderBrush="Cyan" BorderThickness="0.5" Margin="0,5"/>
    .
    .(some lines removed for the sake of readability)
    .
            </StackPanel>
            <StackPanel  Width="400" HorizontalAlignment="Left" Margin="0,20,0,0">
                <Ellipse Width="10" Height="10" HorizontalAlignment="Left" Fill="Black" Margin="5,5"/>
                <Ellipse Width="10" Height="10" HorizontalAlignment="Left" Fill="Black" Margin="5,5"/>
                <Ellipse Width="10" Height="10" HorizontalAlignment="Left" Fill="Black" Margin="5,5"/>
    .
    .(some lines removed for the sake of readability)
    .
  </StackPanel>
Game Logic
The heart of the application logic lays at the BricksBoard class. You can see below a simple diagram showing the relationship between the core classes and the BricksBoard class:
 Figure 8: The Core Class Diagram
For the sake of brevity, we could explain most of the game logic by describing some of the
Figure 8: The Core Class Diagram
For the sake of brevity, we could explain most of the game logic by describing some of the BricksBoard members:
 Figure 9: The BricksBoard Class
Figure 9: The BricksBoard Class
- BricksBoard Constructor: Notice below that the constructor receives an instance of and IPresenterclass. This is so because of the Inversion of Control (IoC) pattern, sometimes called Dependency Injection (DI) pattern:        public BricksBoard(IPresenter presenter)
        {
            this.presenter = presenter;
            this.width = 10;
            this.height = 16;
            InitializeArray();
            next = GetRandomShape();
        }
 Once theBricksBoardis instantiated, an external dependence ofIPresenteris "injected", which in turn will "control" the newBricksBoardinstance (hence "Inversion of Control"). The board size is hard-coded with the 10x16 dimension and the board array is initialized. In the end of the constructor, a new "Next" shape is generated (the "Next" shape defines the shape that will fall from the top of the board after the current shape gets stuck in the the top end of the stack).
- InitializeArray method: This method is responsible for "cleaning" the board and game status. That is, scores are cleared, and board is initialized:
        public override void InitializeArray()
        {
            score = 0;
            level = 1;
            lines = 0;
            if (shape != null)
            {
                shape.Y = 0;
            }
            next = GetRandomShape();
            presenter.UpdateScoreView(score, hiScore, lines, level, next);
            base.InitializeArray();
        }
 The base class InitializeArray() method clears the board by creating an empty 2-dimensional array ofIBrick:        public virtual void InitializeArray()
        {
            shapeArray = new IBrick[width, height];
            for (int row = 0; row < height; row++)
            {
                for (int column = 0; column < width; column++)
                {
                    shapeArray[column, row] = null;
                }
            }
        }
 
- GetRandomShape method: this one randomly generates the nextTetromino that will fall from the board's top. This helps the player to think ahead for next move while dealing with the current move:
        private IShape GetRandomShape()
        {
            IShape newShape = null;
            Random randomClass = new Random();
            int randomCode = randomClass.Next((int)ShapeCodes.I, (int)ShapeCodes.Z + 1);
            switch (randomCode)
            {
                case (int)ShapeCodes.I:
                    newShape = new StickShape();
                    newShape.Color = Colors.Cyan;
                    break;
                case (int)ShapeCodes.J:
                    newShape = new JShape();
                    newShape.Color = Colors.Blue;
                    break;
                case (int)ShapeCodes.L:
                    newShape = new LShape();
                    newShape.Color = Colors.Orange;
                    break;
                case (int)ShapeCodes.O:
                    newShape = new OShape();
                    newShape.Color = Colors.Yellow;
                    break;
                case (int)ShapeCodes.S:
                    newShape = new SShape();
                    newShape.Color = Colors.Green;
                    break;
                case (int)ShapeCodes.T:
                    newShape = new TShape();
                    newShape.Color = Colors.Purple;
                    break;
                case (int)ShapeCodes.Z:
                    newShape = new ZShape();
                    newShape.Color = Colors.Red;
                    break;
            }
            ((BaseShape)newShape).Presenter = presenter;
            presenter.UpdateScoreView(score, hiScore, lines, level, newShape);
            return newShape;
        }
 
- ProcessNextMove method: This method is invoked by the BricksViewModelclass in a timely fashion, depending on the game speed. This is done by theTickevent of the timer in theBricksViewModelclass:        void timer_Tick(object sender, EventArgs e)
        {
            foreach (var b in bricks)
            {
                b.X = b.X;
                b.Y = b.Y;
            }
            presenter.Tick();
        }
 Then the Tick method in theBricksPresenterclass invokes theProcessNextMovemethod:        public void Tick()
        {
            BricksBoard.ProcessNextMove();
        }
 The ProcessNextMove method itself updates the board, placing a new random piece if needed, moving down the current tetromino, if possible, removing the completed rows and updating the score in theViewside, and finishing the current game if the board is already full:        public void ProcessNextMove()
        {
            if (shape == null)
            {
                StartRandomShape();
            }
            bool couldMoveDown = true;
            if (!shape.Anchored)
            {
                RemovePieceFromCurrentPosition(shape);
                couldMoveDown = shape.MoveDown();
            }
            else
            {
                bool full = !StartRandomShape();
                if (full)
                {
                    InitializeArray();
                    GameOver();
                    return;
                }
                else
                {
                    couldMoveDown = shape.MoveDown();
                }
            }
            if (!couldMoveDown)
            {
                RemoveCompletedRows();
            }
            if (presenter != null)
            {
                presenter.UpdateBoardView(GetStringFromShapeArray(), shapeArray, width, height);
            }
        }
 
- StartRandomShape function: This function is called to instantiate a new random tetromino, placing it on the top of the board and in the middle of the board witdh. The function returns true if there's room for a new tetromino, of false otherwise, indicating that the board is full and that the game is over:
        public bool StartRandomShape()
        {
            if (shape != null && !shape.Anchored)
            {
                this.RemovePieceFromCurrentPosition(shape);
            }
            shape = next;
            
            next = GetRandomShape();
            shape.ContainerBoard = this;
            int x = (this.Width - shape.Width) / 2;
            bool ret = this.TestPieceOnPosition(shape, x, 0);
            if (ret)
            {
                this.PutPieceOnPosition(shape, x, 0);
            }
            return ret;
        }
 
- RemovePieceFromCurrentPosition method: this method "detaches" the falling tetromino from its position, clearing the underlying board positions:
        {
            for (int row = 0; row < shape.Height; row++)
            {
                for (int column = 0; column < shape.Width; column++)
                {
                    if (shape.ShapeArray[column, row] != null)
                    {
                        shapeArray[column + shape.X, row + shape.Y] = null;
                    }
                }
            }
        }
- TestPieceOnPosition function: this function tests whether a determined tetromino can be placed at a given position, that is: 1) every tetromino brick must fall in a position inside the board dimensions, and 2) every tetromino must fall in an empty space        public bool TestPieceOnPosition(IShape shape, int x, int y)
        {
            for (int row = 0; row < shape.Height; row++)
            {
                for (int column = 0; column < shape.Width; column++)
                {
                    //is the position out of range?
                    if (column + x < 0)
                        return false;
                    if (row + y < 0)
                        return false;
                    if (column + x >= width)
                        return false;
                    if (row + y >= height)
                        return false;
                    //will the shape collide in the board?
                    if (
                        shapeArray[column + x, row + y] != null &&
                        shape.ShapeArray[column, row] != null)
                    {
                        return false;
                    }
                }
            }
            return true;
        }
 
- PutPieceOnPosition method: updates the board with the current tetromino shape. In the end, theBricksPresenterreceives a notification to update both scores and board.        public void PutPieceOnPosition(IShape shape, int x, int y)
        {
            if (!TestPieceOnPosition(shape, x, y))
                throw new CantSetShapePosition();
            for (int row = 0; row < shape.Height; row++)
            {
                for (int column = 0; column < shape.Width; column++)
                {
                    if (shape.ShapeArray[column, row] != null)
                    {
                        shapeArray[column + x, row + y] = shape.ShapeArray[column, row];
                    }
                }
            }
            shape.X = x;
            shape.Y = y;
            if (presenter != null)
            {
                presenter.UpdateBoardView(GetStringFromShapeArray(), shapeArray, width, height);
            }
        }
 
- RemoveCompletedRows method: At each player's move, the game must test if some horizontal line is completed with bricks. If this is the case, the line is cleared in the board, and the BricksPresenteris updated to reflect the new board configuration. At each row completion, the score is incremented in 10 points. At each 10 rows completed, the level is incremented by 1:        private bool RemoveCompletedRows()
        {
            bool completed = false;
            int row = height - 1;
            while (row >= 0)
            {
                completed = true;
                for (int column = 0; column < width; column++)
                {
                    if (shapeArray[column, row] == null)
                    {
                        completed = false;
                        break;
                    }
                }
                if (completed)
                {
                    IBrick[] removedBricks = new IBrick[width];
                    for (int column = 0; column < width; column++)
                    {
                        removedBricks[column] = shapeArray[column, row];
                    }
                    shape = null;
                    for (int innerRow = row; innerRow > 0; innerRow--)
                    {
                        for (int innerColumn = 0; innerColumn < width; innerColumn++)
                        {
                            shapeArray[innerColumn, innerRow] = shapeArray[innerColumn, innerRow - 1];
                            shapeArray[innerColumn, innerRow - 1] = null;
                        }
                    }
                    score += 10 * level;
                    if (score > hiScore)
                    {
                        hiScore = score;
                    }
                    lines++;
                    level = 1 + (lines / 10);
                    presenter.UpdateScoreView(score, hiScore, lines, level, next);
                }
                else
                {
                    row--;
                }
            }
            if (presenter != null)
            {
                presenter.UpdateBoardView(GetStringFromShapeArray(), shapeArray, width, height);
            }
            if (completed)
            {
                RemoveCompletedRows();
            }
            return completed;
        }
 
- MoveLeft, MoveRight, MoveDown, Rotate90 and Rotate270 functions: these functions just propagates to the moving tetromino, so let's take a look the corresponding methods in the BaseShapeobject. Notice that these methods do just what their names tell, moving the tetramino left, right, down (if possible, of course!), and rotating, that is, transposing columns and rows in an ordered way:        public bool MoveLeft()
        {
            bool test = false;
            if (!anchored)
            {
                if (containerBoard == null)
                    throw new NullContainerBoardException();
                containerBoard.RemovePieceFromCurrentPosition(this);
                test = containerBoard.TestPieceOnPosition(this, this.X - 1, this.Y);
                if (test)
                {
                    containerBoard.RemovePieceFromCurrentPosition(this);
                    containerBoard.PutPieceOnPosition(this, this.X - 1, this.Y);
                }
            }
            return test;
        }
        public bool MoveRight()
        {
            bool test = false;
            if (!anchored)
            {
                if (containerBoard == null)
                    throw new NullContainerBoardException();
                containerBoard.RemovePieceFromCurrentPosition(this);
                test = containerBoard.TestPieceOnPosition(this, this.X + 1, this.Y);
                if (test)
                {
                    containerBoard.PutPieceOnPosition(this, this.X + 1, this.Y);
                }
            }
            return test;
        }
        public bool MoveDown()
        {
            bool test = false;
            if (!anchored)
            {
                containerBoard.RemovePieceFromCurrentPosition(this);
                //should anchor if shape can't move down from current position
                if (!containerBoard.TestPieceOnPosition(this, this.X, this.Y + 1))
                {
                    containerBoard.PutPieceOnPosition(this, this.X, this.Y);
                    this.Anchor();
                }
                else
                {
                    if (containerBoard == null)
                        throw new NullContainerBoardException();
                    test = containerBoard.TestPieceOnPosition(this, this.X, this.Y + 1);
                    if (test)
                    {
                        containerBoard.PutPieceOnPosition(this, this.X, this.Y + 1);
                    }
                }
            }
            return test;
        }
        public bool Rotate90()
        {
            bool test = false;
            if (!anchored)
            {
                if (containerBoard == null)
                    throw new NullContainerBoardException();
                IBrick[,] newShapeArray = new IBrick[height, width];
                IBrick[,] oldShapeArray = new IBrick[width, height];
                for (int row = 0; row < height; row++)
                {
                    for (int column = 0; column < width; column++)
                    {
                        newShapeArray[height - row - 1, column] = shapeArray[column, row];
                        oldShapeArray[column, row] = shapeArray[column, row];
                    }
                }
                containerBoard.RemovePieceFromCurrentPosition(this);
                int w = width;
                int h = height;
                this.width = h;
                this.height = w;
                this.shapeArray = newShapeArray;
                if (containerBoard.TestPieceOnPosition(this, this.X, this.Y))
                {
                    containerBoard.PutPieceOnPosition(this, this.X, this.Y);
                }
                else
                {
                    this.width = w;
                    this.height = h;
                    this.shapeArray = oldShapeArray;
                    containerBoard.PutPieceOnPosition(this, this.X, this.Y);
                }
            }
            return test;
        }
        public bool Rotate270()
        {
            bool test = false;
            if (!anchored)
            {
                if (containerBoard == null)
                    throw new NullContainerBoardException();
                containerBoard.RemovePieceFromCurrentPosition(this);
                IBrick[,] newShapeArray = new IBrick[height, width];
                IBrick[,] oldShapeArray = new IBrick[width, height];
                for (int row = 0; row < height; row++)
                {
                    for (int column = 0; column < width; column++)
                    {
                        newShapeArray[row, width - column - 1] = shapeArray[column, row];
                        oldShapeArray[column, row] = shapeArray[column, row];
                    }
                }
                int w = width;
                int h = height;
                this.width = h;
                this.height = w;
                this.shapeArray = newShapeArray;
                if (containerBoard.TestPieceOnPosition(this, this.X, this.Y))
                {
                    containerBoard.PutPieceOnPosition(this, this.X, this.Y);
                }
                else
                {
                    this.width = w;
                    this.height = h;
                    this.shapeArray = oldShapeArray;
                    containerBoard.PutPieceOnPosition(this, this.X, this.Y);
                }
            }
            return test;
        }
 
Bonus: Creating Funny Brick Animations
This part is not really necessary for the game, but I thought it would add something to the look and feel of the game. At first I was not happy enough with the square, static bricks, so I wanted to create some movements, so that the bricks appeared to be "shaking".
I managed to do this by creating a custom class, called ctlBrick, that inherits from the Grid class. Each ctlBrickinstance represents a different brick on the screen.
        public void GenerateRandomPoints()
        {
            this.Children.Remove(path);
            if (color != Colors.Transparent)
            {
                double h = this.Height;
                double w = this.Width;
                Random rnd = new Random();
                p00 = new Point(2 + rnd.Next(-amplitude, amplitude), 2 + rnd.Next(-amplitude, amplitude));
                p01 = new Point(2 + rnd.Next(-amplitude, amplitude), 1 * h / 4 + rnd.Next(-amplitude, amplitude));
                p02 = new Point(2 + rnd.Next(-amplitude, amplitude), 3 * h / 4 + rnd.Next(-amplitude, amplitude));
                p03 = new Point(2 + rnd.Next(-amplitude, amplitude), -2 + h + rnd.Next(-amplitude, amplitude));
                p30 = new Point(-2 + w + rnd.Next(-amplitude, amplitude), 2 + rnd.Next(-amplitude, amplitude));
                p31 = new Point(-2 + w + rnd.Next(-amplitude, amplitude), 1 * h / 4 + rnd.Next(-amplitude, amplitude));
                p32 = new Point(-2 + w + rnd.Next(-amplitude, amplitude), 3 * h / 4 + rnd.Next(-amplitude, amplitude));
                p33 = new Point(-2 + w + rnd.Next(-amplitude, amplitude), -2 + h + rnd.Next(-amplitude, amplitude));
                p10 = new Point(1 * w / 4 + rnd.Next(-amplitude, amplitude), 2 + rnd.Next(-amplitude, amplitude));
                p20 = new Point(3 * w / 4 + rnd.Next(-amplitude, amplitude), 2 + rnd.Next(-amplitude, amplitude));
                p13 = new Point(1 * w / 4 + rnd.Next(-amplitude, amplitude), -2 + h + rnd.Next(-amplitude, amplitude));
                p23 = new Point(3 * w / 4 + rnd.Next(-amplitude, amplitude), -2 + h + rnd.Next(-amplitude, amplitude));
                var figures = new PathFigureCollection();
                var pathSegmentCollection1 = new PathSegmentCollection();
                var pathSegmentCollection2 = new PathSegmentCollection();
                var pathSegmentCollection3 = new PathSegmentCollection();
                var pathSegmentCollection4 = new PathSegmentCollection();
                PointCollection pointCollection = new PointCollection();
                pointCollection.Add(p10);
                pointCollection.Add(p20);
                pointCollection.Add(p30);
                pointCollection.Add(p31);
                pointCollection.Add(p32);
                pointCollection.Add(p33);
                pointCollection.Add(p23);
                pointCollection.Add(p13);
                pointCollection.Add(p03);
                pointCollection.Add(p02);
                pointCollection.Add(p01);
                pointCollection.Add(p00);
                pathSegmentCollection4.Add(new PolyBezierSegment() { Points = pointCollection });
                figures.Add(new PathFigure() { StartPoint = p00, Segments = pathSegmentCollection4, IsClosed = true });
                path = new Path()
                {
                    Data = new PathGeometry()
                    {
                        Figures = figures
                    },
                    Stroke = new SolidColorBrush(Colors.Black),
                    StrokeThickness = 2,
                    Fill = new SolidColorBrush(color)
                };
                this.Children.Add(path);
            }
        }
The code above is what gives each brick a kind of "shaking" appearance. The "shaking" square is actually composed by aPath element, which contains a PolyBezierSegment class that involves the Grid element. The PolyBezierSegmentas shown above defines a collection of random points that roughly imitate a square, but that slightly moves left, right, up and down, giving an impression of a "shaking square".
Bonus: Templating Funny Buttons
Another nice feature in Silverlight is the one that allows you to create templates for standard visual elements, such as buttons.
In our case, the standard button element doesn't look so good for the other elements on the screen. We have colorful bricks and the funny Comic Sans typeface, so the standard Silverlight button just doesn't fit in. But fortunately we can work on the templates, so that the regular buttons can share the same look and feel of the rest of the application. Here's how it works:
- First of all, we must define the setters for the regular button properties:
        <Style TargetType="Button">
            <Setter Property="FontFamily" Value="Comic Sans MS"/>
            <Setter Property="FontSize" Value="12"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="Foreground" Value="#FF000000"/>
            <Setter Property="Padding" Value="3"/>
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="BorderBrush">
                <Setter.Value>
                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                        <GradientStop Color="#FFA3AEB9" Offset="0"/>
                        <GradientStop Color="#FF8399A9" Offset="0.375"/>
                        <GradientStop Color="#FF718597" Offset="0.375"/>
                        <GradientStop Color="#FF617584" Offset="1"/>
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
 
- Then we must define a setter for a the Templateproperty:            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Grid ShowGridLines="False">
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Normal"/>
         
    <!--When the mouse is over the button, the button background is highlighted. -->
         
                                    <VisualState x:Name="MouseOver">
                                        <Storyboard>
                                        <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" 
                                        Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)" 
                                        To="#FFF0F0F0"/>
                                        <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" 
                                        Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" 
                                        To="#FFF0F0F0"/>
                                        <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" 
                                        Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)" 
                                        To="#FFF0F000"/>
                                        <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" 
                                        Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)" 
                                        To="#FFF0F000"/>
                                        </Storyboard>
                                    </VisualState>
         
    <!--When the button is pressed, the button gets a blue bold border. -->
         
                                    <VisualState x:Name="Pressed">
                                        <Storyboard>
                                        <ColorAnimation Duration="0" 
                                        Storyboard.TargetName="BackgroundGradient" 
                                        Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)" 
                                        To="#FFFFFFFF"/>
                                        <ColorAnimation Duration="0" 
                                        Storyboard.TargetName="BackgroundGradient" 
                                        Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" 
                                        To="#FFF0F0F0"/>
                                        <ColorAnimation Duration="0" 
                                        Storyboard.TargetName="BackgroundGradient" 
                                        Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)" 
                                        To="#FFE0E000"/>
                                        <ColorAnimation Duration="0" 
                                        Storyboard.TargetName="BackgroundGradient" 
                                        Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)" 
                                        To="#FFFFFFFF"/>
                                        </Storyboard>
                                    </VisualState>
         
    <!--When the button is disabled, the button's opacity is lowered to a bit more than 50%. -->
    
                                    <VisualState x:Name="Disabled">
                                        <Storyboard>
                                        <DoubleAnimation Duration="0" 
                                        Storyboard.TargetName="DisabledVisualElement" 
                                        Storyboard.TargetProperty="Opacity" To=".55"/>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
        
    <!--When the button is focused, the button's FocusVisualElement's opacity is set to 100%. -->
    
                                <VisualStateGroup x:Name="FocusStates">
                                    <VisualState x:Name="Focused">
                                        <Storyboard>
                                        <DoubleAnimation Duration="0" 
                                        Storyboard.TargetName="FocusVisualElement" 
                                        Storyboard.TargetProperty="Opacity" To="1"/>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Unfocused" />
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="*"/>
                                </Grid.ColumnDefinitions>
                                <Border x:Name="Background" 
    Grid.Column="1" Margin="0,5,0,0" CornerRadius="0" 
    BorderThickness="{TemplateBinding BorderThickness}" 
    BorderBrush="{TemplateBinding BorderBrush}">
                                    <Grid Margin="1"  ShowGridLines="False">
                                        <Path Stroke="Blue" 
     StrokeThickness="2" x:Name="BackgroundGradient">
                                            <Path.Data>
                                                <PathGeometry>
                                                    <PathFigureCollection>
                                                        <PathFigure StartPoint="10,0" IsClosed="True">
                                                            <PathFigure.Segments>
                                                                <PolyBezierSegment Points="
                                                                               30,5 50,-5 75,0
                                                                               80,10 80,20 75,30
                                                                               50,25 30,35 10,30
                                                                               5,20 5,10 10,0"/>
                                                            </PathFigure.Segments>
                                                        </PathFigure>
                                                    </PathFigureCollection>
                                                </PathGeometry>
                                            </Path.Data>
                                            <Path.Fill>
                                                <LinearGradientBrush x:Name="BackgroundAnimation" 
      StartPoint=".7,0" EndPoint=".7,1" Opacity="1" >
                                                    <GradientStop Color="#FFF0F0F0" Offset="0"/>
                                                    <GradientStop Color="#FFF0F0F0" Offset="0.5"/>
                                                    <GradientStop Color="#FFC0C000" Offset="0.5"/>
                                                    <GradientStop Color="#FFC0C000" Offset="1"/>
                                                </LinearGradientBrush>
                                            </Path.Fill>
                                        </Path>
                                    </Grid>
                                </Border>
                                <ContentPresenter
                              x:Name="contentPresenter"
                              Content="{TemplateBinding Content}"
                              ContentTemplate="{TemplateBinding ContentTemplate}"
                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                              Margin="{TemplateBinding Padding}"
                                    Grid.Column="1"
                                    />
                                <Rectangle x:Name="DisabledVisualElement" 
    Grid.Column="1" RadiusX="0" RadiusY="3" 
    Fill="#FFFFFFFF" Opacity="0" IsHitTestVisible="false" />
    <Path Grid.Column="1" StrokeThickness="3" 
    x:Name="FocusVisualElement" 
    Margin="0,5,0,0" Opacity="0" 
    IsHitTestVisible="false">
                                    <Path.Stroke>
                                        <LinearGradientBrush>
                                            <GradientStop Color="Blue" Offset="0"/>
                                            <GradientStop Color="Blue" Offset="1"/>
                                        </LinearGradientBrush>
                                    </Path.Stroke>
                                    <Path.Data>
                                        <PathGeometry>
                                            <PathFigureCollection>
                                                <PathFigure StartPoint="10,0" IsClosed="True">
                                                    <PathFigure.Segments>
      <PolyBezierSegment 
      Points="30,5 
      50,-5 75,0     
      80,10 80,20 75,30    
      50,25 30,35 10,30   
      5,20 5,10 10,0"/>
                                                    </PathFigure.Segments>
                                                </PathFigure>
                                            </PathFigureCollection>
                                        </PathGeometry>
                                    </Path.Data>
                                </Path>
                            </Grid>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
 

In the Normal state, the button's border is defined by a Path element, which is defined by aPolyBezierSegment described by the curvy lines you see here. 
In the Focused state, the button gets a blue bold border, so that you can see which button is focused. 
In the MouseOver state, the button's background is highlighted. 
In the Disabled state, the button's opacity is lowered. 
 
Hiç yorum yok:
Yorum Gönder