Archive for April, 2010

Basic Quiz Application Using Behaviors

A user on the Expression Blend/SketchFlow forums recently asked a question about how to create a Quiz type of application in Silverlight without using much coding.  I created a simple example using a simple behavior to store the score of the quiz. Some of this behavior is redundant with new behaviors included with Blend 4, but the example should be illustrative if nothing else.

The behavior works by keeping static state in the ScoreKeeperAction class, which can be used to display the score. Each instance of a QuizQuestionBehavior that is attached to a RadioButton (the correct answer) will register its results with the ScoreKeeperAction class when invoked by determining if the RadioButton it is attached to is checked.

Each instance of the ScoreKeeperAction subscribes to changed events on the score, and updates its text whenever the score changes.

To use the behaviors, create a group of radiobuttons, and attach a QuizQuestionBehavior to the correct answer. Set the action’s trigger to a button that will submit the answer and the event to clicked. Also set the name of the Question in the behavior properties. An example of usage:

<Grid x:Name="LayoutRoot" >
		<RadioButton x:Name="CorrectAnswer" HorizontalAlignment="Left" Margin="45,47,0,0" VerticalAlignment="Top" Width="99" Content="Answer 1 (C)" GroupName="QuizGroup">
			<i:Interaction.Triggers>
				<i:EventTrigger SourceName="button" EventName="Click">
					<local:QuizQuestionBehavior QuestionName="Question 1"/>
				</i:EventTrigger>
			</i:Interaction.Triggers>
		</RadioButton>
        <RadioButton HorizontalAlignment="Left" Margin="45,68,0,0" VerticalAlignment="Top" Content="Answer 2" Width="99" GroupName="QuizGroup"/>
        <RadioButton HorizontalAlignment="Left" Margin="45,89,0,0" VerticalAlignment="Top" Content="Answer 3" Width="99" GroupName="QuizGroup"/>
		<RadioButton HorizontalAlignment="Left" Margin="45,110,0,0" VerticalAlignment="Top" Content="Answer 4" Width="99" GroupName="QuizGroup"/>
		<Button x:Name="button" Height="36" HorizontalAlignment="Left" Margin="45,135,0,0" VerticalAlignment="Top" Width="99" Content="Record Answer"/>
	</Grid>

To display the score, create a TextBlock and attach a ScoreKeeperAction, the user can set the text to be displayed in the behavior properties, SCORE is replaced with the number of correct answers and TOTAL is replaced with the total number of questions answered.

<TextBlock x:Name="ScoreBlock" Margin="9,9,8,8" Grid.Row="1" TextWrapping="Wrap">
	<i:Interaction.Triggers>
		<i:EventTrigger>
			<local:ScoreKeeperAction/>
		</i:EventTrigger>
	</i:Interaction.Triggers>
</TextBlock>

To display a summary create a TextBlock and attach a ScoreKeeperAction as well as setting the SummaryBox checkbox to true in the properties.

<TextBlock x:Name="textBlock" Margin="8,8,23,17" TextWrapping="Wrap" Text="Click update.">
	<i:Interaction.Triggers>
		<i:EventTrigger SourceName="textBlock">
			<local:ScoreKeeperAction SummaryBox="True"/>
		</i:EventTrigger>
	</i:Interaction.Triggers>
</TextBlock>

The code of the 2 Behaviors/Actions:

public class QuizQuestionBehavior : TriggerAction<RadioButton>
	{
		public static readonly DependencyProperty QuestionNameProperty = DependencyProperty.Register("QuestionName", typeof(string), typeof(QuizQuestionBehavior), new PropertyMetadata(Guid.NewGuid().ToString()));

    	public string QuestionName
        {
            get { return GetValue(QuestionNameProperty) as string; }
			set { SetValue(QuestionNameProperty, value); }
        }

		public QuizQuestionBehavior() {	}

		private bool answered = false;
		protected override void Invoke(object o)
		{
			if(!answered)
			{
				if(this.AssociatedObject.IsChecked == (bool?)true)
				{
					// Right
					ScoreKeeperAction.QuestionAnswered(this.QuestionName, true);
				}
				else
				{
					// Wrong
					ScoreKeeperAction.QuestionAnswered(this.QuestionName, false);
				}
			}
			answered = true;
		}
	}
