Thursday, January 22, 2015

Code Clinic for C#

Introduction
Welcome
00:04Hello, and welcome to Code Clinic for C#.
00:08My name is David Gassner, and I'm excited to show you
00:11my solutions for the challenges proposed to the Code Clinic authors.
00:16In this monthly series, we'll explore creative and efficient
00:20ways to use C# to solve a range of problems.
00:24I'll describe some of the unique aspects of working
00:26with this language, and show you some tips on how
00:29to make your code run faster and be more
00:31reliable, as well as pointers on making it more readable.
00:36If you search the Lynda.com library for Code Clinic,
00:40you'll find additional courses by different authors using other languages.
00:45Each Code Clinic author solves the exact same set of problems.
00:49So you can compare different solutions, as well
00:51as the pros and cons of each language.
00:54If you're looking for tips on better coding, or trying to decide
00:57on your next new programming language, you're going to love Code Clinic.
01:01So let's get started with Code Clinic for C#.
Collapse this transcript
What you should know before starting this course
00:00This course is designed for software developers who either already use the C#
00:05programming language, or are curious about it
00:08and want to compare it to other languages.
00:12If you're brand new to C# and Visual
00:14Studio, you might be interested in these Lynda.com courses.
00:19For your first introduction to the language,
00:22check out Up and Running with C#.
00:24And to get into more details about
00:26programming with C#, look at C# Essential Training.
00:32To learn your way around Visual Studio, you can look at these courses.
00:37Visual Studio 2012 Essential Training, which is a complete tour of the product.
00:43And Visual Studio 2013 for Web Developers, which focuses on
00:48aspects of the product that are unique to web development.
00:53If you'd like to open the solutions I provide
00:55as part of the course, you'll need some software.
01:00You can either use one of the pay for licence versions of Visual Studio, for
01:04example Visual Studio Professional 2013, or you can use free software.
01:11To work with console applications, or
01:13desktop applications built with the wind forms
01:16or WPF frameworks, you can use Visual Studio Express 2013 for Windows Desktop.
01:24The sample code that I provide as part of the course should
01:27open fine in either the free or the paid versions of the product.
01:32So, let's get started with Code Clinic for C#.
Collapse this transcript
Using the exercise files
00:00Each of the problems in the Code Clinic Series is accompanied by exercise files.
00:06The exercise files are the finished source code for my solution to each problem.
00:12I've copied the exercise files to my desktop, but
00:15you can place them anywhere on your hard disk.
00:18For this challenge, I have two different applications.
00:22One named CollectLakeStats and one named LakeStatsWebService.
00:28If you have one of the pay for license versions of Visual Studio
00:332013, you can open these applications in that one version of Visual Studio.
00:38If you're using the free Visual Studio
00:40Express, you'll need Visual Studio 2013 for
00:44Windows Desktop to open the CollectLakeStates project
00:48and you'll need the web version for LakeStatsWebService.
00:52I'm using Visual Studio Professional, and I'll show
00:56you how to open each of these solutions.
00:59You can either open the solutions from Windows Explorer or
01:02file explorer, or you can open them from within Visual Studio.
01:08In file explorer, I'll go to
01:10the CollectLakeStats folder and double-click collectlakestats.sln.
01:16And that opens a new instance of Visual Studio.
01:20And there's all the files and directories in my solution.
01:28You can do the same thing with the web service.
01:31Open the folder, and look for the solution file.
01:35Double-click, and it should open in a compatible version of Visual Studio.
01:42If you prefer, you can open these files from within Visual Studio.
01:47I'll select File > Open > Project or
01:50Solution, go to the folder, and double-click the solution.
01:56Once again if you're using the express version of Visual Studio.
02:01Be sure you're using the right addition for each application.
02:05Once you have the solutions open, you can explore the code.
02:09And I'll show you how I've configured
02:11each application when I get into those demonstrations.
Collapse this transcript
Getting the most from Code Clinic
00:00If you really want to get the most out of Code Clinic,
00:03create your own solution to the problem before you watch my solution.
00:07If you spent prior time working on the question, you'll have a better idea of
00:12some of the difficult parts and be better
00:14able to understand why I've made certain choices.
00:18Finally, I've included the source code for my solution in the exercise files.
00:23I'm assuming you already have the necessary
00:25tools installed to be able to edit
00:27and compile the code, and I'll describe what you need in the next movie.
00:33Code Clinic is a unique opportunity to look
00:36over my shoulder as I actually solve coding problems.
00:40Keep in mind that my solution isn't the only correct solution.
00:44There are always a multitude of ways to solve a problem, but by watching
00:49how I approach the task, you can learn how to become a better programmer yourself.
Collapse this transcript
Problem One: Statistical Analysis
Introducing Lake Pend Oreille
00:06Hello and welcome to Code Clinic.
00:08My name is David Gasner.
00:11Code Clinic is a monthly course, where a unique
00:13problem is introduced to a collection of lynda.com authors.
00:17In response, each author will create a
00:19solution using their programing language of choice.
00:23You can learn several things from Code Clinic.
00:25Different approaches to solving a problem, the pros and cons of different
00:29languages, and some tips and tricks,
00:32to incorporate into your own coding practices.
00:36This month, we'll work on a problem in
00:38statistical analysis, and to some extent, handling big data.
00:43Its common to use a computer, to
00:44manipulate and summarize large amounts of information.
00:48Providing important insights on how to improve or handle a situation.
00:54In this problem, we'll use weather data collected by
00:57the U.S. Navy, from Lake Pend Oreille in Northern Idaho.
01:02Lake Pend Oreille is the fifth deepest freshwater lake in the United States.
01:07So deep in fact, that the U.S. Navy uses it to test submarines.
01:12As part of that testing, the U.S.
01:14Navy compiles an exhaustive list of weather statistics.
01:18Wind speed, air temperature, barometric pressure and so on.
01:24You can browse this data, by pointing your web browser at http://lpo.dt.navy.mil.
01:29You'll find several weather summaries, a webcam, and the raw
01:35data they collect every five minutes, archived as, standard text files.
01:41For anyone living or working on Lake Pend Oreille.
01:44Weather statistics are an important part of everyday life.
01:48Average wind speed can be very different than median wind speed.
01:52Especially if you are on a small boat in the middle of the lake.
01:56In this challenge, each of our authors will use their favorite language, to
02:00calculate the mean and median of the
02:03wind speed, air temperature, and barometric pressure.
02:07Recorded at the Deep Moore station for a given range of dates.
02:11First, let's briefly review mean and median.
02:15These are both statistics.
02:17To explain statistics, we need a set of numbers.
02:21How about 14 readings for wind gust at
02:25Deep More weather station on January 1st 2014.
02:30You can see the data at this web page.
02:33The first column in the data, is the day the wind gust was recorded.
02:37The second column is the time it was recorded, and
02:40the third column, is the wind gust in miles per hour.
02:45The mean, is also known as the average.
02:49To calculate the mean of a range of numbers, simply add
02:52the values in the set, then divide by the number of values.
02:56In this example, we add 14, plus 14, plus 11 and so on.
03:02Then divide the sum by 14.
03:05The count, of the numbers in the set.
03:07In this case, the mean is equal to nine.
03:11The median, is the number halfway between all
03:14the values in a sorted range of values.
03:17Think of the median as in the median strip of the road.
03:21It always marks the center of the road.
03:24To calculate the median, first sort the numbers from lowest to highest.
03:29If there is an odd number of values, then just take the middle number.
03:34If there's an even number of values, then
03:36calculate the average of the central two numbers.
03:40In this case, there's an even number of values so we sort,
03:45then take the average of the middle two values, eight and 11.
03:49The median is 9.5.
03:52Now, some authors have chosen to implement a web service, allowing other websites or
03:58other web service consumers, to access the
04:01new weather statistics created by the author's program.
04:05This is typically done by creating a RESTful API, which,
04:09when called, returns information in a format known as JSON.
04:14For a detailed description of JSON and its uses, please look
04:18at JavaScript and JSON by Ray Villalobos, in the lynda.com library.
04:24In our case, I've hosted the finished web service, on a Microsoft Azure website.
04:31I've seeded the service with cached data
04:33from January 1st through February 28th 2014.
04:38If I make this HTTP call, I get back the data in JSON format.
04:45Notice that my start date and my end date are a part of the URL.
04:50I'll describe later on how you can call this web service in a variety of ways.
04:56When I make the web service call, I expect to receive the
04:58mean and median for windSpeed, airTemperature
05:00and barometricPressure for the range of dates.
05:06If I wanted the range of dates from January 1st to
05:09January 7th, I would simply change that value in the URL.
05:14And I would get back a different calculation.
05:17And when formatted in JSON, it looks like this.
05:22So there's our first challenge.
05:24Pulls statistics from a data set available online.
05:28And then calculate them, creating aggregate values.
05:32Perhaps you want to pause and create a solution of your own.
05:35How would you solve the problem?
05:38In the following movies, I'll show you how I solved the challenge.
Collapse this transcript
Overview of my solution
00:00Before I describe the details of how I solved this programming problem
00:05I'm going to describe some of the thinking that went into it.
00:08First lets start with the specifications.
00:11What my application and web service need to do.
00:15First I'm going to be collecting data from a remote server over its web service.
00:20The web service will return data one page at a
00:24time for each combination of a date and a data type.
00:28That can't be changed.
00:30That's the nature of the remote web service.
00:34Then I'm going to aggregate that data.
00:36I'm going to calculate the median and averages
00:39I've described and publish that information through my own
00:42web service and the request from the client
00:46is that the data be published in JSON format.
00:50Now there are a couple of ways to solve this.
00:53One approach might be described as Synchronous Processing.
00:58There are three components in this system.
01:02There is a Web Service Client, it could be a web browser, a mobile application
01:07or any other application that can consume a JSON formatted web service.
01:14Then there is My Web Service that is providing that JSON formatted data.
01:19And then there's the real data source
01:21which here I'm calling the Lake Web Service.
01:25In synchronous processing, the Web Service Client would make a request to My
01:29Web Service, and it would then make a request to the Lake Web Service.
01:34The data would come back and then it would
01:37be aggregated and return to the Web Service Client.
01:42And this strategy might work fine as long as we
01:45limit the amount of data that's going to be processed
01:48and as long as all components are working correctly at
01:51all times, but here's where we can get into trouble.
01:56The client has told us that they want to be able to
01:58request any range of dates from the web service that we're creating.
02:04And the problem is in the structure of the data on the actual web service.
02:08And that can create a problem of scale.
02:11The problem is in this step.
02:14Let's say for example that the client request array of seven days.
02:20The data is organize in the link web service, one file per day per data type.
02:27And a little bit of math with three data types tells us
02:30that that results in 21 separate requests for just one week of data.
02:37If the client request a larger range of
02:39days, that creates a larger number of requests.
02:43Each new day adds three new requests, and if they ask for a year of data, the
02:49web service might be sitting there for a long time, using up threads on the server.
02:55And hammering that back end web service and it's a web
02:59service that we don't really have permission to treat in that way.
03:02So another approach is to put together a system that I'm calling asynchronous
03:07processing and separate the task into
03:11two steps, Data Collection and Data Publishing.
03:16In this architecture, I'm going to start with a desktop app.
03:20It'll be a console app with no real user interface.
03:24It could just as easily be built with
03:26WPF or Windows Presentation Foundation or Windows Forms.
03:31But I'm going to keep it simple.
03:33And the job of this application will be
03:35to collect the data from the Lake Web Service.
03:39For each combination of date and data type it'll make a request and get the
03:44data back, and then it'll save the data locally in a cache of some kind.
03:50The cache could be a relational database, but all I'm going to do is
03:54take those pages from the lake web service and save them locally on disk.
03:59So that they're available instantly to my web service.
04:03Now here are the other two components, the web client and my web service.
04:09And when the web client makes a request to
04:11my web service, I'll get the data from the cache.
04:15And aggregate it and return it to the client.
04:19Now because the data is stored locally, responses should be nearly instantaneous.
04:25And I won't be depending on the lake web service to be up and running.
04:28And I went constantly hammering looking for data.
04:33Here are some assumptions about my system.
04:36The web service will only be able to provide pre-cached data.
04:41When request comes in from the web client.
04:43It'll look for those files in the cached directory.
04:47If they're all available it'll do the
04:49aggregation and return the result in JSON format.
04:54But if anything is missing, it'll just return an error.
04:58The data collection process, which will be a console application built
05:03with .net will run in a scheduled task once a day.
05:07So each day, say at two in the morning, it'll go
05:11out to that Lake Web service and get the most recent data.
05:15And you could also set it up, so that it looks for
05:18any holes in it's current data and sees if it can fill them.
05:22The data will be cached as simple text files on disk.
05:26As I described earlier, I could put the
05:29data into a relational database such as SQL
05:32server, but by using simple text files, I
05:35eliminate a whole bunch of labors of complexity.
05:39And just like the original web service does, I'll store the
05:42data in one file for each combination of date and data type.
05:49The applications will be built as a desktop
05:52console application that will do the data collection.
05:55And if I want I can set it up as a scheduled task.
05:59And for publication I'll use an ASP.NET web service.
06:04Alternatively, I could also use Windows Communication Foundation, or WCF, but the
06:10ASP.NET web service will be simpler, take less code, and be easier to update.
06:17And finally, of course, both applications will be coded in C#.
06:23They will share a certain amount of code in common,
06:26but for this demonstration I'm keeping each application completely separate.
06:31As I tune and re-factor my applications, I might find areas where they
06:35can share libraries and where I can make my application more refined and elegant.
06:41But for these steps, each application will be in it's own
06:44Visual Studio Project, one, a website and the other a complete solution.
06:51So let's take a look at the code that I used
06:53to build the desktop console application and the ASP.NET web service.
Collapse this transcript
Retrieving data from the lake's web service
00:00I'll begin my review of my solution to this problem
00:03by opening up one of the applications in Visual Studio.
00:07In the Exercise Files for this chapter,
00:10I'll go to the CollectLakeStates folder, and
00:13double-click to open the Solution file, and
00:16that opens the project in Visual Studio 2013.
00:20Now, before I run the application, I'll show you one important configuration.
00:26I'll go to the menu and choose Debug > Lake Stats Properties.
00:31In the Debug category, I've set the working directory
00:35to a directory named c colon backslash Lake Stats.
00:39This is where I'll be creating my executable, and
00:42it is also where I will be caching data locally.
00:46Before you run this application on your machine if you want
00:49to place this somewhere else on your hard disk, change it here.
00:54Then build the solution by choosing Build > Build Solution.
01:00And you should see a message in the
01:01output window indicating that they're weren't any failures.
01:06Now I'll go to a command window.
01:12I'll change to that \Lakestats folder and look at it's contents
01:18and I see the generated executable, LakeStats.exe and a bunch of other files.
01:25I'll run the application by typing in
01:28LakeStats.exe, and when I run the application without
01:32any arguments, I get back usage hints indicating
01:36how the application is supposed to be run.
01:39So I'll go ahead and run it that way.
01:42I'll call LakeStats.
01:44Then I'll pass in two dates, a start date and an end date.
01:50I'll set my start date as 2014-01-01,
01:55that's January 1st, and 2014-01-07.
02:02So I'm asking for a range of seven days.
02:06For each of the days, I get a
02:08message indicating that it's downloading from the remote website.
02:12And then it's calculating the mean and median for that day.
02:16Notice that the output isn't in JSON format.
02:19It's in object notation.
02:22This output is purely for debugging.
02:25It's verifying that it got the data from
02:27the remote website, and it's doing an initial calculation.
02:32Now, if I run the application a second time, you'll see that it goes much faster.
02:38And instead of the message downloading, I get a message from cache.
02:44And that's because the first time I ran the
02:46application, I created a subdirectory named cachedir, for cache directory.
02:53And in that directory, there are now
02:55three subdirectories named Air_Temp, Barometric_Press, and Wind-Speed.
03:03And if I go into those folders and list
03:05their contents I see one text file for each date.
03:10Notice the format of this date here, it uses underscores instead of dashes.
03:15And that mimics the way it's named in the remote web service.
03:20Finally, I'll list the contents of one of these text files and show
03:25that it has exactly the same structure as the original web service result.
03:31In face, all this application is doing is grabbing the results
03:35from the HTTP request and saving it locally in a text file.
03:41Once it's been cached locally on the hard disk, the web service will be able to
03:46get to this content and aggregate and display the results almost instantly.
03:52So that's how this application works.
03:54In the following movies I'll show you the code.
Collapse this transcript
Running the console application
00:00The console application is structured as four C# classes.
00:05The entry point is this class, program.cs.
00:10It has a main method, which marks it as the beginning point of the program.
00:14As the application starts up, it examines the
00:17arguments that are passed into the command line.
00:20It looks for either one or two arguments.
00:23If there's one argument, that's treated as both a starting and an ending date.
00:27And if there are two arguments, then those
00:29are treated as separate starting and ending dates.
00:32Either way, the values that are passed in are parsed as date time values.
00:38If no arguments or more than two arguments are passed in, then we hit the default
00:43case and we display the usage hints on
00:45the command line and return, exiting the program.
00:50Now, notice that all that code is wrapped in
00:52a try catch block, and if we run into an
00:56exception, then we output the type of the exception and
00:59the exception message, display the usage hints again, and return.
01:05I'll go to my command line.
01:07And type, LakeStats.
01:10And I'll pass in values that can't be parsed as daytime values.
01:15I get back the exception type.
01:17The message, I see the usage hints and the program stops.
01:24If we get past that point in the
01:25code, then we can start looking for other conditions.
01:30Here's the next condition starting at line 42.
01:32I'm comparing the starting and ending dates, making sure that they
01:37either match, or that the start date is before the end date.
01:41If the end date is before the start date, then I output an error and I return.
01:47If I get to line 49, then I'm
01:50ready to start getting data from the remote website.
01:55Here, I am going to use a class called FeedManager.
01:58This is another custom class that's a part of my application.
02:02I'm then looping through the days that have been requested.
02:06Using a while loop, each time through the while
02:10loop I'm adding one to the value of the date.
02:13Using the ad days method down at the bottom of the loop.
02:17Within the loop, I once again have a tried catch block.
02:20Notice that I'm outputting the information about the
02:23exception, but I'm not stopping the program now.
02:26I'm letting this be forgiving.
02:28So if I run into an exception, I'll still
02:31be able to process the rest of the request.
02:34For each date, I'm gathering the wind speed, the
02:38barometric pressure, and the air temperature from the remote website.
02:44Let's take a look at this method, GetData.
02:46That's a part of the FeedManager class.
02:50I'll select the method name and press F12, and
02:53that jumps to that class and to that method.
02:58Notice at the top of the class that there are
03:00three public constants, for wind speed, barometric pressure and air temperature.
03:06The strings for these constants match the
03:09names of the directories on the remote website.
03:12I'll be using those to build the URLs and make the requests to the website.
03:19Notice also that I've created an instance of
03:21a class called cash manager, that has all
03:24the code to manage where the data will be saved locally, I'll get to that later.
03:30Let's look at the GetData method.
03:33Within the method, I'm declaring a list of double values.
03:37This is where I'll store the raw numbers for the
03:40current date and the current data type, such as barometric pressure.
03:46I'm also creating an instance of a class called feed result.
03:50This class has three fields, named, mean, median and count.
03:54And a two string method, that outputs the values with appropriate labels.
04:02For each of the three data types, I'm creating an instance of that FeedResult
04:07class, populating it with the data from the remote website, and returning it here.
04:14Going back once again to the GetData method.
04:17This is where I create the instance of the FeedResult class,
04:22and then I try to get the data from the local cache.
04:26Don't worry about that method call, I'll come back to it later.
04:30But here, on line 36, if I get back null from get data from cache, then I decide I
04:37need to go get the data from the remote
04:39website, and I call this method, get data from URL.
04:45Notice, this is where I'm outputting the label downloading.
04:50The GetData from URL method does the following work.
04:55It constructs the URL for the current request.
04:58Starting with the base URL, that's the location of all the web services.
05:03And then, appending the year as a directory.
05:06The complete date as a directory and the data type.
05:11Now, I have a URL from which I can request the data.
05:15Within the try catch block, I'm using a number of classes from
05:18the .Net framework including web request, stream, stream writer, and stream reading.
05:26I'm then looping through the response, and I'm saving that data as a list of strings.
05:34Then at line 115, I'm writing that data to the cache.
05:38Again, I'll come back to the cache manager later.
05:42If I get past the catch block, then I return the lines to the calling scope.
05:48Notice in the finally block, I'm closing the response object.
05:52That's very important to prevent any memory loss.
05:56I'll go back to the program class, and show that
06:00this is where I'm outputting the results to the console.
06:04The final method in this class is called
06:06DisplayUsageHints, and this is the method I'm using
06:09to output information to the console, if the
06:12user doesn't pass in the correct of arguments.
06:16So that's the outer structure of the application.
06:20There are two more critical parts.
06:22Doing the calculation of the aggregate values, and managing the local data cache.
06:27And I'll describe those steps in detail in the following movies
Collapse this transcript
Saving the data to a local cache
00:00The Cache Manager class is a critical part of my application.
00:04Versions of this class will be used in both the console application that collects
00:08the data from the lake's web site, and the web service that uses the data.
00:15Here is the version of the class that I'm using in the console application.
00:19It’s called CacheManager.CS.
00:22First of all, there’s a constant here that points to the
00:26location of the directory where I’m going to save the data.
00:30Right now it’s hard coded.
00:32If you wanted the data to go somewhere
00:33else on your system, you could change this string.
00:39First, there is a method called setDataDirectory,
00:41which accepts a single argument called data type.
00:45It verifies that the cache directory and if it doesn't, it creates it.
00:50Then, it creates a string called dataDirectory
00:53that appends the data type and a
00:55trailing backslash and it guarantees that, that
00:58directory exists, creating it if it doesn't.
01:02And finally, a call to the method, SetCurrentDirectory from the dot
01:06net frame works directory class sets that as the current working directory.
01:12Now this method, setDataDirectory, is called from
01:14one place in this class called getFileName.
01:18When I call the get file name method, I'm
01:20passing into current date and a data type, a string.
01:24And then I'm calling this method setDataDirectory.
01:28Then I'm building the location and name of the file.
01:31Starting with the current directory, and then
01:33appending a backslash, and then the current
01:36date in this specific format, that matches the format used on the remote website.
01:43And finally upending the .txt file extension.
01:47Because, that's how the data is stored by the remote web service.
01:51This getFileName method is called a couple of times.
01:54First, in right file to cache.
01:57This method receives three arguments.
02:00The content, which is the content retrieved from the remote site, the
02:04date time, which is the current date and the data type, a string.
02:09It gets the file name and then uses the stream
02:12writer class to write that file to the local hard disk.
02:17The getDataFromCache method does something similar.
02:20Once again it gets the fileName based on Date and
02:23dataType and if the file doesn't exist it returns null.
02:28But then if the file does exist it reads the file
02:32into memory using the file stream, file, and stream reader classes.
02:37And returns the list of strings.
02:42The feed manager class is where this cacheManager class is being used.
02:46Here's the call to writefileToCache, and here's the call to getDataFromCache.
02:54So that's how the cacheManager is working.
02:58Now if you preferred, you could change your caching strategy to use
03:02a relational database, such as SQL server or a no SQL database.
03:07All the code to manage the cache is in the cacheManager class.
03:12So theoretically, you could make those changes in a
03:15local class without changing the rest of your API.
03:18So that`s how I`m managing the local data.
03:22And now, in the next movie, I`ll show you
03:24how I`m aggregating the values and showing the results.
Collapse this transcript
Calculating the aggregated statistics
00:00So far I've described two major components of the Console
00:03application, the entry point where it gets the arguments or requests
00:08from the user, and the local caching strategy which is currently
00:12saving the data as simple files on the local hard disk.
00:17Now I'll describe how the aggregate calculations are being done.
00:21Which in the Console application is calculating all the values for
00:25a single date and a single data type at a time.
00:29This code is in the FeedManager class.
00:32If scroll down to the get data method down to about
00:34line 57, you'll see that we're working with an object called values.
00:40The values object is declared up here.
00:44It's a list of double values.
00:46It's the raw numbers that come from the text files, regardless of whether we
00:51get them in this call from the remote website or from the local data cache.
00:58Here's where the values list is being populated, starting at line 47.
01:03We're looping through lines the strings in the list, and then I"m parsing each line.
01:09Getting the third item in the list and adding
01:12that value to the list as a double value.
01:15The conversion from string to double is happening right here on line 52.
01:21Notice this option in the call to the strings split class.
01:26I'm passing it an option called
01:28StringSplitOption.RemoveEmptyEntries, and that's because some of the
01:33files have values in them that are just commas separated from each other.
01:38To get consistency in parsing, I had to make this little adjustment.
01:43Once I get past this code, I know how many items I have in the list.
01:47And I save that to my instance of the feed result class.
01:52Then, I get the mean value.
01:55The list class makes this incredibly easy because it has a method named Average.
02:01it does all the rounding for me, and right now I'm returning the raw value.
02:07If I want to round it to two values, I could do this.
02:10I could wrap values.average in math.round, and then pass in a value of two.
02:20And that would mean round it to a value of two decimals.
02:25Next, I'm going to calculate the median value.
02:28This takes a little bit more work, because there isn't
02:31a method in the list interface that does it for me.
02:34As I described in the introduction, here's how you do a median calculation.
02:40First sort the values in ascending order and I am doing
02:43that with a simple call to the list interfaces sort method.
02:48Then, find the median index.
02:51The point that's midway between the highest and the lowest values.
02:55Notice I am casting this return value as a double.
02:58That guarantees that I won't lose any fractional values as I do the calculation.
03:04Then I'm checking to see whether I have a fractional or whole value.
03:08This expression at line 70, truncates the index
03:12and then compares it to the untruncated index.
03:16If they match each other, then I know I had an odd number
03:19of entries and I got back an index that was a whole number.
03:23And I can simply take the value from that index, notice that to
03:26deal with it as an index, I have to convert it to an integer.
03:30And then take the value from the list at that position and save it as the median.
03:36If I have an even number of values, then my index will be fractional.
03:41And here's how I'm doing that calculation.
03:44Once again I'm the indexes integer, and I'm saving it as the low index.
03:49Then I'm adding one to get the high index.
03:52And then taking the average of those two values and saving that as the median.
03:57And then returning the result.
04:00The result is an instance of the feed result class.
04:05And the program receives those values and simply outputs them to the console.
04:11And that's the complete Console application.
04:14Now at this point, we're not
04:15quite matching to specifications because first of
04:18all we're only doing these aggregate calculations for one day at a time.
04:23And the spec said we needed to handle a range of dates.
04:26But I'm going to do that in the context of the web service.
04:30In the web service, the user will be able to pass in a range
04:33of dates and get the aggregate for all of those dates at the same time.
04:38The job of the console application, is simply to get the data from the remote
04:42website, save it locally and then report to the console what happened.
Collapse this transcript
Configuring the ASP.NET web service
00:00The second critical component of my solution is the web service.
00:04Which I've built as an ASP dot web net service.
00:08Alternatively I could have used
00:10the windows communications foundations frame work.
00:13But that takes a good bit more meta data and for my purposes a
00:17simple ASP dot net web service was easier to create and easier to maintain.
00:24I'll open the code from Visual Studio by selecting File > Open
00:29> Project or Solution and then from the Exercise Files folder I'll
00:34go down to the LakeStatsWebService folder and I'll open the solution file.
00:40Depending on where you've placed your exercise files you
00:43might see an error occur when you open the project.
00:46If you do, close the project and then try opening it this way.
00:50Select File > Open > Website, navigate to the project folder and click Open.
01:00You'll see that the properties window in the bottom right looks a bit different.
01:04It shows website properties instead of project
01:07properties but it's the same visual studio project.
01:12Just like the Console application, this application has four different classes.
01:18But there is also a critical entry point and ASMX file, and this is the file that
01:24the web service consumer will actually refer to when they make an HTTP request.
01:30It has a web service annotation and a code behind reference pointing to this file.
01:36LakeStatsService.cs.
01:40It also has a class reference, pointing to
01:43a Class LakeStatsService, that's defined in this cs file.
01:50Now, before I get in to the actual
01:52code, I'll describe some critical configurations that I've made.
01:57First, I've added a web service annotation with my own custom namespace.
02:03Then, I've added a web service binding and the method that
02:07encapsulates my web service, which is
02:09named GetLakeStats, has two critical annotations.
02:14Web method, which means that it can be called over the web.
02:17And script method which says that it can be called as
02:20a get request and that the response will be in Json format.
02:26But there's one other critical configuration that I
02:28had to add and that's in the web.config file.
02:33In this file, I added protocol definitions.
02:37One for HttpPost, and one for HttpGet.
02:41And this means that my web service can be
02:43called either with a Get request, or a Post request.
02:48Now as I indicated in the introduction to this chapter, I've
02:52posted my web service on a website that's hosted at Microsoft Azure.
02:58And if I call the web service, I'll see that I'm
03:01referring to the Asmx file here, as part of the URL.
03:05And the name of the method, getLakeStats here.
03:08And then everything else is the arguments, the values that I'm passing in.
03:15Now, because I'm using an Asp.net web
03:17service, I get some automatic free testing capabilities.
03:22I'm going to trim off everything after the
03:24.Asmx and then press enter to make that request.
03:29And now I get a webpage that shows the available operations or methods.
03:35I'll click into GetLakeStats.
03:37This screen lets me make requests using a Post request.
03:43I'll pass in dates that I know have been locally cast at the web service.
03:46And
03:49now invoke the web service.
03:52And I get back exactly the sort of response I did with a Get request.
03:58I can also scroll down on this page and see documentation
04:02for calling the web service with Soap and with a GET request.
04:08But my focus is on the GET request and so I'm going to stick with this sort of call
04:13where I'm simply passing in the starting and ending values as a part of the URL.
04:20So, that's the basic architecture of the web service.
04:23I've configured it to respond to both POST and GET requests.
04:28I've added the appropriate annotations to the method
04:31that I'm exposing as a web service operation.
04:34And now I'm ready to add the actual
04:36code, that's receiving and responding to the request.
04:40And I'll describe that code in the following movies.
Collapse this transcript
Returning data in JSON format
00:00For testing purposes, I created my GetLakeStats method so that if
00:05blank values were passed in, then I would cede the service with default values.
00:11And that's the code you see in the beginning of the method GetLakeStats.
00:13Because I'm going to be calling this directly from a browser, this
00:19bit of code won't actually be used, but it was useful during testing.
00:25The next bit of code declares a couple
00:27of date time values named startDate and endDate.
00:31And at lines 39 and 40, I'm taking the arguments that
00:34are passed into this method and parsing them into DateTime values.
00:39This code and the following code is all wrapped in tricatch blocks.
00:44If I get into the catch block, notice I'm taking
00:47the exception and passing it into this method, called GetExceptionAsJson.
00:54And this method creates a dictionary object.
00:57It passes in a single item into the dictionary with
01:00a name of error and a value of the exceptions message.
01:05And then serializes that using a class called JavaScriptSerialize.
01:11Now, let's watch what happens if I pass in values that can't be parsed.
01:16I'll set my start value to XYZ.
01:20And there's the result.
01:21I get back a valid Java Script object, which can be parsed by a JSON parser.
01:28The name of the object is Error.
01:30And the message is the message that's exposed by the exception.
01:35So that's some trivial error handling that I was able to
01:38add to the web service to deal with the most common issue.
01:42Here is another common issue, a value that can't be parsed as a valid date.
01:53Instead of 131, our passing 2-31 and we all know that February only has
02:0028 days and I get back the error, string was not recognized as a valid date time.
02:07if I get past that initial condition.
02:09And I have valid date time values.
02:12The next step, is to setup a place to store my results.
02:16I'm creating another dictionary.
02:19And for this dictionary, the names will be strings.
02:22But the items values will be instances, of the FeedResult class.
02:28Let's take a look at feed result for the web service.
02:31It's a little different than the version I created for the console application.
02:36It has two fields named Mean and Median.
02:40And then the two string method returns it's value as JSON.
02:45Now, once again, I could have used a whole dictionary and a serializer, but
02:50this bit of code was so simple that I decided to hand code it.
02:54And part of that is to show
02:55you various approaches to creating JSON formatted data.
03:01Going back to my class, I'm getting those
03:04feed result objects by calling the method, GetData.
03:08Which is a member of the class, FeedManager.
03:12Just as I did with the console
03:13application, I'm calling the GetData method three times.
03:17Once for the Wind Speed, once for the
03:20Air Temperature, and once for the Barometric Pressure.
03:24And I’m taking those objects and adding them to my dictionary.
03:28Then, in the coded lines 52 and 53, I’m once again using the
03:33Java Script Serializer class, and serializing
03:36the data as a Java Script object.
03:39And then I’m returning that data to the requester using
03:43Context.Response.Write, a standard method of the asp.net framework.
03:50And that's how the data is coming back.
03:53I'll once again call the web service with valid dates,
03:56and each of these objects represents an item in the dictionary.
04:01The three items taken together are passed back and serialized.
04:06And that's how i'm getting my JSON formatted content.
04:09There's one other thing to explain here and
04:12that's how I'm aggregating a range of dates,
04:14rather than one date at a time and I'll describe that code in the following movie.
Collapse this transcript
Calculating statistics for a range of dates
00:00So far I've described how I configured my web service, and how I wrote the code
00:05to get the data and then format it as JSON to send back to the requester.
00:11Now I'll describe how I'm handling the aggregation of
00:14values for more than one date at a time.
00:17And for this, I'll go to the GetData method of the feed manager class.
00:23Just as with the Console application, I have these
00:25three constants, Wind Speed, Barometric Pressure and Air Temperature.
00:32And each time the GetData method is called, I'm using the start date, the
00:36end date, and the data type to go get the data from the cache.
00:41Now this is a different condition than the console application.
00:45Remember in my introduction I said I didn't want to do all
00:48these operations synchronously in the web service, so in this code I've established
00:53a rule that says if any particular combination of date and data
00:58type isn't represented in my local data cache, I won't try to respond.
01:03Instead, I'll just throw an exception.
01:07And because the code that you see starting at line
01:0933 isn't wrapped in a try catch block, the exception
01:13will bubble up to the get lake stats method of
01:16the primary service class, and it'll be exposed to the requester.
01:21Let's see what happens in that condition.
01:24I'll go back to the browser.
01:26And this time, I'll pass in a value of 4-1-2014.
01:34And this time, I get an exception.
01:36File not found: Wind_Speed, 3/22/2014.
01:41After a bit of investigation.
01:43I figured out, that that data wasn't
01:46actually even available, on the remote web service.
01:49And so that's a condition, that my application has to be able to deal with.
01:54My rules say, that if the user requests a particular range of dates, I either have
02:00to aggregate the values from all those dates,
02:03or tell the requester, I can't do it.
02:05And tell them, why.
02:07And so that's why this exception is appearing.
02:10I don't have the data for that date, so
02:12I'm not going to try to aggregate the values.
02:15If I tried, it would be inaccurate,
02:18because I don't have all the available data.
02:21So back to the code.
02:23In this code, I'm starting with a range of dates, a starting and an ending
02:28date, and I'm getting the lines of text from the method get data from cache.
02:33This is in the cache manager class for the web service.
02:37In this version of the cache manager I'm looping through all the dates.
02:42For each date I'm getting the file name
02:44for the current date and the current data type.
02:47If the file doesn't exist, this is where that exception is being thrown.
02:53I'm creating the error message file not found, telling the requestor
02:57what data type and date weren't available, and throwing the exception.
03:02But if I do have the file available in the local data cache, I'm using a file
03:07stream and a stream reader to read the
03:09file and add those lines to the overall collection.
03:14Once I finish the loop I have all the data
03:17for the current data type for the entire range of dates.
03:21And the cache manager can then return the file lines.
03:27Going back to the feed manager class, which is where I called that from.
03:31Once I have all the lines, I can then actually do the aggregation.
03:36And this code is pretty much exactly the same as in the console lab.
03:40I'm looping through the lines and parsing them and
03:43grabbing the numeric values and adding them to a list.
03:46I'm calculating the mean using the average method, and calculating the
03:51median using the same code as before and returning the result.
03:56And then in the service class, I'm taking that result,
03:59packaging it up into the dictionary as I described before.
04:03And after I've collected all of the data, I'm
04:06serializing that into JSON, and returning it to the requester.
04:11And that's all the code I need to create the web service.
04:15Now currently, my console application that's gathering the data
04:19from the remote web service and saving it locally
04:22is living on my local development computer, and my
04:25web service is being hosted in a Microsoft Azure website.
04:29So the question becomes, how does that cache
04:32data get from one location to the other?
04:35I haven't solved that in this particular example because my goal was to show
04:39you the C# code, but in a complete deployment, I would host both the
04:45Console application and the web service on a single machine, or I would set
04:49up the Console application to post the data to the web service, storage area.
04:55For that purpose I might decide at that point
04:57to use a relational database instead of a file-based cache.
05:02But the code that I've provided has solved all of the important parts of this
05:05challenge, using C# and the .NET framework to gather data from a remote website.
05:13Aggregate it, and then return the results as a
05:16web service to a requester using the JSON format.
Collapse this transcript
Problem Two: Image Analysis
Identify the image subset
00:06Hello and welcome to Code Clinic.
00:08My name is David Gassner.
00:11Code Clinic is a monthly course where a unique problem
00:14is introduced to a collection of Lynda.com authors.
00:17In response, each author will create a solution
00:20using their programming language of choice.
00:23You can learn several things from Code Clinic:
00:26Different approaches to solving a problem,
00:28the pros and cons of different languages,
00:31and some tips and tricks to incorporate
00:33into your own coding practices.
00:37This is a problem centered around image analysis.
00:40In one sense, this is simply data analysis.
00:44Images are really nothing more than
00:46specialized and well-defined sets of data.
00:50An image consists of pixels.
00:53Pixels consist of data, representing a color,
00:57and in some cases, transparency.
01:01The pixels are arranged in rows and columns.
01:04When assembled correctly they represent an image.
01:09Our brains are very good at recognizing
01:11patterns, but computers are not.
01:15Think about CAPTCHA security devices,
01:18those puzzles you sometimes see when logging into a website.
01:23The CAPTCHA asks what letters and numbers are in the image.
01:27Information is obscured by random lines,
01:30or sometimes overlapping transparent blocks of color.
01:35All of those intersecting shapes make it
01:37difficult for a computer program to separate
01:40the background noise from the actual data.
01:44Another example is the test to determine color blindness.
01:48Letters and numbers are hidden in
01:50a circle filled with different color dots.
01:53If you're color blind, you won't be able to see the numbers.
01:57For a computer program this can be incredibly difficult,
02:01as it requires detecting an edge,
02:04as well as recognizing the overall shape.
02:07It's difficult even for the most advanced programmer.
02:11In this problem we're trying to solve
02:14a common issue for many photographers: Plagiarism.
02:18A photographer will take a picture and post it on the
02:20Internet, only to discover someone has stolen their image
02:24and placed a subset of that image on their website.
02:29For example, here is an image,
02:32and then a cropped version of the same image.
02:36It would be extremely handy to have a program searching
02:39the Internet for cropped versions of an original image,
02:43so that a photographer could protect their rights.
02:46In fact, Google Image Search will do just that,
02:50but we're curious how it works
02:52and what the required code might look like.
02:56Here's the challenge: Given two images,
03:00determine if one image is a subset of the other image.
03:05We'll assume that they're both JPEG files,
03:07that the resolution is the same, as well as the bit depth.
03:12We've provided a set of images.
03:14The program should determine which images
03:16are cropped versions of other images.
03:20Perhaps you'll want to pause
03:21and create a solution of your own.
03:24How would you solve the problem?
03:26In the next videos I'll show you how I solved this challenge.
Collapse this transcript
Overview of my first solution
00:00- [Voiceover] Working through this challenge
00:01required two different solutions.
00:04I'll start by describing the scenarios I'm trying to cover
00:07and then show you first solution
00:09that only handled one scenario.
00:12We're working with two photos that we want to compare.
00:16One larger and one smaller.
00:18In one scenario, the simplest of them,
00:21the smaller photo is simply cropped from the larger one
00:25and no other changes are made to the smaller photo.
00:28So essentially, the pixels in the smaller photo
00:31exactly match some pixels in the larger photo.
00:36The more complex scenarios though, are these.
00:39Perhaps, the smaller photo was both cropped
00:42and then scaled or resized.
00:45In this event,
00:47the series of pixels that started off in the larger photo
00:50will have changed in the smaller photo in some way
00:54because the photo will be stretched or compressed.
00:57Either way,
00:58the pixels will occur in a slightly different order
01:02or in a slightly different number than in the original photo
01:06and in a more extreme circumstance
01:09the smaller photo might be cropped, scaled, and rotated
01:13making an exact match of the pixels between the two photos
01:16much more complex.
01:18My first programming solution
01:20covered only the first scenario,
01:23where the smaller photo is cropped from the larger photo
01:26but no other changes are made
01:28and this was my strategy.
01:31First, I converted the images to sets of strings.
01:35Let's take a simple image.
01:37Each image consists of rows and columns of pixels.
01:42Each pixel is defined by a red, a green, a blue,
01:46and an alpha channel.
01:48Alpha means transparency.
01:51Each of these channels is assigned a numeric value.
01:55In C Sharp you can convert an image to a bitmap object
01:59and then get a pixel at a particular position in the image.
02:04This code, for example, gets a reference to the pixel
02:07in the top left corner of the image
02:10at an X position and a Y position of zero
02:13and in this image this is the result.
02:15I'm getting an alpha, a red, a green, and a blue color.
02:20Now that's a long string
02:21and that's not what I want to work with in my comparisons.
02:24Instead, I added a little bit of code
02:27to get the pixels hash code.
02:30The hash code is a pure numeric value.
02:33It contains the same values as that longer string
02:36but it's a smaller string
02:38and so it will be faster and easier to work with.
02:43So let's take a large image.
02:45I'll take this larger image
02:47and convert it completely to strings.
02:50It's sort of like in the "Matrix"
02:53now instead of seeing actual colors,
02:55I'm seeing values that the computer can understand.
02:59In this case, numbers converted to strings.
03:02Then I did the same thing to the smaller image.
03:05Once again, converting it to a set of strings
03:09and then when I compare the two, I can find the match.
03:13Now this will only work again
03:15where the smaller image doesn't have any other changes
03:18other than the cropping
03:21and here's what ended up happening.
03:23It worked for simple cropped photos
03:26but it doesn't work
03:27if a cropped photo is also scaled or rotated.
03:31So let's take a look at my code for the first solution.
Collapse this transcript
Selecting image files to compare
00:00The exercise files folder for this challenge
00:03includes two folders that have Visual Studio solutions
00:06and one folder named "Images."
00:10The Image folder contains three sub-folders.
00:14"Original" contains original images with their full sizes.
00:20There's a "Scaled" folder that contains the same images
00:23scaled down to ten percent of their original size.
00:26I'll explain later how I'm using those.
00:30And finally, there's a folder named "Web."
00:33This folder contains two very small images.
00:37This one has a resolution of 267x400 and a DPI of 72.
00:44It's a very small image.
00:47And this image is cropped from the original image.
00:51It has a resolution of 267x265
00:55and in fact, it comes from the very top left corner
00:58of the original image.
01:00These are the images I'll use
01:02to demonstrate this first solution.
01:06To open the Visual Studio projects, you can either use
01:10one of the pay-for-license versions of Visual Studio
01:13or you can use the free Visual Studio Express 2013
01:17for Windows desktop.
01:20To open the project, just double-click the "Solution" file.
01:25This application is built with the WinForms framework.
01:29I could have also used WPF
01:31or Windows Presentation Foundation
01:33or built this as a Windows 8 Modern app.
01:37I've chosen to use WinForms because it gives me
01:40the broadest access to the classes I'm going to use.
01:44I'll begin by showing how I'm selecting images to compare.
01:49I've included a file dialog component
01:51as an invisible component in this application.
01:56And when the user clicks one of these links,
01:58they can select an image.
02:00The application asks them to select a large image
02:02and a small image.
02:06There are two field declarations called "largeFileName"
02:08and "smallFileName."
02:11And when the form loads,
02:12I'm initializing the appearance of some of the components.
02:16There are two methods that are called,
02:19one for the large link and one for the small link.
02:22In each case, I use the file dialog component, open it
02:27and then wait for the user to respond.
02:30After the code continues, at line 39 in this method,
02:34I then evaluate what the user selected
02:37by looking at the dialog boxes FileName property
02:41and its ToString method.
02:43And, if the ToString value is not a blank string,
02:47that means the user selected a file.
02:50I save the filename, use a label to display
02:54the location and name of the file that was selected
02:57and then, use a picture-box component
03:00to display the image that's been selected.
03:04After I've done all that, I call a method
03:05called "validate selections."
03:08And here, I examine the two filenames.
03:11If the user has selected both files,
03:14then I enable a button that they can click
03:16to launch the comparison operation.
03:20So, I'll demonstrate this application.
03:24When it opens, I click the "large image" link
03:27and I can choose one of the large images.
03:31I'll go to my "original" folder under Images
03:34and choose this image.
03:37And then, I'll click the other link
03:39and choose a small image.
03:42Now that I've chosen both images,
03:44the "Compare Images" button is ready to use.
03:48And I'll show you what happens
03:49when the user clicks that button, in the next movie.
Collapse this transcript
Comparing images with strict pixel matching
00:00-So far I've described the basic structure
00:02of this application, how it selects images
00:06and gets ready to compare them.
00:08Now I'll describe the code that's extracting
00:11strings from the images, and then comparing
00:13the images to each other.
00:16When the user clicks the compare button,
00:19they're running this method,
00:20named compare button click, and here
00:23are the steps it follows.
00:25At lines 76 and 77, I'm turning the files
00:29that are stored on disk into bitmap objects,
00:33and I'm using the bitmap classes constructor
00:35to do that.
00:38Then before I start operating on those objects,
00:40I'm initializing a progress bar component.
00:44Notice at line 81 I'm setting the maximum value
00:47for the progress bar to the sum of the heights
00:51of the two bitmaps.
00:53I'm going to be scanning the bitmaps one line
00:56at a time, so by adding the two height values
00:59together, I'm telling the progress bar that I'll
01:02be incrementing it once for each line of pixels.
01:07Then I set the initial value to zero,
01:09and make the progress bar visible.
01:12Next I'm calling a method called get rows.
01:16The get rows method accepts a bitmap object,
01:19and I'll place my cursor on the name of the method,
01:22and press F12 to jump to it.
01:25The get rows method creates a list object
01:28that will contain string values.
01:31Essentially, I'm going to turn each image
01:33into a list of strings, and each of those strings
01:37will consist of the hash-codes for the pixels.
01:40One string is for one row of pixels.
01:45I'll create one string builder object
01:47and then use it for the lifetime of this method.
01:50And now, I'm ready to loop
01:52through the bitmap's rows.
01:55There are two for loops here, an outer loop,
01:57and an inner loop.
01:59The outer loop is for the rows.
02:02I'll execute that row once for each row of pixels.
02:06And the inner loop is for individual pixels
02:08within a row.
02:10So in the outer loop I'm starting by clearing
02:12the string builder.
02:14It doesn't have any string now.
02:16Then at line 114, I'm looping through the pixels
02:20for the current row.
02:22Within the loop, I'm getting the hash-code
02:25for the current pixel.
02:26Using the get pixel method and the get hash-code
02:30method that I described in an earlier movie.
02:34The hash-code is an integer, but I'm appending
02:37it to a string builder object, and that turns it
02:40into a string.
02:42Because I'm looping through all the pixels
02:44for the current row, by the time I'm finished
02:46with the inner loop I now have a single string
02:49consisting of string representations
02:52of all the pixels in the row.
02:55Once I've finished creating that string, I add it
02:58to the rows list using rows dot add, and then
03:02I increment the progress bar by one.
03:05So each time through the row of pixels,
03:07the progress bar will increment.
03:10Once I'm done with the outer loop, I'm finished
03:12turning the image into a list of strings,
03:15and I return that list.
03:17So now let's go back to where I called that method
03:20at lines 86 and 87.
03:23I now have two lists of strings, and at line 91,
03:28I'm calling a method named is subset, and I'm
03:31passing in the two lists.
03:34So I'll jump to that method.
03:37In is subset, I'm starting by creating two integer
03:40values named matches and misses, and then I'm
03:44going to loop through the rows of the small image,
03:47and match them against rows of the large image.
03:51Once again, there are two loops here,
03:53this time for each loops.
03:55There's an outer loop to loop through the small
03:58images rows, and an inner loop
04:00for the large images rows.
04:03For each row in the small image, I start
04:05by assuming that I won't make a match,
04:08and then I loop through the rows of the large
04:10image and I see if each row contains
04:13the small image row.
04:15If I get a match on line 136, then I say
04:19that the match is true, meaning I got a match,
04:22and I break out of the inner loop.
04:25Then at lines 142 to 145, I evaluate what happened,
04:30and if the match is true, I increment the matches
04:33value, and if it's false, I increment the misses value.
04:37Once the outer loop is done,
04:39I've finished my comparison.
04:41I've compared all of the rows of pixels
04:43in the small image to all the rows of pixels
04:46in the large image, and notice, I'm not looking
04:49for an exact match.
04:51I'm asking whether the large image row
04:53contains the small image row.
04:56Finally, at the end of this method, I do a
04:58calculation, looking at the matches and the misses,
05:02and finding out what percentage of matches I got.
05:05That's the calculation at line 149.
05:09There's a bit of de-bugging code
05:10to see what happened, and then I return
05:13a bullion value determined by matching
05:15the percentage of matches that I got
05:17to the threshold that I set at the top
05:20of the code.
05:22The threshold is set way at the top to point 9,
05:26meaning if I got a 90% match, I'm going to say,
05:29yes, those two images match each other.
05:33So now let's see what happens.
05:37For this, I'm going to use the web images,
05:40and I'll warn you that if you try to use
05:42any of the other images in my image set,
05:45it won't work.
05:46I'll describe why in a moment.
05:49I'll go to my web folder and choose
05:50yellow pansy dot jpg for my large image,
05:55and I'll choose yellow pansy cropped
05:56for my small image, and I prepared these images
05:59exactly for this test.
06:01I know for sure that this is almost an exact match
06:04to the large image.
06:07I'll click compare images, and I get the message
06:09that the small image is a subset of the large image.
06:14But now let's take a look at some images
06:15that won't work this way.
06:18I'll go to my scaled images, and I'll choose
06:22the big picture of the parrot.
06:25Then for the small image, I'll choose this one
06:28that has the letter A at the end of the name.
06:30And that, a cropped and scaled image, it's
06:34not an exact match, because it hasn't just been
06:37cropped, it's also been re-sized.
06:40I'll try to compare the images, and I get
06:43a negative result.
06:45So think about why this is happening.
06:49When I cropped the small image, and then,
06:51before I save it, I scale it, make it either
06:55larger or smaller, the software that I use
06:58has to fill in what's changed.
07:00Especially if I make it larger, the software
07:03has to decide whether to duplicate pixels,
07:06or, through some sort of logic, add pixels
07:09of completely different colors that let the
07:12human eye see a blended image.
07:15Either way, the two strings just aren't going
07:17to match anymore, and this can get even
07:20tougher when you're dealing with images that
07:22have been rotated or otherwise changed.
07:26So this is a good solution, but only for a
07:28very small set of images, only those images
07:32that have been cropped, but nothing else
07:34has been done to them.
07:36To handle more complex situations,
07:39I'll need a completely different solution.
07:41I'll show you what I came up with
07:43in the following movies.
Collapse this transcript
Overview of my second solution
00:00As I've previously described, the process
00:02of comparing photos gets much more complex
00:06when a photo has both been cropped
00:08and then resized, rotated or both.
00:12Here are two photos where one is a subset of the other.
00:16The photo on the right was cropped from
00:18the photo on the left, but then it was scaled.
00:21And for any particular row of pixels,
00:24there won't be an exact match, either in
00:26the number of pixels or their composition.
00:29So doing a simple string matching, like I did
00:31in my first solution, just isn't going to work.
00:37The problem gets even more complex when you're
00:40dealing with images that might have been rotated.
00:43For example, here's a large image of the Statue of Liberty.
00:47And here's a cropped image.
00:50But you can see right away that the image
00:51has been flipped, not just rotated,
00:55and there definitely won't be an exact match.
00:58Or the color-blind test where the pixels
01:01might match exactly, where there might
01:04be an exact match in the colored sections,
01:06but if any changes have been made to the tinting
01:09of the colors, once again, it'll be hard to find.
01:13This is a problem that people
01:14have written dissertations about.
01:17In fact, in my searches I found complete dissertations.
01:22This is an example of an article that was written
01:24by a number of computer scientists, describing
01:27one approach to sub-image detection and retrieval.
01:32If you read through some of this article,
01:34you'll find that it's a big challenge.
01:37It's computationally intensive, it takes a lot
01:41of computer power to go through an image,
01:44identify what are called "key points,"
01:47and then to match the images, even when
01:50a sub-image has been changed in some way.
01:53This article describes a process where the images are turned
01:57into data sets, which are stored in very large databases,
02:01and even then, the matching takes quite a bit of time.
02:06Or this article, which defines another algorithm.
02:11Reading through these articles, it became clear
02:14that creating my own algorithm to solve
02:16this problem was going to be very time intensive.
02:19And I couldn't be sure that I'd be able
02:22to solve it without building an entire database.
02:26So I went looking for existing
02:28solutions for the .NET Framework.
02:31Here's one solution that some of the authors
02:33who are working in Code Clinic will use.
02:36It's a library called ImageMagick.
02:39It's available for multiple operating systems, and it does
02:43have tools for finding sub-images in main images.
02:48The challenge for me, because I'm
02:49working in C Sharp, is figuring out how
02:52to integrate this into a .NET-based program.
02:56I found some .NET wrappers for the ImageMagick
02:59library, but none of them included the right components.
03:05So after looking a little more, I found AForge.
03:09AForge is a framework for .NET that includes
03:13many libraries for managing images
03:16and handling other advanced techniques.
03:19The library is already built for .NET, so it's
03:22easy to integrate into a .NET application
03:25where you're programming with C Sharp or Visual Basic.
03:30To integrate the library, I created a new
03:33version of my solution, and I've opened it up here
03:36by opening the CompareImagesAForge solution.
03:40Then I went to the Tools menu, to the
03:44Library Package Manager, and I selected
03:47Manage NuGet Packages for Solution.
03:51I clicked on Nuget.org, and then
03:54I searched online for AForge.
03:59I found all the libraries that are
04:01available for the AForge framework.
04:04I chose this one: AForge.Imaging.
04:09I imported the imaging library, then,
04:12because it was dependent on other libraries,
04:15I also automatically got the core
04:17library and AForge.Math.
04:21Then I was able to use tools from the AForge
04:24library to compare images to each other.
04:28I'll show you how I used those tools in the next movie.
Collapse this transcript
Comparing images with AForge.Imaging
00:00- As I described in the previous movie,
00:02the key to my second solution
00:04is the AForge library.
00:07An open source framework
00:09that lets me do image comparisons,
00:11and has a lot of other capabilities.
00:14So now, I'm working in the Compare Images AForge solution
00:18that I've opened up in Visual Studio
00:20and as with the last solution,
00:22you can either open it in Visual Studio Professional
00:25or one of the other advanced versions,
00:28or you can open it in the free version,
00:30Visual Studio Express 2013 for Windows Desktop.
00:35I'm going to start by running the application
00:37and showing you a successful match.
00:41I'll choose a large image
00:43and for this experiment, I'm going to choose this one,
00:46a picture of the wedding.
00:49Then I'll choose a small image.
00:51And this is a cropped image
00:53that's also been scaled significantly.
00:56It's been resized to be much larger
00:58than the version it was cropped from.
01:01If you look very closely though,
01:03this is definitely a crop of that first image.
01:06It's coming from this doorway right here.
01:10Now I'll click the button labeled Compare Images
01:13and I'll wait a few moments.
01:15Notice that I'm using the scaled versions
01:17of the images.
01:19The images that have been reduced in size.
01:22And the reason I'm doing that
01:23is because the AForge library,
01:26while it worked with the very large images,
01:28took a long time.
01:31And I see that I got back a correct response.
01:34A match was found between the sub-image
01:38and the original image.
01:40So now let's take a look at the code that's doing this.
01:44I'll go back to Visual Studio and look at my code
01:48and the first part of the code in Form 1.CS
01:51is pretty much the same as in the first solution.
01:55It's letting me choose the images I want
01:57to compare and displaying them on the screen.
02:01But when I compare the two images
02:03by clicking that button,
02:05here's the code that I'm actually running.
02:07And it turns out to be a tiny amount of code.
02:11On line 75 and 76 I'm once again
02:14turning my images into bitmap objects.
02:18And then, I'm using a couple of classes
02:21that come from the AForge.imaging library.
02:25The exhaustive template matching class
02:28is doing the hard work.
02:30When I instantiate that class,
02:32I'm passing in a float value, .9
02:36and that means I'm asking for a match
02:39to a degree of certainty of 90 percent.
02:43That's a pretty high level.
02:45Now, when I clicked the Compare button
02:47I was showing this processing string,
02:50but notice that I took away the progress bar
02:52from the earlier solution.
02:55And that's because
02:56the method that I'm about to call, Process Image,
02:59from the exhaustive template matching object,
03:02is a black box.
03:04I haven't yet found a way
03:05for it to report to me
03:07how far along it is in its process.
03:10I'm simply calling it and getting back a result.
03:14And so, because I can't make the progress bar increment
03:18I won't use it at all.
03:21But now, when I call this method,
03:23I'm passing in the large bitmap and the small bitmap.
03:27And I get back an array.
03:29The array contains instances of the template match class.
03:33Once again, this is a class from AForge.
03:37And here's the rule,
03:39this array will contain one object
03:41for each match it finds.
03:44When you're comparing two images to each other
03:47you're really looking at two possibilities.
03:49Either no matches or one match.
03:53If you were dealing with vector images,
03:55where let's say, the small image was a single shape,
03:58and that image might recur multiple times
04:01in the original image,
04:03then your array could contain more than one object.
04:06But for our application,
04:08we can expect either zero or one.
04:11So after I've run the process image method,
04:14I then examine the array,
04:15which I've named Matches,
04:17and I look at its length.
04:19If the length is greater than zero,
04:22that means I found a match.
04:24And if the length is zero,
04:26then I didn't find a match.
04:28And then I'm finding out
04:29where the two images matched
04:31by looking at the match objects rectangle property
04:35which has a location property,
04:37which has x and y values.
04:40And I'm using that information
04:42to tell the user what I found.
04:45And that's all the code.
04:47Once again, this is a very complex problem,
04:50but this AForge library has solved it for me.
04:54So now that we know how the code works,
04:57let's run it on a few more images.
05:00This time, for the large image I'll choose the parrot image,
05:03and then I'll choose the sub-image.
05:07Notice the A at the end of the file name.
05:10That means that this image is a crop
05:12of the original image.
05:14But like that doorway in the wedding shot,
05:16it was scaled after it was cropped.
05:19And this was the set of image
05:21that my original solution failed to find a match in.
05:25But now I'll compare the two images
05:28and because of the amount of color and detail
05:30in these images,
05:32this process takes a little bit longer
05:33than the first one.
05:35But when the comparison is complete,
05:38I'm told that the images match.
05:41I'm told that the sub-image occurs in the original image
05:45at an x position of 145, and a y position of 11.
05:51But now let's compare an image that doesn't match.
05:54This image might look, at first glance,
05:57like it's a sub-image of the first,
05:59but there are some significant differences.
06:02Take a look at how far open the bird's beak is,
06:05and compared to the original image
06:07you'll see that that's different,
06:09and you'll see on the smaller image
06:11that the feathers on the back of the bird's neck
06:13are standing up a little bit,
06:15and that's not the case in the original image.
06:19And this time, when I compare the two images,
06:22I'm told instantly that a match wasn't found.
06:25And I know that that's correct.
06:28This is library also turns out to be pretty good
06:30at not getting fooled when it just sees similar colors.
06:35Here's a smaller image that might look,
06:37at first glance, like it's a sub-image,
06:39but if you look carefully at the clouds,
06:42you'll see that it's actually a very different image.
06:45And when I compare the images,
06:47it takes a little bit longer,
06:49but it pretty quickly comes back
06:51and tells me these images don't match.
06:55But that takes us to the real problem images.
06:58I'll start with the Statue of Liberty.
07:00And this is an example where the image
07:02has been cropped, but then flipped.
07:05And this is where we run past the capabilities
07:08of the AForge library.
07:10At least, in the way that I'm currently using it.
07:13It takes a little bit longer
07:14to do the processing than it did in the previous examples,
07:18but then it says, "Couldn't find a match."
07:22And it also fails to find a match
07:24when it looks at images
07:26where either the tinting has changed
07:28or where it has to find some kind of edge.
07:31When I compare these two images,
07:33I'm told instantly that the match wasn't found.
07:37But the AForge library actually handles
07:40a lot of the problems that I was looking at,
07:43especially where I'm looking at images
07:45that have been cropped and rescaled.
07:48If you're running this code on your own computer
07:51try it on your own images
07:52and see what results you get.
07:55Just as I did, I recommend taking any images
07:58that are in their original raw size,
08:01and significantly scaling them down,
08:04reducing their size to reduce the amount
08:07of computational fire power that you need.
08:10You don't need the images to be large
08:12to get a good match,
08:14but you do need a great algorithm,
08:16and the AForge library provided that for my solution.
Collapse this transcript
Problem Three: Eight Queens
A classic CS interview question
00:00(Intro music)
00:07Hello, and welcome to Code Clinic.
00:09My name is David Gassner.
00:12Code Clinic is a monthly course where a
00:14unique problem is introduced to a collection
00:17of Lynda.com authors.
00:19In response, each author will create a solution
00:22using their programming language of choice.
00:25You can learn several things from Code Clinic.
00:27Different approaches to solving a problem,
00:30the pros and cons of different languages,
00:32and some tips and tricks to incorporate into
00:35your own coding practices.
00:38This month, we're working on a classic computer
00:40programming problem called "The Eight Queens."
00:44This famous problem is often used during interviews
00:48or to demonstrate the utility of a computer language.
00:52It requires an understanding of recursion
00:54and algorithm design,
00:56and can be quite useful as an exercise
00:58in learning to program solutions for complex problems.
01:03This problem was proposed by Max Bezzel in 1848
01:07and solved by Franz Nauck in 1850.
01:11The problem is simple.
01:13Start with a chess board and eight queens.
01:15Then, set up the board so that no two queens
01:18can attack each other.
01:20There's more than one solution.
01:22Our challenge is to find them all.
01:25We already know there are 92 possible solutions
01:28and we already have examples of the solutions
01:30in several computer languages.
01:34If you've never played chess,
01:35you'll need to understand that a queen can
01:37attack by moving an unlimited number of spaces
01:41in three directions,
01:43horizontally, vertically, and diagonally.
01:47This means that no two queens can share a row
01:49or a column.
01:51Nor can they be located diagonally from each other.
01:55In the following movies, I'll show you my solution
01:58to the eight queens problem.
01:59I'd encourage you to also look at the solutions
02:02from other authors in the Lynda.com library.
02:06You will be able to compare different author styles
02:08and different languages.
Collapse this transcript
Overview of my solution
00:00- This programming challenge does not specify
00:03the framework or application style.
00:06It's a challenge of logic,
00:08and so it's up to the programmer
00:10to decide what kind of application
00:11they want to build.
00:13My sense of the application requirements
00:16are that I'll need very fast calculation
00:18and sorting capabilities,
00:20and I'll need a rich desktop interface
00:22to display the results to the user.
00:25There's no particular reason to build
00:27this as, say, a mobile or a web application
00:30and so I'll default to the desktop
00:32environment where I have the most tools.
00:35On Windows, you can choose from
00:37either Windows Forms or WinForms
00:40or WPF, Windows Presentation Foundation.
00:44For this solution, I'm working with Windows Forms.
00:49In deciding how to build this application,
00:52I had to decide what sort of logic to use.
00:55The logic you use is called the algorithm,
00:58and there's been a lot of research done
01:00on the Eight Queens problem.
01:02There are a number of approaches you can take.
01:05One is called the exhaustive search
01:07algorithm where you basically create
01:09every combination of different placements
01:12and then evaluate whether each one works.
01:16The problem with this approach is that there
01:18are a ridiculous number of possibilities,
01:21over 200 trillion of them.
01:23So it would be very computationally
01:26expensive to try to do this.
01:29Another approach is called iterative repair.
01:33With this logic, you start by placing
01:35queens in all locations on the board
01:38and count the available conflicts, and start
01:41removing pieces until you find solutions.
01:45Once again, it's computationally expensive
01:47and would take a lot of code.
01:50I'm using an algorithm or a logic
01:52pattern known as backtracking.
01:55With this approach, you place the queen
01:57in a particular location.
01:59You would start, for example,
02:00by placing a queen in the top left corner
02:03of the chessboard and then you begin
02:06to test other possible locations.
02:09You might place a queen next to the first one,
02:12below it, or to the lower right,
02:15and then for each placement,
02:16you test whether it's going to work.
02:19You're checking for conflicts
02:21and there are three kinds of conflicts:
02:23horizontal -- if the new queen is on the same row;
02:27vertical -- if it's in the same column;
02:29and diagonal.
02:31When we get to the actual solution,
02:33I'll show you an equation you can use
02:36to test for diagonal placement.
02:39Once you've found an acceptable
02:41location for the next queen,
02:43you move to the next level.
02:45You have eight queens to deal with
02:47and therefore eight levels.
02:49By doing a recursive search,
02:52you can find all of the 92 possible distinct solutions.
02:57If you're dealing with a visual
02:58environment such as I am with Windows Forms,
03:01you can then display the result to the user.
03:04In my application, I'm doing all of
03:07the calculations up front
03:09and storing all of my 92 solutions,
03:12and then the user can trace through
03:14the solutions by clicking a button
03:16in the application,
03:18showing the first solution,
03:20the second, the third, and so on.
03:24Now, I've built my solution in C#,
03:27but I need to give acknowledgments
03:29where they're due.
03:31I learned from a solution that was
03:32provided on the Code Project website.
03:36This solution was built for .NET
03:38but with a different programming language,
03:40VB.NET, and it was built by a
03:43programmer named Ali Tarhini.
03:46As I said, the solution is available
03:47on the Code Project site,
03:50an open source code-sharing site
03:52and the code is openly licensed
03:55so you can download it,
03:56experiment with it,
03:58make changes in it,
03:59and share it as you see fit
04:01as long as you give proper acknowledgment.
04:04The VB.NET version of the solution
04:06is available at this URL:
04:08http://www.codeproject.com/Articles/32235/
04:10This solution does more than my solution does.
04:13In addition to presenting the
04:1492 possible solutions,
04:16it lets the user experiment
04:19by placing items on the chessboard
04:21and then seeing whether they can
04:22find a solution manually.
04:25The point here is that rather than try
04:28to build the code from scratch myself,
04:30I found some solutions that worked.
04:33I looked at a number of others
04:34before I came upon this one,
04:36and I learned from the successes of
04:38other developers and then adapted
04:41that code to create something that
04:43worked for my specifications.
04:45This is an important lesson
04:47in software development.
04:49You don't have to re-invent every wheel yourself.
04:52In many cases, the problems you're
04:54trying to solve have already been
04:56solved by somebody else,
04:58and you can learn from those solutions
05:00and adapt them for your own needs.
Collapse this transcript
Opening the solution in Visual Studio
00:00- As with all of my solutions for the Code Clinic series,
00:04I've provided my solution, as exercise files,
00:08which you can download from the lynda dot com website
00:10and extract anywhere on your hard disk.
00:13I've placed them on my desktop in an exercise files folder.
00:18within that folder,
00:19you'll find a folder named eight queens problem,
00:22and within that, the visual studio project.
00:26To open the project, you'll need either a pay for license
00:29version of Visual Studio, I'm using Professional,
00:33or you can use the free version,
00:35Visual Studio Express 2013 for Windows desktop.
00:41From the menu select file, open, project or solution.
00:46Navigate to the exercise files folder,
00:49then to the eight queens problem sub folder,
00:52and then the solution file.
00:55And that will open the project
00:57in your copy of Visual Studio.
01:00Then to test the solution,
01:02click the start button on the tool bar.
01:05You'll be running the application in debug mode,
01:08so you can add break points,
01:10step through the code
01:12and inspect variables,
01:14to help you understand how the solution is put together.
01:17As the application starts up,
01:19it displays a chess board.
01:22Then, it calculates all of the solutions.
01:26Down at the bottom of the interface there's a message,
01:28click to show one of 92 solutions.
01:31And a button.
01:32And each time I click a button, a new solution is displayed.
01:38There are only two graphic files involved in the solution.
01:42The chess board itself, and the chess piece image.
01:45And I'll show you how I've implemented those
01:47a little bit later.
01:50I'll click again, and there's solution number two,
01:52three, four, five and so on.
01:56And if you were to click 92 times,
01:59you would then cycle back to the first solution.
02:02So that's how you can open the project from the
02:04exercise files in your copy of Visual Studio
02:08and test out the solution.
02:11Next, I'll show you how the user interface is put together.
Collapse this transcript
Building the user interface
00:00- When I run my application, a chessboard
00:03is displayed and then each time I click the next
00:06solution button, I'm seeing the graphic of the
00:09queen image being displayed in eight positions.
00:12I'm going to start my walkthrough of the code
00:15by showing how I'm handling those images.
00:18In a .NET application, you can deal
00:20with image files in a couple of different ways,
00:22but the most efficient runtime approach is to
00:25compile the images into the project as resources.
00:30I've done this in my project and I'll show you
00:32where it's done by going to the menu choice
00:35Debug and then EightQueensProblem properties.
00:40Within the properties interface, I'll click
00:43on resources, and here are my two
00:45graphic files, the chessboard and the queen.
00:50If you want to use graphics in this way,
00:53you can go to this resources panel,
00:56then choose add resource, add existing file.
01:00Then you can choose the graphic
01:02file and add it to the project, and then you can
01:06address the image as a .NET object.
01:10I'll show you where this is done in the chessboard.cs file.
01:16I'll open up the partial class chessboard,
01:18and I'm looking at it as C# code, and I'll scroll down
01:23a bit to a method named DrawBoard.
01:26This method is called each time
01:28I need to repaint the screen.
01:31And right here on line 71, there's a
01:34reference to resources.chessboard.
01:37That's the compiled graphic.
01:40So, to display that image I'm creating a .bmp object
01:44wrapped around the chessboard resource,
01:46and then drawing it on the screen,
01:48and scaling it to the size of the rectangle.
01:52That's the size of the application window itself.
01:56Then, I move down here a little further and show that
02:00I'm using the QueenImage as a resource here.
02:03This is the code that's deciding
02:05where to place the queen image.
02:07It's going to place eight of these queens for each solution.
02:10And I'll go through some of this other logic later.
02:13But the important thing here is that
02:15I have a reference to the resource
02:18and once again, I'm using the DrawImage
02:19method to draw it on the screen.
02:22This all happens incredibly fast, and again,
02:26it happens each time I'm displaying
02:28a new solution to the problem.
02:31So, now I'll run the application again,
02:33and I'll click the button, and I see both
02:36the chessboard image and the eight
02:38instances of the QueenImage resource.
02:42So, that's how the user interface is being displayed.
02:45Next, I'll get to the code that's
02:47calculating the 92 distinct solutions.
Collapse this transcript
Calculating the 92 distinct solutions
00:00- As my application starts up, it automatically loads
00:03form one, the automatically generated form that's created
00:08when you create a new Windows form project in Visual Studio.
00:12The form one load method starting at line 22
00:14is executed automatically.
00:18There's a private field up here,
00:20an instance of my chess board class.
00:23It's instantiated at line 19.
00:26Then, a method of that chess board class
00:29called "GetSolutions" is called.
00:31I'll dig down into that method in a moment
00:33because it's critical to understanding this solution,
00:36but I'll also point out that there's a method here
00:39that adds the chess board to the current form.
00:43And that's how the chess board is being displayed.
00:47Now let's look at the GetSolutions method.
00:50This is where the guts of the solution resides.
00:53I'll go do the definition of that method,
00:56which is in my chess board class.
01:00In this class, there's a list named Queens.
01:04The purpose of this list is to contain
01:07eight instances of another class called Queen.
01:11I'll go to the definition of the list,
01:14and then go to the definition of the class Queen.
01:19The Queen class has two private fields
01:21called mrow and mcolumn, both integers,
01:24a constructor method that initializes those values to zero,
01:29another constructor that lets you create an instance
01:32of the Queen class and set those values,
01:35and then public getters and setters for both fields.
01:39This is a pretty straightforward data class.
01:43Each instance of the class will represent one queen
01:46on the chess board.
01:48I'll close that and I'll go back to my chess board class.
01:52Then, from there, I'll find the GetSolutions method again.
01:58When the GetSolutions method is called,
02:00I first clear that list.
02:02It's empty.
02:04And then there's another method named "ResetCells."
02:08That method is emptying an array
02:10of Boolean values called "cells."
02:14It's declared with this syntax:
02:16bool, meaning Boolean, then a pair of brackets and a comma.
02:21In C Sharp, that means, "Create a two-dimensional array."
02:27The underlying private field named mcells
02:30is being instantiated here.
02:33It's being declared with eight positions in each dimension,
02:37one for the horizontal and one for the vertical.
02:40So an item with index of zero and zero would be the top left,
02:45and an item with an index of seven and seven
02:48would be the lower right.
02:50Those two values together would then equate
02:52to a true or false value.
02:55If it's true, that means a queen is in that position.
02:58And if it's false, that means
03:00that square on the chess board is blank.
03:03That's how we're going to track our solutions.
03:06Now I'll go back to the GetSolutions method again.
03:12After calling ResetCells to reset that array,
03:15I call a method named DrawBoard.
03:18This is the method where I'm displaying the chess board.
03:23When I start up the application,
03:24the mcells's multidimensional array has 64 items.
03:30Eight times eight.
03:32Initially, they're all false.
03:34And so, when I draw the board, I get to this code,
03:38which is looking for items in the mcells's array
03:40that are true.
03:42It doesn't find any, and so no queens
03:44are drawn on the board.
03:46This explains why, as the application starts up,
03:49it can display the chess board but no chess pieces.
03:56Once again, let's go back to the GetSolutions method.
04:00We've cleared to the queens list.
04:02We've reset the cells, and we've drawn the board.
04:05Next, we're going to do some looping.
04:09First, there's a for loop that's looping eight times,
04:12from zero through seven.
04:14So the list will contain eight queen objects.
04:19Then there's a bit of code that's setting
04:20the row and column of each queen object.
04:24At this top level of the code, the row's being set to zero,
04:28meaning the first row,
04:29and the column is being set to the next available column.
04:34Then there's a call to a method named PlaceQueen.
04:37That's where we'll go next.
04:40The PlaceQueen method is designed to be called recursively.
04:43That is, the first time you call it, it'll call itself
04:46again and again and again, so that it's called eight times,
04:51once for each row or column in the set.
04:54This first bit of code will only be called
04:56on the eighth iteration,
04:58and it's only going to decide that a cell is valid
05:02if it passes some tests.
05:04But the important bit of code that's called
05:06every single time is down here.
05:09Each time this bit of code is called,
05:11it's going to place a queen in a particular position.
05:15Then it's going to check that position
05:17and make sure that it hasn't created a conflict.
05:21The logic for that is in the method CheckAll,
05:24and we'll jump to that.
05:27Here's the code that's checking for an existing conflict.
05:31It's looking for three possible conflicts:
05:34horizontal, vertical, and diagonal.
05:38The horizontal check is pretty easy.
05:41You look at the row properties for each queen,
05:43and if they match, that means it won't work.
05:47Then you do the same thing for the columns.
05:50For the diagonal check, there's a calculation here.
05:53It's saying, if the sum of the row and the column
05:57for the first queen match the sum for the second queen,
06:01or the row minus the row of the first and second queen,
06:05and the same calculation for the columns,
06:08if any of those come out true, then we say,
06:11this position isn't going to work and we return false.
06:15But if we pass all of those tests, we return true.
06:19That means that we found a queen that can be put
06:22in that position.
06:24Going back to the PlaceQueen method
06:25from which this was called,
06:27we say that, if the CheckAll method worked,
06:30then we decide we placed a queen and we're ready
06:33to go on to the next one.
06:35Finally, when we get to the last recursion,
06:38that is, the eighth call, or level number seven,
06:41taking into account the zero-based offset,
06:44then we can decide whether a queen can be placed
06:47in a particular position.
06:49Here we're back to the mcells's two-dimensional array.
06:54If the row and the column worked out well,
06:56then we set the value in that position to true,
06:59and otherwise, we set it to false.
07:02After all the calls to the PlaceQueen method,
07:05we'll end up with 92 arrays in this solutions list,
07:10and that's the list that we're looping through
07:12when we're displaying them on the chess board.
07:17So, if I start the application,
07:22as the application starts up, all the looping
07:25from calling PlaceQueen and the checking method and so on
07:28is being done, and I have an array with my solutions.
07:32And I can loop through and display them.
07:35So, finally, in the last movie of this chapter, I'll show you
07:39how I'm managing that solutions list at run time
07:42and letting the user see each of the possible solutions.
Collapse this transcript
Displaying the solutions
00:00-The chessboard class in my solution
00:02contains all of the logic, both to solve the
00:05problem, and to contain the solutions.
00:08And the solutions are stored in
00:10this list object, called msolutions.
00:13It's a list, and each item in the list
00:16is a two dimensional array.
00:18Within the array, just like M cells,
00:21there are 64 items, indexed by row and column.
00:26And each item has a true or a false value.
00:30Access to the msolutions field is
00:32provided by the solutions list,
00:35which has a getter and a setter.
00:39Now, as the application starts up,
00:42the code in form one is executed,
00:45and it calls the get solutions method
00:47to find all of the solutions.
00:50So by the time the chessboard is
00:52displayed, all of the solutions are
00:53already found, and they're available through
00:56the solutions property of the chessboard object.
01:00Now, as the user clicks the button, they'll
01:03execute an event handler method, and this is
01:06where we're seeing each solution in turn.
01:10So here's the logic that's doing that.
01:13It's in the method button one click.
01:17At the top of this class, there's a field
01:18called solution number, initialized to zero.
01:23Each time the user clicks the button,
01:25we go get the solution from that list,
01:28using solution number as an index.
01:32And that's the two dimensional array
01:33that we're going to represent visually.
01:37We're taking that two dimensional array
01:39and saving it as the cells field.
01:41Then we're calling the method draw board.
01:44Which is a member of the chessboard class.
01:48And once again, this code was executed
01:50as the application started up.
01:52But this time, it's dealing with a particular
01:55array, one of the 92 solutions.
01:59And here's the code that's deciding
02:01where to place each queen.
02:05First of all, there's a bit of conditional
02:06code, making sure that the chessboard
02:09isn't completely invisible.
02:12Then, there's code to draw the board
02:13itself, that I've previously described.
02:16Here's where we're using the chessboard resource.
02:19Then, there's two four-loops, one inside the other.
02:23One representing rows, the other columns.
02:27Within the inner four-loop, we look at
02:29the item in the m-cells array.
02:33Then I'm checking whether the item
02:35at that position of the array
02:36has a value of true or false.
02:39If it's true, then the following code will be executed.
02:44First, we create a rectangle object.
02:48And there's a calculation here to place the
02:50rectangle on the chessboard in the position
02:53that equates to this row and column.
02:57Then I'm creating a bit map,
03:00and I'm using the queen image resource
03:03and drawing the image to fill that rectangle.
03:07In a full solution, this block of code
03:10will be executed eight times,
03:12once for each of the eight queens
03:14that's being placed on the chessboard.
03:18And then, finally, at the end of the code,
03:20there's a call to the invalidate method,
03:22which means repaint the user interface.
03:27This bit of code is executed each time
03:29the user clicks that button.
03:32This time, and this time, and this time, and so on.
03:38The queen graphics are being placed
03:39using that logic, that's deciding where
03:42the rectangle will be created, and then
03:44filling the rectangle with the queen image resource.
03:49So there are three major steps to
03:51understanding this application.
03:53And I've described them each in turn.
03:56First, generating and managing the user interface.
04:00Then, the logic that finds all the 92 distinct
04:03solutions to the eight queens problem.
04:07And then more user interface control
04:10that allows the user to cycle through
04:11the solutions and see them one at a
04:14time in the Windows forms application.
04:18I encourage you to explore this code further,
04:21and experiment with it, trying different things.
04:24And as I mentioned earlier in this chapter,
04:27this application was based on an existing
04:29application that was built with VB.net.
04:32If you're a Visual Basic programmer,
04:35try that version of the application out,
04:37and you can learn a little bit about
04:39solving complex algorithm problems
04:42using any .net based language.
Collapse this transcript
Problem Four: Accessing Peripherals
Building a musicial instrument using mouse movements
00:06- Hello and welcome to Code Clinic.
00:08My name is David Gassner.
00:11Code Clinic is a monthly course
00:13where a unique problem is introduced
00:15to a collection of lynda.com authors.
00:18In response, each author will create a solution
00:20using their programming language of choice.
00:23You can learn several things from Code Clinic.
00:26Different approaches to solving a problem.
00:28The pros and cons of different languages.
00:31And some tips and tricks to incorporate
00:33into your own coding practices.
00:35This month, the problem is to create
00:37a musical instrument controlled by a mouse.
00:41Move the mouse up and down to change the pitch.
00:44Move it side to side to change the volume.
00:47The instrument is silent until we
00:49click and hold one of the mouse buttons.
00:52Letting go of the button turns off the musical note.
00:55It's a simple request, but because it requires
00:58access to the mouse, it may be difficult
01:01or impossible in some languages.
01:03In all cases, we'll explore some ways
01:05to solve the problem and learn why they might not work.
01:10Thomas Edison observed. "Negative results are just what I
01:13"want, they're just as valuable to me as positive results.
01:17"I can never find the thing that does the job
01:19"best until I find the ones that don't."
01:23You may want to take some time
01:25and solve the problem yourself.
01:27In the next videos, I'll explain
01:29how I answered this challenge.
Collapse this transcript
Overview of my solution
00:00- In this month's challenge,
00:02I'm creating a simple musical instrument,
00:04a Windows application that responds to mouse clicks
00:08and moves, and plays an audio sound in response.
00:12This application has some specific requirements.
00:17It needs access to all the audio capabilities of a computer,
00:20and it has to know how to handle
00:22media files and other sounds.
00:25So I need complete access
00:27to the computer's operating system.
00:30That points me to desktop environments.
00:32So for this application, I'll be working in
00:35Windows Presentation Foundation, or WPF.
00:40I did three different versions of this application,
00:43starting from a very simple strategy
00:46to a more complex and powerful one.
00:49In the first application, I took advantage of
00:52Windows Systems Sounds, things like beep.
00:56These are exposed in the .NET API as console methods.
01:00This bit of code, System.Console.Beep,
01:05passing in values of 440 and 3,000,
01:08means make a beeping sound at 440 hertz.
01:12That's the pitch, or frequency, of the sound.
01:16It will have a duration of
01:183,000 milliseconds, or three seconds.
01:22Here's what the finished application looks like.
01:25This application just has a single action.
01:28It plays a beep, and it does it
01:31when I click this large button.
01:33(beep)
01:37The advantages of using System Sounds
01:39are that they're very simple and easy to code.
01:42That action took exactly one line of code,
01:45but it's very static.
01:48With System Sounds, you can't control the volume
01:50or the pitch, or frequency.
01:53This technique, while it's easy,
01:56doesn't satisfy the application requirements.
02:00The second strategy I tried was using an audio file.
02:04I started with a WAV file and I fed it
02:06into a class called MediaPlayer.
02:09Then I played the WAV file,
02:12and allowed the user to adjust the volume.
02:14Here's what it looks and sounds like...
02:19Now, when I click the mouse the sound will start,
02:23and when I release the mouse the sound will end.
02:25(beep)
02:27(beep)
02:29When I move the cursor from side to side,
02:32it'll affect the volume.
02:34(beep fading in and out)
02:42The advantage of this strategy, is that I'm using
02:45a class that's built into the .NET framework
02:48and I have control over the sound's volume.
02:51But I still haven't satisfied the application requirements
02:54because with the MediaPlayer, I can't control the frequency,
02:58also known as the pitch.
03:01So I tried a third strategy.
03:04In this example, I'll generate my own custom sound
03:08instead of starting with a WAV file.
03:11I'll be using a library called NAudio.
03:15This library is completely free and open source.
03:18So you can incorporate it into your applications
03:21without paying any royalties.
03:23The NAudio library is able to generate sounds from scratch,
03:27and then you can affect both volume and pitch as needed.
03:31Here's what it looks and sounds like...
03:36Once again, in this application, when I hold down
03:39the mouse button I will play a tone,
03:41and when I release the mouse, it'll stop.
03:44(beep)
03:46But now I have control over both volume and pitch.
03:49I'll control the pitch by moving the cursor up and down,
03:52and the volume by moving side to side.
03:55(beep fluctuating in pitch)
04:00(beep fading in and out)
04:05So the NAudio library satisfies
04:07the application requirements.
04:09It gives me control over the volume
04:11and frequency of the sound.
04:13There's still one drawback.
04:15As you play with this application, you'll see that
04:18it doesn't handle frequency changes quite perfectly.
04:21You sometimes hear little clicks and pops in the sound.
04:25When I get to that code, I'll describe where those clicks
04:28are coming from, and what you might have to do
04:30to solve this problem completely.
04:33Before we get to the code, I want to
04:35acknowledge a few different sources.
04:37As is so commonly the case in programming,
04:40I was only able to solve this problem
04:43with the help of existing code.
04:45First, there's the NAudio library.
04:48I was able to download it into Visual Studio
04:51using the NuGet framework that's built into
04:53the Visual Studio IDE.
04:56But you can also download NAudio from its website
04:59at naudio.codeplex.com.
05:02At that website, you'll find the downloadable binaries
05:06plus documentation and examples of all sorts of things
05:09that you can do with this incredibly valuable library.
05:13Also, my application depends highly on code
05:16that was created by a programmer named Mark Heath.
05:19In fact, you'll see that some of the code is taken
05:22verbatim from a blog post that he put up in 2010.
05:27You can find that blog post at this URL.
05:30So let's get started looking at the code
05:33that I used to solve this month's challenge.
Collapse this transcript
Playing system sounds
00:00- The Exercise Files for this chapter
00:02are arranged in a single Solution File
00:05that you can use in Visual Studio.
00:07You can either open them up in a Visual Studio Edition
00:10that's paid for license,
00:12I'll be using Visual Studio Professional,
00:15or you can use the Free Edition,
00:17Visual Studio Express 2013 for Windows Desktop.
00:23To open the Solution, go to the Audio Players Folder.
00:27You'll find an Audio Players Solution File,
00:30and then one chapter for each project.
00:33I'm going to start describing the Beep project,
00:36but there are also Folders for PlayWav
00:39and for the project that uses the NAudio library.
00:43To open the Solution, just double-click the Solution File.
00:47If you have a correct version of Visual Studio installed,
00:50it should open automatically in that version.
00:53Notice that the Beep project
00:55is automatically set as the StartUp Project.
00:58You can change that by right-clicking
01:01and choosing Set as StartUp Project,
01:04and so, when I click the Start button,
01:06I'll fire up that application.
01:09As I described previously, this is a very simple application
01:13that plays a single System Sound.
01:16When I click the large Beep button,
01:18that will play a System Sound,
01:21a beep sound that's set at a frequency or a pitch
01:24of 440 Hz and will last for three seconds.
01:29(beep plays)
01:33Let's take a look at the code that plays that sound.
01:36I'll open the project here in the Solution Explorer,
01:40and then double-click on MainWindow.xaml
01:44Windows Presentation Foundation
01:46defines its user interface in xaml,
01:49the xml-based language that affects the marker.
01:53Here's the definition of the button in xaml.
01:55It has a name of PlayBtn and, when I click it,
01:59it executes this method, PlayBtn_Click.
02:05I'll press the F12 key and that will take me
02:08to the xaml files code behind file,
02:11named Windows.xaml.cs
02:15You can also reach that file through the Solution Explorer
02:19by opening up the tree control next to MainWindow.xaml
02:22and you'll find the code file there.
02:25Here's the implementation of the method,
02:28and this is all the code you need
02:29to play a simple System Sound.
02:31I have two integer variables.
02:34The frequency is set at 440,
02:37and the duration at 3,000.
02:39That's a frequency or pitch of 440 Hz
02:43and a duration of 3,000 milliseconds, or three seconds.
02:48Then, to make the sound play,
02:50I call the method System.Console.Beep
02:54and pass in those two values.
02:56If I wanted to make this sound a little bit shorter,
02:59I could change the duration here.
03:01I'll make it one second, and run the application again,
03:06and now, when I click the button, it'll be shorter,
03:10(beep plays)
03:13and I'll return it back to 3,000,
03:16and that's all there is to it.
03:18It takes, really, just a single line of code
03:21to play a System Sound.
03:23That's great, it's easy to do, easy to debug,
03:27and almost foolproof, but it doesn't do
03:30what we need the application to do.
03:32We need to play a tone that we can affect
03:34after it's started, and we need to be able to change
03:38both the volume and the frequency,
03:40and this coding technique doesn't allow that.
03:43So next, I'll take a look at a slightly more complex,
03:47but much more powerful, application
03:50that uses a pre-existing sound file,
03:52in this example a wav file,
03:55and a .net class called The Media Player.
Collapse this transcript
Playing an existing audio file
00:00- Going from simple to complex and from weak to powerful,
00:04my next step is to work with preexisting sound files.
00:08And here I'll use the project named Play Wave File.
00:13I'll right click on it and choose Set a Startup Project.
00:17Then I'll click the start button
00:19and that runs that application.
00:23As I described previously, this application
00:26uses a preexisting sound file and lets the user
00:29affect the volume by moving the cursor from side to side.
00:33When I hold down the cursor, it plays the sound
00:36and when I release it, it stops.
00:38(Beeps)
00:41And now I'll show the volume control.
00:43(Beeps louder then softer)
00:51So here's how this application is put together.
00:54First, here's the wave file.
00:58You can create this wave file
00:59with any audio system you like
01:01and all you need to do to use it
01:04in your application is to add it
01:06to your Visual Studio project.
01:09And here's the code that I'm using.
01:12I'll open the file MainWindow.xaml.cs
01:17and then I'll look at it in full screen.
01:21At the top of the class, I'm creating an instance
01:24of the MediaPlayer class.
01:27This class is a part of the .net framework.
01:31You don't need to import any libraries
01:32but you do need to include a using statement
01:36for System.Windows.Media.
01:39Then I've declared a couple of other fields,
01:41a boolean value named isMouseDown
01:44and a point named lastPoint.
01:48I'll be using these values to track the mouse gestures.
01:52In the constructor method for the MainWindow,
01:55I'm initializing my media player, that's on line 23.
02:00I call the player object Open method
02:03and I pass in a Uri wrapped around the name of the file.
02:07Notice that when I construct the URI I'm passing
02:10in an argument of UriKind.Relative.
02:13That means look for the file in this project,
02:16not in an explicit place on the hard disk.
02:20That makes this project portable from computer to computer.
02:25Then I'm initializing the volume of the player at 0.5.
02:28The volume can be set in a range from zero to one,
02:32and it supports, obviously, fractional values.
02:35Then I'm calling a method named DisplayValues.
02:40That's at the bottom of the code.
02:42It gets the current volume from the player object,
02:45does a little bit of math on it to make it presentable
02:48and then displays that value
02:50followed by a percentage character.
02:53And that's how I'm displaying
02:55the current volume on the screen.
02:57Let's see that in action.
02:59I'll run the application again.
03:01And there's the value that was calculated
03:03by the DisplayValues method
03:06and here's how it behaves
03:08as I change the volume at runtime.
03:10(Beeps)
03:13Now we'll go back to the code.
03:17Next I have a couple of event handler methods
03:20named MouseLeftButtonDown and MouseLeftButtonUp.
03:24In each of these, I changed the value
03:26of isMouseDown so I can track
03:28whether the mouse is currently down at any moment,
03:31and then I either play or stop the player.
03:35Notice I don't have to reload the wave file every time.
03:38That was already done in the constructor method.
03:41So when I play it, I'm playing the preselected tone
03:44and when I stop it I'm just stopping it.
03:48Then finally the code for managing the volume
03:50is in the method WindowMouseMove.
03:54Once again, this is an event handler and it's reacting
03:57whenever the mouse moves up or down or from side to side.
04:02First I'm getting the current mouse position.
04:06That's returned as a point object.
04:09A point has X and Y values to indicate the horizontal
04:12and vertical position of the mouse cursor.
04:17Then I have a bit of conditional code.
04:20Notice that I'm only processing the mouse move
04:22if the mouse button is currently held down.
04:26If the mouse button is up, I'm ignoring it.
04:30The next step is to get the delta,
04:32the amount of change that happened
04:34since the last mouse move.
04:37And I'm getting both a vertical
04:38and a horizontal Delta because I want
04:41to find out whether the mouse moved mostly up
04:44and down, or mostly side to side.
04:48Then I have another conditional clause starting at line 55
04:51and I'm asking the question, if the value
04:54of the horizontal Delta is greater than the value
04:58of the vertical Delta, that means it was a side to side move.
05:03I'm actually going to ignore mouse moves
05:05that are mostly vertical at this point.
05:07Then I take the Delta value, the amount
05:10that the mouse moved from the last position
05:13and I divide it by 200 and then I add
05:17that value to the current value.
05:20Now here's why I'm doing that division operation.
05:23The Delta comes to me as an integer value,
05:26but I need to express the volume as a fractional value
05:31and I chose the value 200 because that allows me
05:34to affect the volume relative to the amount
05:36of space I have from side to side of the window.
05:41So I'm adding the Delta to the volume.
05:44If the mouse moves to the right,
05:46I have a positive Delta that will increase the volume
05:49and if it moves to the left, I'll have a negative delta
05:53and that will decrease the volume.
05:56Then once again, I call the DisplayValues method
05:58to update the label at the top of the screen
06:01and show the user what the value is of the current volume.
06:05Finally, I save the current point using
06:09that field lastPoint, so the next time I execute
06:12this method I'll be able to get new Deltas,
06:15new calculations of the change in the mouse position.
06:20And so let's once again run the application
06:23and see how all that code is working.
06:26I'll click the start button,
06:28and once again I'll hold down the mouse
06:31and move the cursor from side to side.
06:33Notice when I move it up and down,
06:36nothing will change.
06:38(Beeps)
06:44So this application is solving part of the problem.
06:47I'm allowing the user to control the volume of a tone,
06:51but I still haven't satisfied the entire specification
06:54because I also need to allow the user
06:57to control the pitch and I'll show you
06:59how I did that in the next project.
Collapse this transcript
Creating sounds with the NAudio library
00:00- The last I'll describe is named NAudioWaveProvider.
00:05I'll set it as the Startup Project
00:08and then I'll run it.
00:10And in this project the user can control
00:13both the volume and the pitch.
00:15The initial pitch is 440 Hertz
00:18and the volume is showing "Off".
00:21But when I hold the mouse down
00:23the tone starts and I can control the volume
00:26moving the mouse from side to side,
00:28just as I did before,
00:30but now I can also control the pitch.
00:32(digital tone)
00:42So let's see how this project is put together.
00:45First I'll show you that I've added a library
00:48using the NuGet Framework.
00:51I went to the NuGet Package Manager
00:54and I installed the NAudio library.
00:58NAudio is an open-source library
01:01for controlling audio on Windows-based computers.
01:04It can be used with .NET applications
01:06using a variety of languages.
01:09Most of the documentation and examples
01:11you find are built with C#
01:14but you also see examples that are built with C++.
01:18So, once you've added the library to your project
01:22you can use its classes,
01:24and here's what I've done.
01:27First, I have a class named WaveProvider32.
01:32As I described previously,
01:34this code is right from a blog post
01:36by the programmer Mark Heath
01:38and you'll see the link to that blog post
01:40here in the code.
01:43This is an abstract class
01:44that's implementing an interface named IWaveProvider.
01:49In the constructor method it's setting default values
01:53and setting the WaveFormat,
01:55and it implements a Read method.
01:58It executes some calculations
02:01and then returns a simple WaveProvider.
02:05Then there's a subclass named SineWaveProvider,
02:09and here's where the default values
02:11for the frequency and the amplitude are set.
02:15Notice that the default frequency is set at 440
02:19and this would explain why the tone sounds
02:21like it does when you first play it.
02:24Then there's an override of the Read method,
02:27and once again it's doing some basic math
02:30and returning the sampleCount.
02:33Those two classes are pretty much the same
02:35as what Mark Heath provided in his example,
02:38and here's how I've used these classes in my application.
02:43I'll go to my main window XAML file.
02:46The user interface is similar to the WaveFile example,
02:50I once again have labels to show
02:52the user what's going on,
02:54and I'll be affecting these labels at runtime.
02:57I'll go directly to the C# code behind file
03:01and show that I've declared an instance
03:03of the SineWaveProvider32 class.
03:06That's this custom class that I already showed.
03:10Then just as I did in the previous project,
03:13and then just as I did in the previous project,
03:16at lines 19 and 20,
03:18I have fields that allow me
03:19to manage the mouse gestures.
03:22In the main window's constructor method
03:25I'm initialing my SineWaveProvider object
03:28and setting its WaveFormat.
03:31And then I'm creating an instance
03:32of a class named WaveOut,
03:35this is essentially the sound player
03:37in the NAudio framework.
03:39It does the same sort of work as the media player
03:43but it can work with custom sounds,
03:45whereas the media player has to work
03:47with pre-built sound files.
03:50I'm initializing the player by providing the WaveProvider,
03:54that's how my defined tone is getting to the player,
03:57and then I'm setting the volume.
03:59Notice that in this example I have the suffix "f",
04:03meaning that this is a float value,
04:06and that's because the volume property
04:08of the WaveOut object is also a float.
04:12Then I have a call to my DisplayValues method.
04:15Here's the implementation
04:16of DisplayValues for this application.
04:19First, I have slightly different logic here.
04:22If the mouse isn't currently down
04:25I set the label that's showing the volume
04:27to a string of "Off",
04:29but if the mouse is down
04:31then I detect the volume,
04:33do a little bit of math,
04:35and then display the volume
04:36followed by the percentage sign.
04:40Next I determine the current frequency,
04:42and notice I'm going right back to the WaveProvider
04:45to find out what the current frequency is,
04:48and once again I'm displaying it.
04:51So now let's go back
04:52and analyze the MouseMove method,
04:55that's where the bulk of the work is happening.
04:58Just as with the WaveFile project
05:00I'm determining whether the mouse is currently down
05:03and I'm only processing the mouse gestures if it is.
05:08I'm once again getting the horizontal and vertical deltas,
05:11and I once again have some conditional code.
05:14But this time I'm handling both
05:16horizontal and vertical changes.
05:19For the horizontal changes I'll affect the volume.
05:23Notice there's some more complex code here.
05:27I discovered in my experimentation
05:29that if I tried to set the volume of the player object,
05:33that's the WaveOut object,
05:35to a value greater than one
05:37it would throw an exception,
05:39so I'm saving the current volume before I try to change it
05:43and then I change the volume by the value of the delta,
05:47but if this throws an exception
05:50I restore the old volume
05:51and ignore everything else.
05:54Now, if the user moves the cursor up and down,
05:57that means they wanna change the pitch,
06:00and here's the code that's doing that.
06:02The pitch is known as the frequency,
06:05so just as I did with the volume
06:07I'm first saving the current frequency
06:10and then I have a little bit of code
06:12that's making sure that the frequency
06:14hasn't gone below zero.
06:17I discovered, again in testing,
06:19that when I allow the frequency to go into a negative value
06:23it did some very unexpected things.
06:25You'll see, if you experiment with it,
06:28that it loops back to a higher value
06:30instead of stopping at zero or throwing an exception,
06:33so I have an explicit check in my code.
06:36Now, if the vertical delta is greater than zero,
06:40and if the frequency isn't already set at zero,
06:44then I set the increment value to a value of 10,
06:48and notice once again that's a float, not an integer,
06:51otherwise I set it to a negative 10,
06:55and then finally I affect the frequency.
06:58Notice I'm using the "-=" operator.
07:01Again, experiment with that to see
07:03what would happen with "+=".
07:05Finally, if I hit that ArgumentOutOfRangeException
07:09I bail and I reset the frequency
07:11to whatever the last frequency was.
07:15And after all that code is executed
07:17I once again update the screen
07:19by calling my DisplayValues method.
07:22And then, just as I did in the WaveFile project,
07:26I remember the current mouse position
07:28so I can calculate the deltas again
07:31the next time this event happens.
07:34I'll click the Start button
07:36and test the application again.
07:38(digital tone)
07:45Now you might be wondering
07:46why I'm allowing the frequency to jump like that
07:49rather than creating a smooth change
07:51from one frequency to the next.
07:54Let's what happens when I change
07:55the increment values from 10 to one.
08:00I'll change it and start the application again,
08:03and then play the sound.
08:05(digital tone)
08:12And you hear those clicks happen.
08:14That's happening because of a problem
08:16that's common in dealing with audio output.
08:20Changes in the frequency
08:21that are very close together
08:23will frequently emit those sort of clicks,
08:26and to solve this problem I discovered
08:28I would need to move to another language,
08:30I would need to get into C++
08:33where I have lower level access to system audio properties.
08:38So, I decided that this had to be good enough.
08:43I went back and re-read my specifications.
08:46The specification said that the frequency had to change
08:49in reaction to mouse moves,
08:52it didn't say it had to be smooth
08:54and it didn't say how much it had to change,
08:57and so I decided that this
08:58was an acceptable solution.
09:01As with many programming projects,
09:03I found that solving the last 10 percent of the problem
09:07could take as much as 70 or 80 percent of the time.
09:11And so I offer this to the client
09:13and the client says "that's good enough"
09:15and we call it a day.
09:17And now we have a finished application
09:20that creates a simple musical instrument
09:22that reacts to user gestures with the mouse
09:25and affects the volume and the pitch of a musical tone.
09:29(digital tone)
Collapse this transcript
Problem Five: Recursion and Directories
Recursing directories and gathering image data
00:07- Hello, and welcome to Code Clinic.
00:09My name is David Gassner.
00:12Code Clinic is a monthly course where a unique problem
00:14is introduced to a collection of lynda.com authors.
00:18In response, each author will create a solution
00:21using their programming language of choice.
00:24You can learn several things from Code Clinic.
00:27Different approaches to solving a problem,
00:29the pros and cons of different languages,
00:32and some tips and tricks to incorporate
00:34into your own coding practices.
00:38This month, the problem combines two concepts:
00:41Recursion and accessing image data.
00:45Recursion means to repeat something in a similar way.
00:49In programming, recursion means
00:51a function that calls itself,
00:53nesting a call to a subroutine within a call
00:56to the same subroutine.
00:59You'll see this sort of code in the samples
01:01you're about to see from some authors.
01:04Sometimes, however, recursion is handled for you
01:07by the language or application framework.
01:11It's happening in the background,
01:12but your programming job is greatly simplified.
01:17The second part of this month's challenge
01:19is to extract image data from picture files.
01:23JPEG files can contain additional image data
01:26stored as standards called EXIF or ITPC.
01:31EXIF stands for Exchangeable Image File Format,
01:35and is a well-documented standard.
01:38If you have a digital camera or have taken photos
01:40with a newer cell phone camera,
01:43the image probably had EXIF data available.
01:47Using a Mac, you can see this metadata information
01:51by opening the image in Preview.
01:54Opening Tools and the Show Inspector
01:57and selecting the EXIF or IPTC tab.
02:01On windows, you can see metadata by right-clicking an image,
02:05selecting properties and then the Details tab.
02:09You'll see things like caption, dimensions, camera type,
02:13color space, exposure information and other details.
02:18Cell phones will also embed geographic location data,
02:22identifying the longitude and latitude.
02:26This month's challenge is to look through
02:28the example files included with Code Clinic.
02:31Find images, extract the caption from the metadata,
02:35and then reorganize those photos
02:38into an alphabetical folder structure
02:40based on the caption.
02:43As always, you may want to take some
02:45time and solve the problem yourself.
02:48In the following videos, I'll show you how I solved this.
Collapse this transcript
Overview of my solution
00:00- The primary focus of this month's challenge
00:03is reading metadata from image files.
00:06But we also have to handle folder recursion,
00:09that is starting at a top-level folder
00:11and recursing through its subfolder structure.
00:15My solution in C# requires access
00:18to the operating-system resources.
00:21I have to be able to find and read image files
00:24and support other operating-system features.
00:28I also will need to incorporate some third-party libraries
00:31such as Adobe's XMP Toolkit to get
00:34all the data I'm looking for.
00:37The solution for this month's challenge
00:39has been built with Windows Presentation Foundation,
00:42or WPF.
00:44It could have also been built with Windows Forms,
00:47and resulted in pretty much the same output.
00:51The first challenge turns out to be the easiest in .NET.
00:55We have to start from a top-level folder
00:58and then recurse through the subfolders,
01:01and find all of the files in all of those folders.
01:05In some languages, you would create a function
01:08and you would call that function for the top-level folder.
01:11It would then look for all the subfolders
01:14and call itself for each of them.
01:17But it's a lot easier in .NET.
01:19The .NET framework has a method
01:22that handles folder recursion automatically for you,
01:25as long as you configure it correctly.
01:29It's a method called EnumerateFiles,
01:32which is a member of the Directory Class.
01:35It's a static method.
01:37So you call it from the Directory Class directly,
01:40and you pass in the location of the directory
01:42you want to explore, a file pattern if you like,
01:46and then an option called SearchOption.AllDirectories.
01:51It returns an implementation of an interface
01:54called IEnumerable, which in this case is called files.
01:58And then you can iterate through the files
02:01and deal with them one at a time.
02:03It turns out that this solution
02:05is perfect for this challenge.
02:07The list you get back is a flat list.
02:10It isn't hierarchical like the original directory structure,
02:14and that's what you want for this challenge.
02:17So the real work for this month's challenge
02:19went into solving the second part,
02:22extracting image data.
02:25And part of that challenge was pretty easy.
02:28Most of the files that I'll be working with
02:30are in JPEG format, and these files
02:34support the Exif standard.
02:36Exif stands for Exchangeable Image File format,
02:40and it's an international standard
02:42that's implemented across a lot of software platforms.
02:46The .NET API, as accessed from WPF,
02:50lets you easily extract metadata from JPEG files.
02:54You'll use the following classes.
02:57FileStream to open the file,
03:00and then two classes named BitmapSource and BitmapData.
03:04And from there, I'll easily be able
03:06to get the caption from each file.
03:09It's known as the title in this API,
03:12but it's the same information.
03:15The other type of file, however,
03:17took a little bit more work.
03:19PNG files don't support the standard for Exif.
03:24And in fact, there isn't a single universal standard
03:26for metadata in PNG files.
03:29But it turns out that the one PNG file
03:32that I'll be working with uses a format called XMP
03:36that was created and is managed by Adobe Systems.
03:41Adobe offers a free toolkit for developers
03:44called the XMP Toolkit.
03:47It's built for programming in C++,
03:50and Adobe also provides a Java version.
03:53But for C# developers, you'll need something more.
03:57And it turns out there is a publicly available solution
04:01for C# developers to read XMP data from image files.
04:06It's called the C# XMP Toolkit.
04:09It isn't managed directly by Adobe.
04:12Instead, it's an open-source free library,
04:15and you can download it and add it
04:18to your Visual Studio solution,
04:21and then call its methods from your C# code.
04:25It's essentially a C# wrapper for Adobe's XMP Toolkit.
04:30And as you'll see when I review the code for the solution,
04:33you'll actually need the XMP Toolkit DLL from Adobe.
04:38The C# Toolkit doesn't do the work by itself,
04:41it depends on Adobe's code.
04:43And I'll show you how to set it all up in Visual Studio.
04:47To download the latest version of the C# XMP Toolkit,
04:51go to this website at sourceforge.net/projects/csxmptk.
05:00Finally, I actually had to do a little bit
05:02of special work to let the user browse for a folder
05:06in Windows Presentation Foundation.
05:08In an earlier chapter of the Code Clinic series,
05:11I built an application using Windows Forms.
05:14And with just a few lines of code,
05:16I was able to popup a directory browser.
05:20Windows Presentation Foundation
05:22doesn't have that same built-in class.
05:25Now you could go back to the WinForms classes,
05:28but alternatively you can use classes
05:31that are members of a library called
05:32the Windows 7 API Code Pack.
05:35Don't worry that it says Windows 7.
05:38This code works on Windows Vista,
05:40Windows 7, and Windows 8.
05:43It does not however work on Windows XP,
05:46and that would be something to take into account
05:48when you're planning your version of the application.
05:52If you need to support Windows XP,
05:54you would think about changing your application structure
05:57to Windows Forms instead of WPF.
06:01But we're now at a point in the Windows evolution
06:04where three versions of Windows is enough for me.
06:08Windows 8, Windows 7, and Windows Vista.
06:12And now, let's take a look at the code I created
06:14to solve this month's challenge.
Collapse this transcript
Exploring the solution's dependencies
00:00- The exercise files for this solution
00:03contain two sub-folders.
00:05The Images folder has two sub-folders
00:08named problem2 and problem3.
00:11The problem2 folder contains images
00:13from the chapter in Code Clinic on image comparison.
00:18The problem3 folder has the images
00:20from the eight queens problem.
00:23Our goal is to search this entire folder,
00:27including all of its sub-folders,
00:29and make a flat list of all the images
00:32and then list all of their titles.
00:35The other folder within Exercise Files is called Imageinfo,
00:39and this contains the Visual Studio Solution.
00:43There are three sub-folders.
00:45The primary sub-folder is the ImageInfo project.
00:49This is the c# project that I created,
00:52and it's where I put all of my code.
00:55There's also a folder named CsXmpToolkit.
00:59This is the c# Xmp toolkit that I downloaded
01:02from the open source site.
01:05And there's a Packages folder that contains references
01:08to the Windows API code pack library.
01:11I'll be using that to present a user dialogue
01:14to select directories.
01:18To open the solution double click
01:20the Imageinfo solution file.
01:24You should be able to open this solution in any
01:26pay-for-license version of Visual Studio,
01:29or in the free edition Visual Studio Express 2013
01:34for Windows Desktop.
01:37Let's start with looking at the structure of this solution.
01:42The Imageinfo project is set as the start-up project,
01:45so when you click the Start button
01:47you'll be running that application.
01:50When you click Choose Directory you'll see a prompt
01:53and you'll be asked to select a directory.
01:56I'll go back to the Exercise files folder,
01:59and double click the Images folder,
02:02and click Select Folder,
02:05and there's the result.
02:07The application reads all of the files
02:09in the top-level folder and all of its sub-folders,
02:13and then lists them alphabetically,
02:15grouping them by their initial character.
02:19Down toward the bottom you'll see
02:20that there are two images in the T category.
02:26That's all the application does.
02:28I'll close it.
02:30This application depends on CsXmptoolkit,
02:35the open source project.
02:37I'll go back to the Imageinfo project,
02:39then go to Project, add reference.
02:45I'll the Solution category, and see that
02:48the CsXmptoolkit has been added here.
02:54There's one other very important dependency.
02:57This dll Xmptoolkit.dll comes from Adobe.
03:02If you're a c++ developer you could download
03:06the Xmp toolkit SDK from Adobe
03:09and build this dll yourself.
03:11But our goal is to do as much as we can just with c#.
03:15So I downloaded the finished, or pre-compiled dll,
03:19and just copied it into my project,
03:22and then very importantly, I sent a copy to
03:26Output Directory property to copy always.
03:29So when I build the application this dll will be copied
03:33into either the dBug or the release directory,
03:37and it will be available to my application.
03:40If the dll weren't available
03:42the application just wouldn't work.
03:44It's completely dependent on it.
03:47Within the Imageinfo project you'll see the common starting
03:51window named Mainwindow.xaml.
03:55As always you can look at this in design or in xaml view.
04:00The design view doesn't tell you much.
04:02It just shows a button,
04:04but there's also a scroll viewer here,
04:07and within the scroll viewer a text block.
04:10You'll see that information down here in the xaml window.
04:16Finally, all of my custom logic is in this file:
04:19Mainwindow.xaml.cs, the code behind file
04:24for the main xaml file.
04:27Before you continue watching the videos in this chapter
04:30I recommend taking a look at the code
04:32and analyzing it yourself.
04:34See how much you understand about how the code is working
04:37and what it's doing, and then watch the rest of the videos,
04:41where I'll review all of the code
04:43and explain what it does.
Collapse this transcript
Browsing folders with Windows API Code Pack
00:00- In my finished solution, when I click the Start button,
00:04I click a button labeled "Choose directory"
00:07and that pops up this browser window.
00:10It'll always default to the last directory
00:12that was selected by the user, if you
00:15start from the exercise files folder
00:18and choose just the images folder
00:20and then select that folder you'll see a listing
00:23of all of the images but if you repeat the process
00:28and just choose one of the sub-folders
00:30you'll only see the images listed in that sub-folder.
00:34I'll go back to the images folder and down to problem3
00:38and click Select Folder and now
00:41I only see the titles for two of the images.
00:45If I make the window a bit wider
00:48I can see the entire directory name.
00:51So let's start with the code that's popping up
00:53that browsing window that lets the user choose a directory.
00:58As I mentioned previously, when you work
01:01in Windows Presentation Foundation
01:03you don't always have all the tools
01:05that you might of had in Windows Forms.
01:08But it's pretty easy to find replacements
01:10for those common components.
01:14When the user clicks the button
01:15that executes this method named Button_Click
01:19and here's the code that's being executed.
01:22I'm creating an instance of a class
01:24called CommonOpenFileDIalog, when I move the cursor
01:28over the name of the class in the declaration
01:31I see that the class is a member
01:33of Microsoft.WindowsAPICodePack.Dialogs.
01:39Now, how did I get this code?
01:41This came from NuGet, the architecture that lets you
01:45easily find, download and integrate libraries
01:49into your Visual Studio solutions.
01:52I went into PROJECT and selected "Manage NuGet" folders.
01:59And in this dialog box, if you click on "Installed packages"
02:03you'll see the code pack library's there.
02:06Notice that they're labeled with Windows 7
02:09but they work perfectly well with Windows 8
02:11and all the way back to Windows Vista.
02:15So that's where that project came from.
02:18The rest of the code is pretty straightforward.
02:20I create an instance of the dialog,
02:23I set its Title, I set a boolean property
02:26called IsFolderPicker = true, if I don't do that
02:30then I'll be letting the user choose files.
02:33And then I call this ShowDialog method.
02:36If the user successfully selects a folder or directory,
02:41this method will return a value
02:43of CommonFileDialogResult.Ok.
02:47So if I get that value back I know I
02:49have a valid directory name to work with.
02:53Here I'm clearing a collection named data
02:57which is an instance of a Dictionary class.
03:01The Dictionary class contains name value pairs.
03:05And I've defined this Dictionary to contain
03:07items where the key is a string
03:10and the referenced item is a List of strings.
03:14This is how I'll be categorizing my results.
03:18All photos where the first character is A
03:20will go into one list, all those with B
03:23will go into another list and so on.
03:26I'm both declaring and instantiating the directory
03:29as a private field of the class.
03:32So it'll be available globally to all the code in this class
03:36but won't be available anywhere else.
03:39Going back to me Button_Click method,
03:42I clear that Dictionary and then I set another field
03:46value named imageDir, that was a string,
03:49to the value returned by the dialog box.
03:53And then I execute two methods.
03:56AnalyzeImages and DisplayResults.
04:00I'll go over the details of those methods in a moment
04:03but their basic purposes are these,
04:06the AnalyzeImages method finds all of the image files
04:10in the folder structure and then one file at a time
04:14gets its Title and stores that
04:16information in the Dictionary.
04:18Then the DisplayResults method processes the Dictionary,
04:22displaying the data one category at a time.
04:25So in the next couple of videos
04:27I'll show you how that code works.
Collapse this transcript
Retrieving image data from JPG and PNG files
00:00- Once the user has selected a folder to work
00:02with, the next step is to go get all
00:05of the files and analyze them.
00:08That's all done in the AnalyzeImages.
00:12As I mentioned in the introduction,
00:14this is where recursion takes place.
00:17In some languages you would write a method
00:20that analyzed all the files in the current folder and
00:23then called the method again for each of the subfolders.
00:28The .NET APIs though make this very easy.
00:31The EnumerateFiles method has an argument
00:34which allows you to set an option
00:36of AllDirectories and that means go get
00:39all the files for the selected directory
00:41plus its entire subdirectory structure.
00:45You get back a list which is an implementation
00:48of an interface named IEnumerable.
00:52It doesn't matter to you what the actual
00:54concrete class is of that collection.
00:56All you need to know is that you're getting
00:58back files and you can iterate through them,
01:01looping, and handling one file at a time.
01:06So after the call at line 54, I now have
01:09a flat list of all the files, all of
01:12the images that are in all of the directories.
01:16At line 57, I start my iteration.
01:20I'm using a foreach loop, which was
01:23enabled by the IEnumerable interface.
01:26For each file, I get its information using the FileInfo
01:30class and then I examine the file extension.
01:35I'm only going to process files
01:37that have a .jpg or a .png extension.
01:42Any other extension will be ignored.
01:44That's important here because in
01:46an image folder where you might think you
01:48only have image files, you might also have
01:51an invisible database file that Microsoft Windows
01:54frequently generates and you don't want
01:57to try to process that file, it's not going to work.
02:01The continue; keyword means start back
02:03at the beginning of the loop and go to
02:05the next file, we're not interested in this one.
02:10At line 69, I now process an image file.
02:15The first step is to use a FileStream.
02:18I'm instantiating the FileStream inside
02:21a using clause, that will cause it
02:23to close automatically when we're done
02:25with this section of the code.
02:28I'm opening it with read access only.
02:32The next step is to set a variable name title,
02:34which I'm initializing to an empty string.
02:38We'll set the actual title in just a little bit.
02:41Then I'm creating two objects, a BitmapSource
02:44and a BitmapMetadata object.
02:48The first one takes the stream and looks at it
02:51as a graphic and then the second statement gets
02:54the Metadata, the information about the graphic.
02:59The Metadata has a property called Format
03:02which will tell us what kind of file we're working with.
03:05And next we have a Switch statement.
03:08For JPG files, this is very easy.
03:11Because JPG files support the E x i f,
03:15or Exif standard, you'll get a title back
03:18and you can simply save that value.
03:21The work is done.
03:23For PNG files, it's a bit harder but
03:26the CSXmpToolkit makes it actually pretty easy.
03:31First, I'm creating something called an Xmp object.
03:34You can see by moving the cursor over
03:37the declaration that this class is
03:39a member of the CSXmpToolkit.
03:43As is the next one, XmpFileMode.
03:46Once again, I'm placing this code inside
03:49a using statement to make sure that
03:51the Xmp is closed when I'm done with it.
03:55Next, I'm taking the Xmp object
03:57and wrapping it in a class named DublinCore.
04:01The structure of this class is dictated
04:03by Adobe's XmpToolkit, but when you wrap
04:07that around the Xmp, you're actually getting
04:10the data out of the Xmp structure
04:12and now we can get the title of the image.
04:16And you get that with the expression dc.Title.DefaultValue.
04:21Now you might be wondering why
04:23DefaultValue, why not just value?
04:25And the answer is Xmp allows you
04:28to store multiple versions of a title,
04:30one per human language.
04:33So you could have an English version,
04:34a French version, a German version, and so on.
04:39I know that the images I'm working
04:41with only have English versions so I'll use
04:44the DefaultValue property and I'll be
04:47pretty sure I'm getting what I'm looking for.
04:51Finally, I'll store the information.
04:55Working on the current title, I'll extract
04:57the initial character using a substring extension
05:00and then make sure it's uppercase with a call to ToUpper.
05:05Then I'll look at my data collection and ask,
05:07is there already a list that's
05:09indexed by that first character?
05:12And if there isn't, I add the list, that's on line 99.
05:17And finally, on line 101, I add
05:20the current title to that list.
05:23So this method is doing all that work.
05:25Getting the information out of the file,
05:28that's the title, using either
05:30the Exif Metadata or the Xmp Metadata,
05:34depending on the file format, and
05:37then storing the title in the dictionary.
05:40Once the loop is complete, my dictionary will be done.
05:44It'll contain multiple items.
05:47Each item will be indexed by a character
05:49of the alphabet and within that item,
05:51there will be a list of image titles.
05:55My next step is to display the results
05:58and I'll show you how I did that in the next video.
Collapse this transcript
Displaying the results
00:00- So far, we've asked the user to select a directory,
00:03and then we've analyzed all the files in
00:06that directory and its sub directories.
00:09We're ready to display the results.
00:12That code is here in the display results method.
00:16The first step is to get a list of all the key values.
00:21We're getting that from this expression, data.Keys,
00:25and we're extracting it as a list of strings.
00:29Then a simple call on line 112 to the list sort method,
00:34will place the list of keys in alphabetical order.
00:38Then, we're displaying the directory name by setting
00:41the text property of the text block,
00:44which has a name of txtOutput.
00:47By setting that value, we're clearing
00:49any previously displayed results.
00:52All the rest of the code will use the plus equals operator
00:56to concatenate values to that text property.
00:59Next, there's a for each loop, looping
01:02through the list of key values.
01:05For each of those values, we're displaying
01:07the value and then adding a line feed.
01:11Then, extracting the list of titles that's referenced
01:15with that key, and on line 123 sorting that list.
01:20This makes sure that the image
01:21titles are also in alphabetical order.
01:25Finally, there's a nested loop,
01:26this time operating on the titles list.
01:29On line 126, we're outputting a bullet
01:32character, the title, and another line feed.
01:37Finally, at the end of the for each loop, one more
01:39line feed for good measure to separate
01:42the categories from each other.
01:44That's the entire code for the display results method.
01:48It's operating on a dictionary where we know that the keys
01:52of each item are individual alphabetical characters,
01:56and the lists that are referenced
01:57by those keys contain image titles.
02:01Finally, here's the result once again.
02:05I run the application, I choose a directory,
02:09I select it, and I see the results.
02:12I can scroll through and see all the information
02:15that came out of those image files.
02:18So that's my solution to this month's challenge.
02:21Using C# to find all the files within a particular
02:24directory structure, and then extract and display
02:28the metadata, the titles, for each of those images.
Collapse this transcript
Problem Six: Building the Web
Converting static web pages into ASP.NET web forms
00:06- Hello, and welcome to Code Clinic.
00:09My name is David Gassner.
00:11Code Clinic is a monthly course where a unique problem
00:15is introduced to a collection of lynda.com authors.
00:18In response, each author will create a solution
00:22using their programming language of choice.
00:25You can learn several things from Code Clinic:
00:28different approaches to solving a problem,
00:30the pros and cons of different languages,
00:33and some tips and tricks to incorporate
00:35into your own coding practices.
00:39This month, we're working on a problem
00:41common to anyone building a website or a report.
00:44Specifically, merging live data with static template.
00:49Data is only useful when we can effectively
00:52communicate it to someone else, and to do that,
00:55we need to format it into some sort of
00:57meaningful visual representation.
01:00Sometimes this takes the form of a printed report.
01:03Most likely, it means publishing data to a web page.
01:08In this problem, we have the preformatted HTML
01:11web page and some data files in CSV format.
01:16Our job is to write code that will insert the data files
01:19into the web page and apply the proper formatting.
01:24We're going to use the files created by
01:25James Williamson in Chapter 9 of his course,
01:28titled "Dreamweaver CC Essential Training."
01:32James provides all of the material
01:34we need for this challenge.
01:36I invite you to download the example files for that course
01:40if you'd like to attempt your own solution.
01:44In the next videos, I'll show you
01:46how I solved this challenge.
Collapse this transcript
Overview of my solution
00:00- In this month's challenge, I'm going to show you
00:03how I added data from CSV or comma delimited files
00:07into what starts as a static webpage.
00:12C# is used for all of the Microsoft platforms
00:15including desktop applications built with Windows forms
00:19and Windows Presentation Foundation
00:21and modern applications built for Windows 8.
00:25But one of the most common uses of C#
00:28is to work with ASP.NET, the server-side engine
00:32that generates webpages dynamically.
00:35And so this is the natural approach
00:37to applying C# to this challenge.
00:40The first thing I did was to break down the HTML page
00:44into reusable components that I can put together
00:47at runtime with ASP.NET.
00:50This is a normal process in ASP.NET.
00:53You take the parts of the page that will be reused a lot
00:57and you turn them into something called user controls.
01:00And I'll show you how you do that in code
01:02when I get into the code review.
01:05In this case, I'm starting from a static webpage
01:08and I decided to break it down into four parts
01:12that look like this.
01:14First the header, this component
01:17will be used on every page in the entire site
01:20and so will the bottom of the page
01:22which I called the footer.
01:24Then you have the content.
01:26This is the material that's unique to this page
01:29and it's where my data tables are going to go.
01:32And finally, the sidebar.
01:34In the webpage I'm working on,
01:36this is also static content but it's easy
01:39to foresee the possibility that it would become dynamic too.
01:44So, I break the webpage down into these four components
01:48and then I put them together again at runtime
01:51to generate and output a single page.
01:55This allows me to see and work on
01:56just a small part of the page at any given time
02:00and as you'll see when I get into the code,
02:02it allows me to focus just on the task at hand.
02:06Reading the data from the CSV files
02:08and injecting it into the webpage.
02:12The task of reading the CSV files
02:14and getting the data into memory
02:16was accomplished with some common.NET classes.
02:20Here's what the code looks like.
02:23I'm using a class called the StreamReader
02:25to open and read the file into memory
02:28and then I'm storing the data inside a list
02:31and each item in the list is an instance
02:33of a custom class that I define named Course.
02:38The Course has fields or properties
02:40that allow me to store each bit of data,
02:43the name of the course and the number of credits.
02:47Then the while loop is examining the stream
02:50and grabbing data one line at a time.
02:53For each line, I'm splitting the line by the comma
02:56and then storing the values in the data object.
02:59Once I have the data in memory,
03:02the next step is to model the data tables
03:04in the HTML output and I'm doing that
03:07with server-side web controls.
03:10In ASP.NET, a component that starts
03:13with an ASP prefix and has a runat attribute of server
03:18is processed at the server before being sent to the browser.
03:22And here I'm using tags named Table,
03:24TableRow, and TableHeaderCell, all being run at the server.
03:31These are all static values but by using
03:34a server-side table component,
03:36it allows me to add data at runtime dynamically
03:40and I'm doing that with pure C# code.
03:43I already have the data in memory
03:45so for each item in a list, I'm creating
03:49a couple of cells, adding the values from the Course
03:53into those cells, adding the cells into a row
03:57and adding the row to the table.
04:00This, it turns out, is the only C# code that I really needed
04:04in addition to code I used to parse the CSV files.
04:09So this is a natural way to use C#
04:12to take something that starts off as a static webpage
04:16and to turn it into something that's generated dynamically
04:18at the server and then delivered to the browser.
Collapse this transcript
Exploring the solution's files
00:00- The Exercise Files for this challenge
00:02are organized in two folders.
00:05This folder, named "StaticSite,"
00:08contains the original web page from the course:
00:11Dreamweaver CC Essential Training.
00:14The "programs" subfolder has an html page,
00:17and you should be able to open that html page
00:20in Internet Explorer, or in any other modern browser.
00:25The page is organized with a header at the top,
00:28a footer at the bottom with a bunch of links,
00:31a sidebar to the right,
00:34and the page's primary content on the left.
00:38The primary content
00:39displays the curriculum for this program.
00:43In this version of the web page,
00:45the data is embedded in the html file.
00:49You can right-click and choose 'View Source'
00:52to look at the html structure and data.
00:56And if you scroll down to the tables
00:58you'll see the data's right there.
01:01Each course number is wrapped in "" tags
01:04and we'll have to solve that in our C# code
01:07when we inject the data into the web page.
01:12The "StaticSite" folder also contains
01:14subfolders containing various assets.
01:17Including: style sheet files,
01:19fonts,
01:21images, and more.
01:25The other primary folder
01:27in the Exercise Files for this challenge
01:29is named "DynamicSite."
01:32And this is a web page that's been built
01:34in asp.net.
01:37The first folders you see,
01:38with the underscore prefix,
01:40are exactly the same as with the default site.
01:44There's still a cascading style sheets file,
01:47and there are still fonts,
01:48images, and javascript files.
01:51But there are two new subfolders.
01:54The first is named "App_Code,"
01:56and it contains a couple of C# classes.
02:00And the "App_Data" folder
02:02now contains the csv files.
02:05The original two csv files from the course
02:08are both here.
02:10They're called "first_semester" and "second_semester" .csv.
02:14I've also created
02:15a "third_semester" and a "fourth_semester" file
02:19that represent the data that was in the original web page.
02:22Because my goal is for the asp.net version
02:25to look exactly the same as the original version.
02:29You can open this asp.net site
02:32in either one of the pay for license versions
02:34of Visual Studio,
02:36or in one of the free editions.
02:38Visual Studio Express 2013 for web.
02:43To open the site,
02:44just double-click the "DynamicSite" solution file
02:48and if you have the right version of Visual Studio
02:50it should open automatically.
02:53Now, to test this version of the page
02:55click the start button up at the top.
02:58As with the static version,
03:00you can run the asp.net version
03:02in Internet Explorer, or in any other modern browser.
03:06When you click the button,
03:08you'll rebuild the site
03:10and then launch it.
03:12Notice in the web address the url.
03:15That we're no longer accessing the file
03:17directly from the file system,
03:19but instead we're going through a web server,
03:22indicated by the local host address.
03:25That special port might be different
03:27on your computer than on mine,
03:29and that's because it's set dynamically
03:31when you launch the page
03:32and when you launch the developed web server.
03:36I'll scroll down a bit,
03:38and show that the page looks exactly the same.
03:41And then, just to prove it again,
03:44I'll take this version and move it over to the right...
03:48I'll minimize Visual Studio,
03:50and close all these other files.
03:53I'll go to my "Exercise Files" folder,
03:56to the static site,
03:58to the "programs" subfolder,
04:01and I'll open up the static site.
04:04Then I'll drag that version
04:06and put it in its own window over here.
04:09And here are the two versions of the page.
04:12The one on the left is being opened
04:13from the file system,
04:15that's the static page,
04:16and the one on the right is being opened
04:18from the web server.
04:20I'll scroll down to the curriculum section
04:23and I'll do that in this version as well.
04:26And as you can see, they look exactly the same.
04:30So next,
04:31I'll take a look at the code that I used
04:33to transform this static web page
04:36into something that's generated dynamically
04:38on a web server upon request.
Collapse this transcript
Breaking down a web page into ASP.NET user controls
00:00- My first step in transforming this static web page
00:03into an ASP.NET page was to break down the HTML
00:08into parts.
00:09I chose to break down the original web page into four parts.
00:13A header and the footer, a sidebar and the main content.
00:18In the original web page as shown here in TextPad,
00:22all of the markup and all of the data
00:25was in a single file.
00:26That made the file long and fairly complex,
00:29not impossible to deal with
00:31but also not reusable.
00:34If the user wanted for example to reuse the header content
00:37in another page, they would have had to copy it.
00:40One of the great advantages
00:42of working in a dynamic web server environment
00:45such as ASP.NET, is that you can take this
00:48commonly used content and put it in its own reusable files
00:53and then call that content whenever you need it,
00:56so that was my first step,
00:58breaking everything down into pieces.
01:00I chose to create each piece as something called
01:03a user control.
01:05In ASP.NET, a user control
01:08has a file extension of .ascx
01:11and this is the user control for the header content.
01:15At the top of the file there's a control meta tag,
01:19it declares the language I'm programming in, C#,
01:22and then indicates the code file
01:26also known as the code behind file,
01:28which is in this case header.ascx.cs.
01:33In the Solution Explorer pane over on the right,
01:37you can locate the associated code file
01:40by opening up the three control for that file
01:43and then you can double-click and open the file.
01:47Notice in this user control
01:50and also in a couple of the others,
01:52the code behind file isn't doing anything.
01:55There's a page load event handler but nothing else.
01:59That's the header file
02:01and it has exactly the same content
02:03as the original static HTML,
02:06just with a little bit better indentation.
02:10There is a little bit of code here that bears explaining.
02:13If you simply copy and paste your HTML code
02:16into Visual Studio
02:18and then look at it in design view in a user control file,
02:22your cascading style sheets won't work.
02:25These codes at lines three to five has an if clause.
02:29It's asking if and then looking at the condition false
02:34which means that this code will never execute.
02:37The code within the if clause is a client side link
02:40to the website's CSS or cascading style sheet file.
02:45The reason I put this in this file
02:48and in all of the other user controls
02:50is so that when I go into design mode
02:52the CSS files will be recognized and used
02:56and I'll be able to see more clearly
02:58what this file will look like at runtime.
03:00The design will never be perfect in Visual Studio,
03:04you have to really look at it in the web browser
03:06to see what it really looks like,
03:08but it will be closer than if I didn't have that code.
03:13Here's another example,
03:14the footer user control.
03:16Once again I have exactly the same code
03:19as in the original static footer
03:21and once again I have the code at lines three to five
03:24that incorporate the CSS file.
03:27The footer for this web page is nothing more
03:30than a bunch of links.
03:31When I look at it in design mode,
03:34I see pretty much exactly what it's going to look like
03:37when I open it in the web browser.
03:40I'll close that user control and then open up the sidebar.
03:46The sidebar user control has the stacked elements
03:49over on the right side of the page
03:51which incorporate a bunch of links
03:54and then some dynamic content that's being fed
03:57by some social websites.
03:59I'll once again look at it in design mode
04:02and see that it's pretty close.
04:06Finally, there's the curriculum user control.
04:09This is my main content for the page.
04:12I could have chosen to put this in the main page
04:14but I decided to put it in its own user control
04:17just to make it isolated.
04:19So I could work on it without worrying about the whole page
04:22at the same time.
04:24The text at the top is the text the user sees
04:27at the top of that section
04:29and then there's a series of four HTML tables.
04:32I'll talk about this table tags a little later
04:36but just notice for the moment
04:37that they have the ASP prefix
04:40and they each have a runat attribute
04:43set to a value of server,
04:45that means these tables are being processed on the server
04:49before they're being sent to the browser.
04:51Here's how I'm putting everything together.
04:54I'll close the user controls
04:56and open the file default.aspx.
05:00In an ASP.NET web form file,
05:03you have a page meta data tag at the top.
05:06Once again it declares the language
05:08and the code file
05:11then if you're going to use user controls
05:13you need to register them.
05:16You can either register them the way I have here
05:19within each page that uses them
05:21or you can also register them globally
05:24in the web.config file.
05:26Because this is only one page, I've kept it simple
05:29and kept all my code in one place.
05:32Notice that for each register tag,
05:35there's a TagPrefix, always set to controls
05:38and then a TagName.
05:41The source is then set to the actual filename.
05:45The TagName is in the alias,
05:47a name by which I can refer to this user control.
05:51For example, this is the register tag for the header
05:54and at line 11,
05:56this is where I'm actually calling the header
05:59and outputting it to the browser.
06:02Then within the body tag, I'm outputting the curriculum,
06:05the sidebar, and the footer.
06:08Now, I've isolated each of those bits of code
06:11in their own file and I can work on them one at a time.
06:16My main focus in this challenge
06:18was to work on the curriculum.
06:21In the next video, I'll describe in more detail
06:24how I built this user control
06:26and how I'm getting data out of the CSV files
06:29and injecting the data at runtime into the HTML.
Collapse this transcript
Modelling HTML markup with web form controls
00:00- Before I can add the data to an HTML table,
00:03I have to describe how the HTML table is going to look.
00:08And in this solution, I did this in file cirriculum.ascx.
00:12As I previously described, this is a user control file.
00:17It has basic HTML markup and simple text in it,
00:21and most of the file is exactly the same
00:24as in the original static site.
00:26But when we get to the HTML tables, everything changes.
00:31Instead of simple HTML tables,
00:33I'm using server side web form controls.
00:38In this solution, I'm using three controls in my markup
00:41named Table, TableRow and TableHeaderCell.
00:46Each of these has an XML prefix of asp,
00:50and a runat attribute of server.
00:53The tables each have an ID.
00:55There's a Table1, Table2, Table3 and Table4,
01:00and the captions for each of these tables
01:02are set with literal strings.
01:04If I preferred, I could have set those
01:06dynamically at runtime.
01:08And then the TableHeaderCells also have literal strings.
01:13I could have done everything at runtime if I preferred,
01:16but the stated goal of this challenge is to
01:18inject data into a template that's already set up,
01:22and this comes closest.
01:25Notice that there's nothing in the table markup
01:27that would determine fonts, colors,
01:30or other aspects of the table's appearance.
01:34Just as in the static tables,
01:37those are being determined by the cascading
01:39style sheet files.
01:41And, if you understand how that's working in the
01:43static version, you already understand
01:46how it's working in ASP.NET.
01:49James Williamson, the original author of these files,
01:52gives a great explanation of how the CSS is used at runtime
01:56by the browser to render the tables and the data.
02:01And so that's really all there is to it.
02:04There are four tables, they each have one row.
02:08Each of those rows has two TableHeaderCells,
02:11and the literal text for those cells is set here
02:15in the HTML markup.
02:17So in the next movie, I'll show you how I use
02:20C# code to extract the data at runtime
02:24from the CSV files that are stored here,
02:27in the App Data subfolder.
Collapse this transcript
Parsing data from CSV files
00:00- My solution contains four CSV files,
00:03all stored in the App_Data folder.
00:07In ASP.NET this folder has a special name,
00:10it's where you typically put CSV files, XML,
00:14local database files and other data oriented content.
00:20In theory, you could put this content anywhere in the side
00:23but in a moment I'll show you an advantage
00:26of using this specially named subfolder.
00:30There are four CSV files,
00:32one for each semester that I'm going to be displaying.
00:35The first two CSV files came from
00:38James Williamson sample files.
00:40I created the third and fourth semester files
00:43using the data that he was displaying
00:46in his finished website.
00:49I'll close these files
00:50and show you how I'm opening this data at runtime
00:54using C# code.
00:57I'll go to the solution explorer
00:59and I'll open up curriculum.ascx,
01:03clicking on its table icon here.
01:06I'll open the user controls code behind file
01:09curriculum.ascx.cs.
01:14As the page loads, I execute a bit of code.
01:19First, I'm declaring a list of course objects.
01:24Let's take a look at the files in the app code folder.
01:27There are two.
01:28This one is a class name simply course.
01:32It's a data entity.
01:34It declares two properties called course name and hours.
01:39Course name is a string and hours is an integer.
01:45Then there's a class named DataManager,
01:47the DataManager class has a method named GetData.
01:52It receives the name of a file
01:54and then it executes a bunch of code
01:57to open the file, extract its data,
02:00and return that data as a list of data objects.
02:03Let's go through that code a couple lines at a time.
02:08The code at lines 18 through 20
02:10is determining the location of the file
02:14that we're about to open.
02:15When the GetData method is called
02:17only the name of the file is passed in.
02:21The location is set here
02:23because it's always the same for every file.
02:26First, I'm using an expression to get the location
02:30of the data directory,
02:31that's the specially named App_Data directory,
02:35which is available in all ASP.NET applications.
02:39The expression AppDomain.CurrentDomain.GetData
02:43then receives a string of DataDirectory,
02:47it returns the directory object
02:49and then the ToString method returns it as a string.
02:53I get back the complete address of that folder.
02:58Then I append to that an extra backslash
03:01and the name of the file that was passed in to the method.
03:04Now, the fullname variable has the complete location
03:08and name of the file that I'm about to open and parse.
03:12Next, I open the file at line 22.
03:14I'm using a StreamReader object
03:17wrapped around a file object,
03:19that lets me read the file into memory.
03:23Then I create that list of courses,
03:26this is where the list is instantiated.
03:29A list is a resizable set of objects,
03:32it uses generic notation so you can indicate
03:36what type of object it's going to contain.
03:38I'm going to say my list contains course objects,
03:42that is instances of the course class.
03:46Next, I read the file using a while loop.
03:48The expression !reader.EndOfStream
03:52means keep reading the file until I get to the end.
03:56Within the while loop I call the reader objects
03:59read line method,
04:01that reads everything up to a line feed
04:03and returns just the line.
04:06Then I'm using the strings split method,
04:10passing in the comma as the delimiter
04:12and I get back the values.
04:15In our CSV files, each line has two values.
04:18The name of the course and the number.
04:22Notice, that the first line of each of these files though
04:25has a set of strings that don't represent a course.
04:28Here's how I'm telling my code to ignore that line.
04:33I'll go back here
04:34and you'll see that I'm parsing the data
04:37and grabbing each item at lines 32 and 33,
04:41but that code is wrapped in a try-catch block.
04:44In the catch block when I hit a format exception,
04:48I output a bit of code to my debug console
04:51and then call the continue keyword
04:54and that means don't finish this loop,
04:56go back to the beginning of the loop and try the next line.
05:01If I get past that catch block though
05:03that means I have valid values
05:06and I'll take that course object
05:08and I'll add it to my courses list.
05:12Finally, when I'm done with the loop
05:14I return that data.
05:16Going back to the curriculum code file,
05:20I'm calling that GetData method four times,
05:23once for each of the CSV files,
05:26and then I'm calling my own custom method
05:28called AddDataToTable and I'm passing in the courses list
05:33and then passing in a reference to the table
05:35I want to inject the data into.
05:38Notice that I'm injecting data into tables one,
05:41two, three, and four.
05:44I'm matching that with the CSV files
05:47and that's how the correct data is getting
05:49into the correct table.
05:52That's how I'm getting the data from the CSV files
05:55and then passing it to the tables.
05:58In the next movie, I'll show you the code
06:00that's manipulating those tables
06:02and displaying the data in the correct places
06:05with the correct formatting.
Collapse this transcript
Adding data to HTML tables at runtime
00:00- So far, I've described how I've modeled
00:02the HTML using web form controls,
00:06and then how I extracted the data
00:08from the csv file using parsing code,
00:11which you'll find in the data manager class
00:13of the solution.
00:15Finally, I'll show you how I'm injecting
00:17that data into the HTML tables at run time.
00:22That code is in the curriculum code file
00:25curicculum.ascx.cs, and it's in the method
00:29AddDataToTable.
00:31This method receives two arguments.
00:34A list of courses and a reference
00:36to the table I want to inject the data into.
00:39I'm starting with a foreach loop,
00:42looping through the courses in the list
00:44and then adding each course as a row
00:47to the selected HTML table.
00:49For each course, I'm creating an instance
00:51of the table row class.
00:53That's exactly the same table row
00:56that I'm adding to the tables
00:58in the HTML markup.
01:01But now I'm referring to it using C# code
01:04instead of HTML.
01:06So I've created the row, but right now
01:09it's not attached to the table.
01:12If you go back to the static version of the website,
01:16you'll see that in the HTML tables
01:19the course names have bold around the course IDs.
01:23This is done with a pair of strong tags.
01:26So here's how I'm making that happen
01:28in my C# code.
01:31First I'm splitting up the course name into words.
01:35Parsing using a space character as the delimiter.
01:39I'll get back different numbers of words
01:40depending on the course, but I always know
01:43that the first word is the course ID.
01:46So then I'm rebuilding the course name
01:48one word at a time.
01:50At line 37 I'm wrapping the course number
01:53or ID in a pair of strong tags.
01:57Then on lines 38 to 41 I'm looping
02:00through the remaining course words
02:03and for each word re-appending a space character
02:06and that word.
02:08So now the variable course name has the same text,
02:12but the strong tags are where they need to be.
02:16So I have a table row and I have my course name.
02:20Next I'll create my table cells.
02:23Once again, these are web form controls.
02:27I'm instantiating them using pure C# code,
02:30instead of markup, but they're the same thing.
02:33To add pure text to a cell, use the cell's ID
02:38then the controls property,
02:40and from there call the add method.
02:43To set the text of the cell, wrap the text
02:46in an instance of the class LiteralControl.
02:49So that's how I'm setting the text of the cell,
02:52and then I'm adding the cell to the row.
02:56Then I repeat that code at lines 48 to 50
02:59for the hours, the numeric value.
03:02Notice that the expression at line 49
03:05has course.hours.ToString.
03:08When I parsed the csv files, I took that numeric value
03:12and stored it as a number just in case I need
03:15to do any mathematical operations
03:17or numeric formatting later on.
03:20But here, I need to re-enter it as a simple string,
03:24and that's how I'm doing it.
03:27So now I have my two cells
03:28and I've added them to the row.
03:31At line 53, I'm adding the row to the table,
03:34once again, using the controls property
03:37of the object I'm working with
03:39with the syntax table.controls.add.
03:43And that's it.
03:45That's all the work that I'm doing.
03:47Just as with the tables themselves,
03:49I'm not adding any information about colors or fonts.
03:53I'm letting the existing cascading style sheet file
03:57do all that work for me.
03:59Once again, here's the result.
04:02The finished HTML page looking exactly the same
04:06as it did when it was a static webpage.
04:09But now the page is being generated
04:11dynamically at run time.
04:13If I modify the csv files and re-run the page,
04:17the new or edited data will
04:19show up here automatically.
04:22I won't have to go back to the core page
04:24or the user controls if the only thing
04:27that's changed is the data.
04:30So that's the rest of the solution.
04:33I've described how I re-factored a static webpage
04:36and turned it into a dynamic asp.net web form.
04:40And then how I wrote just a little bit
04:42of C# code to extract the data
04:45from the csv files at run time
04:47and inject that data dynamically into the web form
04:51before sending it back to the browser.

No comments:

Post a Comment