My esteemed colleague Alex Golesh sent me a request to rate his app on the MIX 10k competition, I wasn’t aware of this competition, but I immediately decided that I want in.
The rules are simple, do anything you want on Silverlight or XBAP but you mustn’t exceed 10kb of code and resources.
After some thinking I’ve decided I want to create a mathematical function plotter, you can find such on sites like www.Mathway.com.
So Now before you continue reading and learning how I put it together, play with my app, discover all the features, try out all the functions. It will make you life easier understanding what I’m doing because we will get very soon to XAML.
http://2009.visitmix.com/MIXtify/TenKDisplay.aspx?SubmissionID=0031
(And if you liked it, rate it, please).
Thread.Join();
You have returned. Good. Hope you took your time. Now let’s us continue.
I will begin dissecting the final solution but it took me a while to grasp how everything will fit in, and what UI to use.
Just to describe chronologically, I’ve started with manipulating a simple object Line inside a Silverlight application. When I mastered it I hardcoded some functions such as X^2 and Sin(x) to see how the line is behaving with different sets of points.
Then I put my mind on the UI of how the user will create his function.
At first I fantasized on Mathway.com style ability to parse a function from a string, I realized quickly that building such a parsing engine will most definitely bring me over the 10k limit.
So I inclined to be building a much simpler way to describe what function is to be plotted: The user chooses the function to plot, if there is a need for extra parameters, a listbox is used to enable editing.
One last comment: In order to remain under 10k limit, comments, nice separation to functions (or files), extensive error handling is a luxury one can not afford. You have been warned.
So enough talking let’s start seeing some code.
Everything we need is inside Page.xaml and Page.xaml.cs.
Beside that we have the app.xaml and app.xaml.cs that were created with some code generated from the visual studio template. I left them almost untouched except of deleting comments (Which by the way if you didn’t know saved me precious k-s!).
- Open Page.xaml and notice we have a Grid object called LayoutRoot with background.
<UserControl x:Class="Plotlight.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="White">
</Grid>
</UserControl>
- A cool tip I learned from Guy Burstein, change the Width and Height of your app to MinWidth and MinHeight. I choose 600, you can have whatever u like.
- Because we want our app to win and we are no designers the easiest thing we can do to make our app more pretty is use gradient brush, everybody loves them and you don’t need to really have a talent. right? Remove the background = “White”.
<Grid x:Name="LayoutRoot">
<Grid.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#C4008DFA"/>
<GradientStop Color="#FFFFFFFF" Offset="1"/>
<GradientStop Color="#FDF9FCFE" Offset="1"/>
</LinearGradientBrush>
</Grid.Background>
- We will create a basic layout for our app, I decided to create 2 columns, one for the function retrieval and second for the plotting area.
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*"/>
<ColumnDefinition Width="70*"/>
</Grid.ColumnDefinitions>
- Create a StackPanel, this will arrange all children control in a way that each will be on top of the other one.
<StackPanel Margin="10" Orientation="Vertical" Grid.Column="0">
- Add a pair of TextBlock and Combobox, the ComboBox will contain the functions that are offered to be plotted.
<TextBlock FontSize="14" Text="Choose a Function:" Margin="0,0,5,0"/>
<ComboBox x:Name="fChs" Width="200" DisplayMemberPath="Name" SelectionChanged="fChs_SelectionChanged"/>
- Add another pair, now it will be a TextBlock and a listbox. The listbox will contain a mean to edit parameters to the functions.
<TextBlock FontSize="14" Text="Enter coefficients:" Margin="0,0,5,0"/>
<ListBox x:Name="ParamList" Width="200" Height="200">
</ListBox> - Inside the listbox we will define a DataTemplate, we will do that later on. The DataTemplate will define how a certain class is to be shown in the UI so each entry will receive it’s own line inside the listbox.
- Add another StackPanel this time it will align the controls Horizontally, and fill it with 2 buttons and TextBlock: One button will Plot, the second will clear and the label we will use to notify an error, if needed that why it is not visible.
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,8,0,0">
<Button Margin="0,0,5,0" Content="Plot!" Click="Plot_Click"/>
<Button Content="Clear" Click="Clear_Click"/>
<TextBlock x:Name="ErrorLabel" Foreground="Red" Text="Error" Visibility="Collapsed"/>
</StackPanel>
OK we finished creating the first column of our Layout.
The second column contains a part with a lot of boring text and it is not very interesting, but the second part is our drawing area.
- We will create a Border for our drawing Area. So it will be very clear where the function will be plotted. Actually the Width and Height are very important. it is 400 and 3 for each side of the border. Is is very important for us that the plotting area would be 400 exactly, in the code behind we will see that we will plot a point for each pixel (400 points total).
<Border BorderThickness="3" BorderBrush="Black" Grid.Column="1" Width="406" Height="406">
- Now we will create another Grid, this will serve as the background for our drawing area. I choose Azure for some reason :) and the Grid lines are shown because they will be our Axis. Yep, remember less is code in our app.
<Grid Background="Azure" ShowGridLines="True" >
- So now it shouldn’t surprise you that we are separating the Grid into 4 equal quarters
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*"/>
<ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50*"/>
<RowDefinition Height="50*"/>
</Grid.RowDefinitions>
- The last part is inserting a Canvas object on top of the Grid. On this objet we will draw our beloved function. Notice that I’m using a transformation to flip the content of the Canvas. The reason that while Y axis behaves the opposite from the Y axis of the screen meaning when values increases Graph Y Axis is going up and Screen Y axis is going down, this small manipulation saves some trouble.
<Canvas x:Name="Canvas" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Grid.RowSpan="2" RenderTransformOrigin="0.5,0.5">
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleY="-1"/>
</TransformGroup>
</Canvas.RenderTransform>
</Canvas>
Now we are ready for Part 2. Which we will discuss the interesting part, the code behind.
Stay Tuned.
Ariel