public class ScoreKeeperAction : TriggerAction<TextBlock>
	{
		public static readonly DependencyProperty DisplayTextProperty = DependencyProperty.Register("DisplayText", typeof(string), typeof(ScoreKeeperAction), new PropertyMetadata("Answered SCORE/TOTAL questions correctly."));

		public string DisplayText
		{
			get { return GetValue(DisplayTextProperty) as string; }
			set { SetValue(DisplayTextProperty, value); }
		}

		public static readonly DependencyProperty SummaryBoxProperty = DependencyProperty.Register("SummaryBox", typeof(bool), typeof(ScoreKeeperAction), new PropertyMetadata(false));

		public bool SummaryBox
		{
			get { return (bool)GetValue(SummaryBoxProperty); }
			set { SetValue(SummaryBoxProperty, value); }
		}

		private static int questionsAnswered = 0;
		private static int QuestionsAnswered
		{
			get
			{
				return questionsAnswered;
			}
			set
			{
				if (questionsAnswered != value)
				{
					questionsAnswered = value;
					if (QuestionsAnsweredChanged != null)
					{
						QuestionsAnsweredChanged(null, new EventArgs());
					}
				}
			}
		}

		private static int correctQuestionsAnswered = 0;
		private static int CorrectQuestionsAnswered
		{
			get
			{
				return correctQuestionsAnswered;
			}
			set
			{
				if (correctQuestionsAnswered != value)
				{
					correctQuestionsAnswered = value;
					if (QuestionsAnsweredChanged != null)
					{
						QuestionsAnsweredChanged(null, new EventArgs());
					}
				}
			}
		}

		private static Dictionary<string, bool> questionResults = new Dictionary<string, bool>();

		public static void QuestionAnswered(string questionName, bool correct)
		{
			if (!questionResults.ContainsKey(questionName))
			{
				questionResults[questionName] = correct;
				QuestionsAnswered++;
				if (correct)
				{
					CorrectQuestionsAnswered++;
				}
			}
		}

		public static event EventHandler QuestionsAnsweredChanged;

		public ScoreKeeperAction()
		{
			QuestionsAnsweredChanged += new EventHandler(UpdateDisplay);
			UpdateDisplay();
		}

		private void UpdateDisplay(object sender, EventArgs e)
		{
			UpdateDisplay();
		}

		private void UpdateDisplay()
		{
			string text = this.DisplayText;
			if (String.IsNullOrEmpty(text))
			{
				text = "SCORE/TOTAL";
			}
			text = text.Replace("SCORE", CorrectQuestionsAnswered.ToString());
			text = text.Replace("TOTAL", QuestionsAnswered.ToString());
			if (this.SummaryBox)
			{
				// Add in summary below text.
				var sb = new StringBuilder();
				sb.AppendLine(text);
				foreach (var item in questionResults)
				{
					sb.AppendLine("Question " + item.Key + " answered " + (item.Value ? "correctly" : "incorrectly") + ".");
				}
				text = sb.ToString();
			}

			if (this.AssociatedObject != null)
			{
				this.AssociatedObject.Text = text;
			}
		}

		protected override void Invoke(object o)
		{
			UpdateDisplay();
		}
	}

The complete solution is available here: QuizApp.zip


WPF Navigation Framework Navigate Behavior

A user on the Expression Blend/SketchFlow forums asked a question about how to navigate between pages in xaml.

There are a number of solutions to this problem, including using a hyperlink item inside an element such as a textblock.

<TextBlock>
    <Hyperlink NavigateUri="Page3.xaml">
        <Run Text="Page 3"/>
    </Hyperlink>
</TextBlock>

However, this will only work when contained within a Page element, and won’t work if you want to have navigation UI in the same page containing a frame. To enable both of these scenarios in an easy to use way, I wrote a simple NavigationAction behavior that will navigate to a Page from either inside a Page, or from an element that is inside the same screen as a Frame element.

The Action created below may be attached to any item inside a page, or any item on a screen that has a Frame object being used to navigate to pages. Set the PageProperty to the filename of the page to be shown.

  • To use this behavior in Blend, add a new Action item to your project, and replicate the code provided below in your action.
  • Build your project.
  • In the asset panel, find your new Action, and drag it onto an element on the artboard.
  • Select the behavior in the Objects and Timeline panel
  • Open the properties panel
  • Set the Page property to the page you want loaded
  • Adjust the trigger event to the event you would like, most likely MouseLeftButtonUp or Down

Source code and example usage are provided below.

The FindChild method is a modified version of code posted here: http://stackoverflow.com/questions/636383/wpf-ways-to-find-controls

	public class NavigateAction : TriggerAction<DependencyObject>
	{
		public static readonly DependencyProperty PageProperty = DependencyProperty.Register("Page", typeof(string), typeof(NavigateAction), new PropertyMetadata(null));

		public string Page
		{
			get { return GetValue(PageProperty) as string; }
			set { SetValue(PageProperty, value); }
		}

		private NavigationService navigationService;
		public NavigateAction()
		{
			NavigationWindow navigationWindow = Application.Current.MainWindow as NavigationWindow;
			if (navigationWindow != null)
			{
				this.navigationService = navigationWindow.NavigationService;
			}

		}

		protected override void Invoke(object o)
		{
			if (this.navigationService == null)
			{
				// Try to find a frame in the main window.
				Frame frame = FindChild<Frame>(Application.Current.MainWindow);
				if (frame != null)
				{
					this.navigationService = frame.NavigationService;
				}
			}

			if (this.navigationService != null)
			{
				this.navigationService.Navigate(new Uri(this.Page, UriKind.Relative));
			}
		}

		public static T FindChild<T>(DependencyObject parent) where T : DependencyObject
		{
			if (parent == null) return null;
			T foundChild = null;
			int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
			for (int i = 0; i < childrenCount; i++)
			{
				var child = VisualTreeHelper.GetChild(parent, i);
				T childType = child as T;
				if (childType == null)
				{
					foundChild = FindChild<T>(child);
					if (foundChild != null)
					{
						return foundChild;
					}
				}
				else
				{
					return (T)child;
				}
			}
			return foundChild;
		}
	}
}

Using the behavior on siblings of a Frame element:

<Window
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:local="clr-namespace:WpfApplication21"
	x:Class="WpfApplication21.MainWindow"
	x:Name="Window"
	Title="MainWindow"
	Width="640" Height="480">
	<Grid>
		<Grid.RowDefinitions>
			<RowDefinition Height="0.925*"/>
			<RowDefinition Height="0.075*"/>
		</Grid.RowDefinitions>
		<Frame x:Name="MainFrame" Content="" Source="Pages\Page1.xaml" Margin="0,0,0,4"/>
		<StackPanel Grid.Row="1" Orientation="Horizontal">

			<Ellipse Fill="Red" Stroke="Black" Width="100">
				<i:Interaction.Triggers>
					<i:EventTrigger EventName="MouseLeftButtonUp">
						<local:NavigateAction Page="Pages/Page1.xaml"/>
					</i:EventTrigger>
				</i:Interaction.Triggers>
			</Ellipse>
			<Ellipse Fill="#FF00FF1C" Stroke="Black" Width="100">
				<i:Interaction.Triggers>
					<i:EventTrigger EventName="MouseLeftButtonUp">
						<local:NavigateAction Page="Pages/Page2.xaml"/>
					</i:EventTrigger>
				</i:Interaction.Triggers>
			</Ellipse>
			<Ellipse Fill="#FF0BC6CD" Stroke="Black" Width="100">
			<i:Interaction.Triggers>
					<i:EventTrigger EventName="MouseLeftButtonUp">
						<local:NavigateAction Page="Pages/Page3.xaml"/>
					</i:EventTrigger>
				</i:Interaction.Triggers>
			</Ellipse>
		</StackPanel>
	</Grid>
</Window>

Using the behavior on items within a page:

<Page
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:local="clr-namespace:WpfApplication21"
	x:Class="WpfApplication21.Page1"
	x:Name="Page"
	WindowTitle="Page"
	FlowDirection="LeftToRight"
	Width="640" Height="480"
	WindowWidth="640" WindowHeight="480">

	<Grid x:Name="LayoutRoot">
		<StackPanel>
			<TextBlock Text="Page 1" TextWrapping="Wrap">
				<i:Interaction.Triggers>
					<i:EventTrigger EventName="MouseLeftButtonUp">
						<local:NavigateAction Page="Pages/Page1.xaml"/>
					</i:EventTrigger>
				</i:Interaction.Triggers>
			</TextBlock>
			<TextBlock Text="Page 2" TextWrapping="Wrap">
				<i:Interaction.Triggers>
					<i:EventTrigger EventName="MouseLeftButtonUp">
						<local:NavigateAction Page="Pages/Page2.xaml"/>
					</i:EventTrigger>
				</i:Interaction.Triggers>
			</TextBlock>
			<TextBlock HorizontalAlignment="Left" Text="Page 3" TextWrapping="Wrap">
				<i:Interaction.Triggers>
					<i:EventTrigger EventName="MouseLeftButtonUp">
						<local:NavigateAction Page="Pages/Page3.xaml"/>
					</i:EventTrigger>
				</i:Interaction.Triggers>
			</TextBlock>
		</StackPanel>
	</Grid>
</Page>

LED Wall Display

LED Wall Display

LED Wall Display

More pictures are available in the Photo Gallery

What is it?

It could be art? Decoration? Science? Any or all of the above. It is however a wall hanging made with 128 tri-color LEDs. Each LED can show one of 4913 colors. It is programmed to show different patterns on the screen, like a 21st century lava lamp.

The idea

I built this project for my Dad for Christmas 2007. The idea was inspired as an extension to the LED Table Project. In combination with seeing the amazing effects from the LED Table, I also saw several projects online where people had built LED arrays to make interesting displays.

The main inspiration for this project is the HypnoCube project. It is a similar circuit but in a 3D configuration. My project has twice the number of LEDs, so the circuitry is essentially doubled.

The main idea in the end was to build a LED display out of an array of tri-color LEDs. I ended up with a 16 x 8 tri-color LEDs, which is essentially an array of 48 x 8 single color LEDs.

Once built, it should be programmed to display various patterns to act as an art display hanging on a wall.

Building

Most of the details will be under the Technical Details section, but a brief overview of the steps:

  1. Design the circuit in Eagle Layout Editor
  2. Lay out the circuit board in Eagle Layout Editor
  3. Order the circuit board to be produced and the electrical parts
  4. Cut the circuit board into the 130 individual small boards for the LEDs
  5. Solder the 128 LEDs to their boards and the interconnect wiring
  6. Assemble the main circuit board
  7. Write the code
  8. Build the physical frame
  9. Assemble all of the pieces

Technical Details

Theory of Operation

I would be happy to provide the eagle schematic and board files, as well as any information about the code that you like. Everything here is presented with no warranties whatsoever. Just send me an email.

The LED display works on the POV (persistance of vision) effect of the human eye. The LEDs each stay on for a very brief period of time, but the human eye and brain together make it appear as if the LEDs stay on the entire time. Because of this effect, the LEDs only need to be lit up for a short time period, which gives us the option to scan through them very quickly. This is important because there are 16 * 8 * 3 = 384 LEDs in the display. Obviously our microcontroller does not have 384 outputs, nor did I want to create a control board with 384 transistors and individual wiring outputs. So the solution is to create a matrix of LEDs. Each row (8) has all of its negative LED terminals tied together, and each column (16 * 3 = 48) has all of its positive terminals tied together. 48 is a feasible number of outputs for a 8 bit microcontroller. Now each row of LEDs can be individually controlled, so all we have to do is scan through the 8 rows in quick succession turning on the appropriate LEDs in each row for the appropriate amount of time, and we have a big colored LED display.

Each LED is set up for 17 levels of brightness, where 0 is off, and 16 is the brightest. For each row, there are 17 individual time periods. At the beginning of the row loop, all the LEDs in the row are turned on, and each is turned off when a counter reaches its brightness level. Once the counter hits 16, the next row is activated. This is all interrupt driven, and is automatic.

The main program loop is simply responsible for setting the color values into an array to be used by the interrupt driven LED code. Each section in the main program loop implements a different display program.

Videos

Overview Video

Removing the polycarbonate sheet in front

View of back side of LED Wall Display while running

There are individual videos for each effect available by clicking on the links below


LCD Photo Frame

The Idea

Before LCD photo frames were widely available, we decided to build one for my Dad for a Father’s Day gift. We had collected thousands of pictures in our online photo gallery, but had no easy way to display them.

At the time, I saw an article in Popular Science describing a project that took laptop and made it into an LCD photo frame. I realized that I could so the same or better utilizing a mini-computer, and an lcd screen.

There are several more pictures of this project are available in the Photo Gallery.

The Design

I decided to use a VIA EPIA mini-itx motherboard. Along with this I purchased a 12V power supply, a 12V adapter, and a laptop hard drive for storage. At the time, large and inexpensive flash memory was not available. If I were to do it again, I would add a card reader like the commercial products have. All of these parts would be mounted inside a wooden frame on the back of the LCD monitor.

Building

The wooden frame was built, and the LCD screen was mounted inside, after removing the built in stand. The various computer parts were assembled, and were placed inside the frame, and attached with velcro to the back of the LCD monitor. On the side panel of the frame was mounted a panel which included the power switch, power plug, ethernet port, and usb plugs. This allows the frame to be hooked up to a network, and connected to via the network. Alternatively, a keyboard may be attached to the usb port.

Software

I initially booted Knoppix to see if linux would have the needed modules to run on the VIA EPIA board/cpu, and it worked flawlessly. Fortunately it also had all the software I needed to build the automatic slideshow, so I simply did a hard drive install of knoppix, and set it to automatically run the slideshow software. Pictures are added by uploading them to gallery software (older version of the software running our photo gallery).


Interactive LED Table

The Idea

This project was built as a Christmas present for Andi’s father Rick. The electronics were
built from a kit supplied by Windell Oskay from evilmadscientist.com. I originally saw the idea in Popular Science magazine, where Windell’s project was featured.

The idea is to make a table which responds to movement with patterns of light. As shown in the picture above, when Rick moves his hand over the surface, the table lights up to match his movement. The lights then spread and fade on and off in a very pleasing rippling pattern.

The table was built with the help of my father and his wood shop.

Building

I happened to be the first person to build one of the kits supplied by Windell. Fortunately I had the knowledge to track down a couple of issues that appeared with my kit, mostly in the form of broken traces from shipping. Windell was still in the process of writing the documentation, which I sent comments back to him, hopefully that was useful.

The kit came with 6 large circuit boards, and a large bag of parts. The assembly involved:

  • Matching sets of 5 LEDs, 96 times
  • Soldering in the 96 sets of 5 LEDs, for 480 LEDs total
  • Soldering a large number of other various components, including capacitors, resistors, and op-amp chips
  • Soldering the boards together to one another to make one large circuit

The circuit is based on a very clever design by Windell, I won’t post the schematic here because he wishes to publish it on his own at a later time. It is entirely analog, and interconnected between the boards, to allow signal propagation. See his site for more information.

The table itself is built out of ash that was cut, kiln dried, and worked completely at my father’s wood shop. He uses a wood saw called a woodmizer.

The glass used on the tabletop is actually the frosted glass top from a table at IKEA. It was cheaper to buy it with the table base and 4 chairs than it was to guy it from a glass shop. Anyone need 4 chairs?

The table ended up working very well, and Rick liked it very much, we were quite happy with how it turned out.

Video

This is not the table we built, it is the demo video Windell has on his website. Our table uses the same internal parts, but frosted glass, in order to hide the electronic components.


Model Lighthouse

The Idea

My Dad and his wife Jean spend their summers in Bayfield, Wisconsin living on their boat.
When people come up to visit, they often take them to some of the various
lighthouses that exist in the Apostle Islands area.

One of my favorites was the Sand Island lighthouse. As a result of the beauty and history of the lighthouses of the area, we decided to build a model for my Dad and Jean.

There are several more pictures of this project are available in the Photo Gallery.

The Design

I decided to carve the base out of foam, and then coat the top of it with strips of plaster cloth. The building is hand made out of pieces of balsa wood, cut and glued together by hand. The rocks are made from plaster poured into rubber molds.

Building

Paige helped to paint the base, rocks and building. She and Andi painted and placed the rocks, as well as helping to arrange the rest of the details on the surface.

Once the building was assembled and painted (including a working “light” in the lighthouse), the surface was coated with modeling supplies to represent grass and other surface vegetation. Then trees were added to complete the scene.

Thanks again to Paige for her help, I certainly couldn’t have finished this project without her painting skills!


Wooden Gear Clock

The Idea

There are many more pictures available in the Photo Gallery.

The Design

The overall design was inspired by this clock which in turn was inspired by this clock. I did not have any plans available, so I created my own design based on the images. The gear templates were taken from various free plans available from the Internet. Oddly, no one single plan had the gears I needed, I had to take them from several sources.

The clock seems to keep great time, and it should, as long as the AC power coming into the house is at 60hz.

Materials

The main wood is black walnut. The marker inlays are maple. The gears are cut from Baltic birch plywood. This material is used because it is composed of many layers, without any significant voids. This is desirable for cutting gears, because they are unlikely to warp. The hands of the clock are Bolivian Rosewood, for contrast.

Building

The most important feature to make this clock work properly is the gearing ratio between the various gears. The AC powered motor runs at 1 rpm, which means it has a internal gear reduction of 1:3600 (it runs from the 60hz ac power). The gears that run the minute hand must be reduced 60:1, which is accomplished through 2 sets of gears, each using an 8 tooth on one side, and a 60 tooth on one set, and a 64 tooth on the other. The last set of gears reduces the hour hand by 1:12. The two sets of gears for this ratio are 8 tooth with a 32 tooth, and a 10 tooth with a 30 tooth.

Each of the gears were cut free-hand, using a scroll saw, and spiral cut blades. Amazingly, I only made a mistake on my very first gear, and had to start over on only that one. Cutting the gears took many hours and a lot of patience.

The face piece was made by routing grooves into a piece of black walnut, installing the maple strips, and sanding them flush. The circle was then cut using a circle jig and a router, 2 cuts were made to form the inside and outside surfaces.

The assembly and fitting of all of the gears, bearings, shafts, motor, was tricky to say the least. Fortunately it all came together without any major mistakes.


  • Categories

  • Archives

  • Copyright © 1996-2010 ChuckHays.net. All rights reserved.
    Jarrah theme by Templates Next | Powered by WordPress