CS411 — Final Term Summary (Lectures 23–45)
📘 Lecture 23 — Touch Events and Manipulation Events
📖 Overview: This lecture introduces touch event handling in WPF, covering basic touch events (TouchDown, TouchMove, TouchUp) and advanced manipulation events that combine multi-touch input for gestures like translation, rotation, and scaling. It also covers inertia simulation and boundary feedback to create natural, fluid touch interactions.
🗂️ Topics Covered
The lecture explains touch events with a fingerprint tracking example, then moves to manipulation events (manipulationstarting, manipulationdelta, manipulationcompleted) with examples for moving, rotating, and zooming photos. It covers inertia handling for smooth deceleration, boundary feedback via reportboundaryfeedback, and a spin prize wheel example using rotation-only manipulation. Finally, it mentions ScrollViewer panning support and the Surface Toolkit for Windows Touch.
📝 Lecture Summary
Touch Events Example
The lecture begins with an example using touch events to track multiple finger touches on a Canvas. A Dictionary<touchdevice, Image> named fingerprints keeps track of which images are associated with which touch devices.
🔑 Definition — TouchDevice: Represents an individual finger or stylus input. Each touch contact gets a unique TouchDevice instance.
📌 Example: When a user touches the Canvas, the OnTouchDown handler captures the touch device, creates a new Image (fingerprint.png), positions it at the touch point using a TranslateTransform, adds the image to the dictionary and the Canvas children. On OnTouchMove, the image follows the finger by updating the Transform's X and Y coordinates. On OnTouchUp, the touch capture is released, and the image is removed from both the Canvas and the dictionary.
Manipulation Events Overview
Manipulation events (manipulationstarting, manipulationstarted, manipulationdelta, manipulationcompleted) combine information from multiple touch events to enable gestures. They work when IsManipulationEnabled=true is set on the element or a parent, and basic touch events are not handled.
💡 Why this matters: Manipulation events automatically interpret multi-touch input (e.g., two-finger pinch to zoom, rotate gestures) without requiring you to write complex gesture recognition logic.
🔑 Definition — ManipulationDelta: Contains Translation, Scale, Rotation, and Expansion (similar to Scale but in pixels instead of scale factor). ManipulationDeltaEventArgs provides both DeltaManipulation (change since last event) and CumulativeManipulation (total change since manipulation started).
Manipulation Events Example: Move, Rotate, Zoom
The example shows how to move, rotate, and zoom a photo using manipulation events. The XAML sets IsManipulationEnabled="True" on the Canvas and uses a MatrixTransform on the Image.
📐 Formula: Matrix transforms: Matrix.Translate(dx, dy) → moves the image; Matrix.RotateAt(angle, originX, originY) → rotates around a point; Matrix.ScaleAt(sx, sy, originX, originY) → scales relative to a point.
📌 Example: In the Canvas_ManipulationDelta event handler, the code gets the existing Matrix from the Image's RenderTransform, applies e.DeltaManipulation.Translation.X/Y, e.DeltaManipulation.Rotation (using RotateAt with e.ManipulationOrigin), and e.DeltaManipulation.Scale.X/Y (using ScaleAt with e.ManipulationOrigin), then sets transform.Matrix = matrix and marks the event as handled.
Manipulation Container
Manipulations are always performed relative to a manipulation container. By default, it's the element with IsManipulationEnabled=true, but you can customize it by handling ManipulationStarting and setting e.ManipulationContainer.
Inertia
Inertia allows natural deceleration after touch gestures. The ManipulationInertiaStarting event fires when all fingers lose contact, before the completed event. You can set properties on TranslationBehavior, RotationBehavior, and/or ExpansionBehavior to control inertia.
🔑 Definition — TranslationBehavior has: DesiredDisplacement, DesiredDeceleration, and InitialVelocity. RotationBehavior has: DesiredRotation, DesiredDeceleration, and InitialVelocity. ExpansionBehavior has: DesiredExpansion, DesiredDeceleration, InitialRadius, and InitialVelocity.
📌 Example: To enable inertia, handle Canvas_ManipulationInertiaStarting and set e.TranslationBehavior.DesiredDeceleration = 0.01; e.RotationBehavior.DesiredDeceleration = 0.01; e.ExpansionBehavior.DesiredDeceleration = 0.01;. This causes the manipulation to continue with decreasing speed after fingers are lifted, producing smooth delta events until it stops.
Boundary Feedback
The ManipulationBoundaryFeedback event handles boundary awareness. Inside a ManipulationDelta event handler, you can call e.ReportBoundaryFeedback() to make the window bounce similar to iPhone bounce-list behavior.
Spin Prize Wheel Example
The lecture demonstrates a spin prize wheel with rotation-only manipulation. The XAML uses a Grid with IsManipulationEnabled="True", an Image with RenderTransformOrigin="0.5,0.5" (center of image) and a RotateTransform, plus an arrow image.
📌 Example: In Grid_ManipulationStarting, set e.Mode = ManipulationModes.Rotate to restrict input to rotation only. In Grid_ManipulationDelta, add e.DeltaManipulation.Rotation to the RotateTransform's Angle. In Grid_ManipulationInertiaStarting, set e.RotationBehavior.DesiredDeceleration = 0.001 for slow deceleration. In Grid_ManipulationCompleted, you can show what the user won.
Scrolling and Surface Toolkit
You can enable panning in a ScrollViewer by setting its PanningMode property to HorizontalOnly, VerticalOnly, HorizontalFirst, VerticalFirst, or Both. The Surface Toolkit for Windows Touch provides WPF controls optimized for multi-touch, including "Surface versions" of common controls (e.g., SurfaceButton, SurfaceCheckBox) and brand-new controls (e.g., ScatterView, LibraryStack).
⭐ Key Takeaways
Manipulation events (starting, delta, inertia starting, completed) must have IsManipulationEnabled=true to work. The ManipulationDelta event provides Translation, Rotation, and Scale deltas that can be applied to a MatrixTransform for smooth gesture handling. Inertia is enabled by setting deceleration values in ManipulationInertiaStarting, producing natural deceleration after touch release. The ManipulationMode property can restrict gestures to specific types (e.g., Rotate only). For boundary feedback, call ReportBoundaryFeedback() in the delta handler to create bounce effects.
🧠 Quick Revision Questions
- What dictionary is used to track which images belong to which touch devices in the touch events example?
- What property must be set to true on a Canvas (or parent element) to enable manipulation events?
- In the ManipulationDelta event handler, which three matrix operations are applied to the photo's MatrixTransform?
- What event fires when all fingers lose contact from the screen, enabling inertia configuration?
- How do you restrict manipulation to only rotation gestures in the ManipulationStarting event?
📘 Lecture 24 — Commands and Input Events in WPF
📖 Overview: This lecture introduces WPF commands as an abstract, loosely coupled alternative to direct event handling, enabling features like automatic input gestures, enable/disable logic, and two-way communication between controls. Understanding commands is essential for building maintainable WPF applications with standardized user interactions.
🗂️ Topics Covered
Commands as abstract event wrappers (e.g., Cut, Copy, Paste), the ICommand interface (Execute, CanExecute, CanExecuteChanged), WPF built-in command libraries (ApplicationCommands, ComponentCommands, MediaCommands, NavigationCommands, EditingCommands), command binding with CommandBindings and event handlers, routing commands via bubbling, XAML vs code-behind implementation, KeyBindings and MouseBindings, CommandTarget property, and controls with built-in command bindings (e.g., TextBox).
📝 Lecture Summary
Commands Overview
Commands are a more abstract and loosely coupled version of events, used for operations like Cut, Copy, and Paste. They are exposed in various ways and can be enabled or disabled—for example, disabling Paste when there’s nothing to paste. Two-way communication becomes cumbersome if you hard-code control lists, but WPF commands solve this. WPF defines a number of built-in commands with automatic support for input gestures (such as keyboard shortcuts). Some WPF controls have built-in behavior tied to various commands.
🔑 Definition — ICommand: Any object implementing ICommand can work as a Command. It defines three members: Execute, CanExecute, and CanExecuteChanged.
For Cut, Copy, and Paste, you could define three classes implementing ICommand, store them (e.g., as static fields of the main Window), call Execute from relevant event handlers (when CanExecute returns true), and handle the CanExecuteChanged event to toggle the IsEnabled property on UI elements. Controls have logic to interface with commands through the Command property.
📌 Example: You can set a button's command in XAML like this:
<Button Command="Help" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/>
This binds the button to the built-in ApplicationCommands.Help command.
Predefined Built-in Commands
WPF provides several categories of built-in commands:
- ApplicationCommands: Close, Copy, Cut, Delete, Find, Help, New, Open, Paste, Print, PrintPreview, Properties, Redo, Replace, Save, SaveAs, SelectAll, Stop, Undo, and more.
- ComponentCommands: MoveDown, MoveLeft, MoveRight, MoveUp, ScrollByLine, ScrollPageDown, ScrollPageLeft, ScrollPageRight, ScrollPageUp, SelectToEnd, SelectToHome, SelectPageDown, SelectPageUp, and more.
- MediaCommands: ChannelDown, ChannelUp, DecreaseVolume, FastForward, IncreaseVolume, MuteVolume, NextTrack, Pause, Play, PreviousTrack, Record, Rewind, Select, Stop, and more.
- NavigationCommands: BrowseBack, BrowseForward, BrowseHome, BrowseStop, Favorites, FirstPage, GoToPage, LastPage, NextPage, PreviousPage, Refresh, Search, Zoom, and more.
- EditingCommands: AlignCenter, AlignJustify, AlignLeft, AlignRight, CorrectSpellingError, DecreaseFontSize, DecreaseIndentation, EnterLineBreak, EnterParagraphBreak, IgnoreSpellingError, IncreaseFontSize, IncreaseIndentation, MoveDownByLine, MoveDownByPage, MoveDownByParagraph, MoveLeftByCharacter, MoveLeftByWord, MoveRightByCharacter, MoveRightByWord, and more.
All instances of RoutedUICommand implement ICommand and support bubbling (events travel up the element tree).
Command Binding
If you set HelpButton.Command = ApplicationCommands.Help; without a command binding, the button will always be disabled. We need to add a CommandBinding to the element or a parent element (due to bubbling).
🔑 Definition — CommandBinding: All UIElement objects have a CommandBindings collection. You add a command binding to link a command to its Execute and CanExecute handlers.
📐 Formula:
this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Help, HelpExecuted, HelpCanExecute));
The CanExecute handler determines if the command can run:
void HelpCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
The Execute handler defines what happens:
void HelpExecuted(object sender, ExecutedRoutedEventArgs e)
{
System.Diagnostics.Process.Start("http://www.adamnathan.net/wpf");
}
💡 Why this matters: Without adding a command binding, even though the command is set, the button remains disabled because there's no handler to tell WPF that the command can execute.
XAML Command Binding
Commands can be entirely defined in XAML using a type converter. The CommandConverter type converter allows using the command name directly as a string.
📌 Example — Complete XAML implementation of an About dialog with Help command:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AboutDialog"
Title="About WPF Unleashed" SizeToContent="WidthAndHeight"
Background="OrangeRed">
<Window.CommandBindings>
<CommandBinding Command="Help"
CanExecute="HelpCanExecute" Executed="HelpExecuted"/>
</Window.CommandBindings>
<StackPanel>
<Label FontWeight="Bold" FontSize="20" Foreground="White">
WPF 4 Unleashed
</Label>
<Label>© 2010 SAMS Publishing</Label>
<Label>Installed Chapters:</Label>
<ListBox>
<ListBoxItem>Chapter 1</ListBoxItem>
<ListBoxItem>Chapter 2</ListBoxItem>
</ListBox>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button MinWidth="75" Margin="10" Command="Help"
Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/>
<Button MinWidth="75" Margin="10">OK</Button>
</StackPanel>
<StatusBar>You have successfully registered this product.</StatusBar>
</StackPanel>
</Window>
The corresponding C# code-behind:
using System.Windows;
using System.Windows.Input;
public partial class AboutDialog : Window
{
public AboutDialog()
{
InitializeComponent();
}
void HelpCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
void HelpExecuted(object sender, ExecutedRoutedEventArgs e)
{
System.Diagnostics.Process.Start("http://www.adamnathan.net/wpf");
}
}
Input Gestures — KeyBindings and MouseBindings
Commands automatically handle input gestures. For example, ApplicationCommands.Help already maps to F1. You can also add custom KeyBindings and MouseBindings manually.
this.InputBindings.Add(
new KeyBinding(ApplicationCommands.Help, new KeyGesture(Key.F2)));
In XAML:
<Window.InputBindings>
<KeyBinding Command="Help" Key="F2"/>
<KeyBinding Command="NotACommand" Key="F1"/>
</Window.InputBindings>
Controls with Built-in Command Bindings
Some controls have built-in command bindings. For example, TextBox automatically responds to Ctrl+Z (Undo), Ctrl+C (Copy), etc. This enables rich interaction between controls without direct coupling.
📌 Example — Commands with CommandTarget property:
<StackPanel Orientation="Horizontal" Height="25">
<Button Command="Cut" CommandTarget="{Binding ElementName=TextBox}"
Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/>
<Button Command="Copy" CommandTarget="{Binding ElementName=TextBox}"
Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/>
<Button Command="Paste" CommandTarget="{Binding ElementName=TextBox}"
Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/>
<Button Command="Undo" CommandTarget="{Binding ElementName=TextBox}"
Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/>
<Button Command="Redo" CommandTarget="{Binding ElementName=TextBox}"
Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/>
<TextBox x:Name="TextBox" Width="200"/>
</StackPanel>
The Button and TextBox have no direct knowledge of each other, yet through commands we achieve rich interaction. The more standardization on built-in commands, the more seamless and declarative the interaction between controls.
💡 Why this matters: Using CommandTarget lets you specify which control the command acts upon, enabling multiple controls to share the same command without hard-coded references.
⭐ Key Takeaways
Commands provide an abstract, loosely coupled alternative to direct event handling, with built-in support for input gestures, enable/disable logic, and bubbling behavior. WPF includes five categories of built-in commands: ApplicationCommands, ComponentCommands, MediaCommands, NavigationCommands, and EditingCommands, all implemented as RoutedUICommand types. Every command must be connected to a CommandBinding that provides CanExecute and Execute handlers; without this, the command UI remains disabled. The CommandTarget property allows commands to target a specific control (like a TextBox) without coupling the controls together. Standardizing on built-in commands enables seamless, declarative interactions between controls, reducing hard-coded logic and improving maintainability.
🧠 Quick Revision Questions
- What three members must the ICommand interface define?
- Name at least four categories of built-in commands in WPF and give one example from each.
- Why does a button with
Command="Help"remain disabled until you add a CommandBinding? - What is the purpose of the
CommandTargetproperty when used with commands? - How does command bubbling work in relation to the element tree?
📘 Lecture 25 — Structuring and Deploying an Application
📖 Overview: This lecture focuses on structuring WPF applications, including window management, dialogs, and application lifecycle. It covers how to create single-instance apps, persist data using isolated storage, and deploy applications using ClickOnce vs Windows Installer, essential for building robust desktop applications.
🗂️ Topics Covered
The lecture covers WPF window types and properties, parent-child window relationships, modal and modeless dialogs, the Application class and its lifecycle events, single-instance application creation using mutex, splash screens, common dialogs like PrintDialog, isolated storage for data persistence, and deployment options including ClickOnce and Windows Installer.
📝 Lecture Summary
Structuring and Deploying an Application
The lecture begins by discussing how to structure and deploy WPF applications, covering standard Windows apps, partial trust web apps, and loose XAML. The Photo Gallery example is used throughout. A WPF Window is a Win32 window with the same chrome (non-client area), taskbar behavior, and properties like Icon, Title, WindowStyle, Topmost, and ShowInTaskbar. You can set position using Left and Top properties or WindowStartupLocation to CenterScreen or CenterOwner.
🔑 Definition — Window: A WPF window that inherits from Win32 window, with standard chrome and taskbar behavior.
📌 Example: Setting window startup location: WindowStartupLocation=CenterScreen
Child Windows
Any number of child windows can be created by instantiating a Window derived class and calling Show. A child window behaves like a parent window but closes when the parent closes and is also minimized, also called a modeless dialog. Another approach is setting the Owner property after the parent is shown, using the OwnedWindows property. Windows have Activated & Deactivated events. Use the Activate method like SetForegroundWindow. Set ShowActivated=false to not show initially.
📐 Formula: Child window creation → ChildWindow child = new ChildWindow(); child.Show();
💡 Why this matters: Proper window management prevents orphaned windows and ensures clean UI lifecycle.
Override Methods for Window Lifecycle
The code shows overriding OnClosing, OnClosed, and OnInitialized methods in the MainWindow class:
- OnClosing: Uses
CancelEventArgsto allow cancellation (e.g., prompt user) - OnClosed: Perform cleanup (e.g., persist favorites)
- OnInitialized: Restore state (e.g., retrieve persisted favorites)
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
if (MessageBox.Show("Are you sure you want to close Photo Gallery?",
"Annoying Prompt", MessageBoxButton.YesNo, MessageBoxImage.Question)
== MessageBoxResult.No)
e.Cancel = true;
}
🔑 Definition — DispatcherObject: Cannot be accessed from a different thread, which avoids many threading issues.
Application Class and Main()
The Application class manages the application lifecycle. You can create a message loop to process Windows messages. The Main() method must have [STAThread] attribute. Two approaches:
- Create Application explicitly:
[STAThread]
public static void Main()
{
Application app = new Application();
MainWindow window = new MainWindow();
window.Show();
app.Run(window);
}
- Use StartupUri in XAML:
[STAThread]
public static void Main()
{
Application app = new Application();
app.StartupUri = new Uri("MainWindow.xaml", UriKind.Relative);
app.Run();
}
The App.xaml file is assigned ApplicationDefinition build action, which generates App.g.cs. This file contains the auto-generated Main() method.
🔑 Definition — Application Class: Manages application lifetime, window collection, and provides events like Startup, Exit, Activated, Deactivated, SessionEnding.
Single Instance Application
To create a single-instance application, use a Mutex. The Mutex constructor's first parameter indicates ownership, and the out parameter indicates if this is the first instance.
bool mutexIsNew;
using (System.Threading.Mutex m =
new System.Threading.Mutex(true, uniqueName, out mutexIsNew))
{
if (mutexIsNew)
// This is the first instance. Run the application.
else
// There is already an instance running. Exit!
}
🔑 Definition — Mutex: A synchronization primitive used to ensure only one instance of an application runs at a time.
Splash Screen
A splash screen can be created in a WPF project by adding a new item → Splash Screen. The image gets build action SplashScreen. Note: Nothing fancy because WPF isn't loaded yet.
Modal Dialogs and Common Dialogs
Modal dialogs include common dialogs provided by Win32. Instantiate, call ShowDialog(), and process the result.
void printMenu_Click(object sender, RoutedEventArgs e)
{
PrintDialog pd = new PrintDialog();
if (pd.ShowDialog() == true) // Result could be true, false, or null
pd.PrintVisual(image, ...);
}
A window shown as a dialog sets its DialogResult to bool. Setting it closes the window, or set Button's IsDefault property to true.
void okButton_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}
🔑 Definition — ShowDialog(): Displays a window as a modal dialog, returning a nullable bool (true, false, or null).
Persisting and Restoring Data
Use .NET Isolated Storage for persisting data. Physically located in the user's Documents folder in a hidden folder. The VS-generated Settings class provides an easier but strongly-typed alternative in app.config.
OnClosed (write data):
IsolatedStorageFile f = IsolatedStorageFile.GetUserStoreForAssembly();
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream("myfile", FileMode.Create, f))
using (StreamWriter writer = new StreamWriter(stream))
{
foreach (TreeViewItem item in favoritesItem.Items)
writer.WriteLine(item.Tag as string);
}
OnInitialized (read data):
IsolatedStorageFile f = IsolatedStorageFile.GetUserStoreForAssembly();
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream("myfile", FileMode.OpenOrCreate, f))
using (StreamReader reader = new StreamReader(stream))
{
string line = reader.ReadLine();
while (line != null)
{
AddFavorite(line);
line = reader.ReadLine();
}
}
🔑 Definition — Isolated Storage: A .NET mechanism for storing data in a user-specific, application-specific location, physically in the user's Documents folder.
ClickOnce vs Windows Installer
Windows Installer benefits: custom setup UI (EULA), control over file installation location, arbitrary code at setup time, install shared assemblies in GAC, register COM components and file associations, install for all users, offline installation from CD.
ClickOnce benefits: built-in support for automatic updates and rollback, web-like "go-away" experience or start menu entry, all files in isolated area (no effect on other apps), clean uninstallation, partial trust support via .NET Code Access Security.
🔑 Definition — ClickOnce: A deployment technology that provides automatic updates, isolated installation, and partial trust execution.
⭐ Key Takeaways
- WPF Windows are Win32 windows with standard chrome and behavior; child windows can be modeless or modal via Show/ShowDialog, and must use Owner for owned windows.
- The Application class manages lifecycle events (Startup, Exit, Activated, SessionEnding) and provides a Window collection and ShutdownMode; Main() must be [STAThread] and use App.Run().
- Single-instance applications use a Mutex to detect and prevent multiple instances; the Mutex constructor's out parameter indicates if it's the first instance.
- Data persistence uses IsolatedStorageFile with StreamWriter/StreamReader in OnClosed/OnInitialized overrides, or the VS-generated Settings class in app.config.
- Deployment choices: ClickOnce offers automatic updates and partial trust, while Windows Installer provides full control, custom setup, GAC installation, and all-users installation.
🧠 Quick Revision Questions
- What are the two ways to create child windows in WPF, and what is the difference between them?
- How does the Application class manage the application lifecycle, and what events does it provide?
- Explain how to implement a single-instance application using Mutex in WPF.
- Describe the steps to persist and restore data using Isolated Storage in the OnClosed and OnInitialized methods.
- Compare ClickOnce and Windows Installer deployment: list three advantages of each approach.
📘 Lecture 26 — Navigation-Based Applications
📖 Overview: This lecture covers navigation-based applications in WPF, exploring how to organize user interfaces around navigation using Pages, NavigationWindow, and Frame containers. It explains the journal system, navigation methods, data passing techniques between pages, and the PageFunction pattern for returning data.
🗂️ Topics Covered
Navigation containers (NavigationWindow and Frame), Page element fundamentals, Navigation methods (Navigate, Hyperlinks, Journal), Journal system and custom content states, Navigation events, Data passing between pages, and PageFunction for returning data. The lecture uses a photo gallery application as a running example to demonstrate navigation concepts.
📝 Lecture Summary
Navigation-Based Applications
Navigation-based apps include Windows Explorer, Media Player, and Photo Gallery. Navigation support can be used for wizards or to organize the entire UI around navigation. Content is typically placed in a Page, which is a simpler version of Window, then hosted in a NavigationWindow or a Frame. These containers provide support for navigating, a history journal, and navigation-related events.
💡 Why this matters: Navigation containers allow building multi-page applications with standard forward/back navigation, similar to web browsers.
🔑 Definition — NavigationWindow: A top-level window with built-in navigation UI (back/forward buttons on top).
🔑 Definition — Frame: A navigation container that works like an HTML frame or iframe, without default navigation UI (though it can be shown).
<navigationwindow
Xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="photogallery.Container"
Title="Photo Gallery" Source="mainpage.xaml"
/>
Page Element
The Page element does everything Window does except for OnClosed and OnClosing events. A navigation-enabled version points the startup URI to the NavigationWindow and references MainPage.xaml as content.
<Page x:Class="photogallery.mainpage"
Title="Photo Gallery" Loaded="Page_Loaded">
<!-- Application-specific content -->
</Page>
Page can interact with its navigation container using the NavigationService class, which exposes functionality regardless of whether the container is a NavigationWindow or Frame. You can get an instance by calling NavigationService.GetNavigationService(page) or use the page's NavigationService property.
Example: Setting a title for the drop-down menu on Back/Forward buttons:
this.NavigationService.Title = "Main Photo Gallery Page";
Example: Refreshing the current Page:
this.NavigationService.Refresh();
Page also has properties like WindowHeight, WindowWidth, and WindowTitle that control parent container behavior, settable both in code and XAML.
Navigation Methods
Navigation can be performed in three main ways:
- Calling the Navigate method
- Using Hyperlinks
- Using the Journal
Navigate method can navigate to a page instance or via URI:
// Navigate to a page instance
PhotoPage nextPage = new PhotoPage();
this.NavigationService.Navigate(nextPage);
// Or navigate to a page via a URI
this.NavigationService.Navigate(new Uri("photopage.xaml", UriKind.Relative));
The root of XAML must be "Page". Navigation can also go to HTML:
this.NavigationService.Navigate(new Uri("http://www.adamnathan.net/wpf"));
Two additional properties useful only from XAML:
this.NavigationService.Content = nextPage;
this.NavigationService.Source = new Uri("photopage.xaml", UriKind.Relative);
Hyperlink element is used to link to XAML pages:
<TextBlock>
Click
<Hyperlink NavigateUri="photopage.xaml">here</Hyperlink> to view the photo.
</TextBlock>
Hyperlinks can also handle Click events, navigate from HTML to WPF, use TargetName to update a Frame, or use # and any named element in a page.
Using Journal
The Journal provides the logic behind Back and Forward navigation. Internally it uses two stacks; Back/Fwd moves pages between stacks. Any other action empties the Forward stack.
🔑 Definition — Journal: The history system that tracks navigation and enables Back/Forward functionality.
Containers have GoBack(), GoForward(), CanGoBack, and CanGoForward properties to avoid exceptions. NavigationWindow always has a journal, but Frame's journal depends on JournalOwnership:
- OwnsJournal: Frame owns its journal
- UsesParentJournal: Uses parent's journal
- Automatic: Uses parent journal when hosted in a window or frame
When Frame has a journal, it shows Back/Forward buttons, but this can be hidden with NavigationUIVisibility="Hidden".
Navigation with URI or Hyperlink always creates a new instance. You can control instance creation when calling Navigate with Page, or use the JournalEntry.KeepAlive attached property to preserve state across Back/Forward navigation. RemoveFromJournal means a page is not stored in the journal.
Custom Content State
For application-specific features like undo/redo, navigation containers support AddBackEntry with a custom CustomContentState abstract class that must define a Replay method. Optionally, JournalEntryName can be set.
Example from photo gallery for undoable image rotation:
[Serializable]
class RotateState : CustomContentState
{
FrameworkElement element;
double rotation;
public RotateState(FrameworkElement element, double rotation)
{
this.element = element;
this.rotation = rotation;
}
public override string JournalEntryName
{
get { return "Rotate " + rotation + "°"; }
}
public override void Replay(NavigationService navigationService, NavigationMode mode)
{
// Rotate the element by the specified amount
element.LayoutTransform = new RotateTransform(rotation);
}
}
Navigation Events
NavigationStopped is an event called instead of LoadCompleted if an error occurs or navigation is cancelled. These events are also defined in the Application class to handle navigation for any container. HTML-to-HTML navigation is not stored in the journal and raises no events.
Passing Data Between Pages
Data can be passed between pages using several methods:
- Using Navigate overload with extra object parameter:
int photoId = 10;
this.NavigationService.Navigate(nextPage, photoId);
The target page receives it in LoadCompleted:
void container_LoadCompleted(object sender, NavigationEventArgs e)
{
if (e.ExtraData != null)
LoadPhoto((int)e.ExtraData);
}
- Using constructor parameters:
public PhotoPage(int id)
{
LoadPhoto(id);
}
Then navigate by instance:
PhotoPage nextPage = new PhotoPage(photoId);
this.NavigationService.Navigate(nextPage);
- Using Application.Properties:
Application.Properties["photoId"] = 10;
this.NavigationService.Navigate(/* */);
if (Application.Properties["photoId"] != null)
LoadPhoto((int)Application.Properties["photoId"]);
PageFunction for Returning Data
PageFunction acts like a function that can return data to the calling page. It is a generic class that specifies the return type.
<PageFunction
x:TypeArguments="sys:String"
x:Class="myproject.PageFunction1"
Title="PageFunction1">
<Grid>
</Grid>
</PageFunction>
Usage:
PageFunction1 nextPage = new PageFunction1<string>();
this.NavigationService.Navigate(nextPage);
nextPage.Return += new ReturnEventHandler<string>(nextPage_Return);
Handling the return:
void nextPage_Return(object sender, ReturnEventArgs<string> e)
{
string returnValue = e.Result;
}
Calling OnReturn(new ReturnEventArgs<string>("the data")); returns data from the PageFunction.
⭐ Key Takeaways
Navigation containers (NavigationWindow and Frame) use Pages as content units and provide a journal system for Back/Forward navigation. The NavigationService class enables programmatic control of navigation regardless of the container type. Data can be passed between pages through Navigate overloads with extra data, constructor parameters, or Application.Properties. For returning data, PageFunction provides a clean mechanism similar to function calls. Custom content states via CustomContentState enable application-specific journal entries for undo/redo functionality.
🧠 Quick Revision Questions
- What are the three main ways to perform navigation in WPF navigation applications?
- How does the JournalOwnership property affect a Frame's behavior regarding Back/Forward buttons?
- What is the difference between NavigationWindow and Frame in terms of default UI?
- How can you pass data from one page to another when navigating, and what events can the target page use to receive that data?
- What is the purpose of the CustomContentState class and how does the Replay method work in the context of the journal system?
📘 Lecture 27 — XAML Browser Applications & Resources
📖 Overview: This lecture explores XAML Browser Applications (XBAPs) for delivering WPF content in web browsers with partial-trust security. It also covers WPF resource management, including binary and logical resources, enabling efficient deployment and localization of application assets.
🗂️ Topics Covered
The lecture covers XBAP architecture and deployment, partial-trust restrictions and security implications, clickonce caching and navigation integration, full-trust browser app configuration, downloading files on demand using application deployment API, and WPF resource types including binary resources, content files, and pack URIs for accessing embedded assets.
📝 Lecture Summary
XAML Browser Applications (XBAPs)
XBAPs are partial-trust WPF applications delivered in a browser, similar to Silverlight but with some limitations. Not all WPF features are available by default, navigation is integrated into the browser, and deployment differs from desktop apps. In Visual Studio, you create a WPF Browser Application, design UI in a Page, compile and run—these are online-only ClickOnce applications with special WPF handling.
⚠️ Security Restriction: Partially trusted code cannot access certain APIs, such as:
AddFavorite(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures));
This throws an exception because it requires FileIOPermission, which is not granted by default.
🔑 Definition — BrowserInteropHelper.IsBrowserHosted: A property that checks whether the current application is running as an XBAP in a browser.
📌 Example: Using OpenFileDialog in an XBAP:
string fileContents = null;
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == true)
{
using (Stream s = ofd.OpenFile())
using (StreamReader sr = new StreamReader(s))
{
fileContents = sr.ReadToEnd();
}
}
Available capabilities despite partial trust: Rich text and media, isolated storage (up to 512KB), arbitrary files on host web server, browser file open dialog for local files.
Parameter passing methods:
- BrowserInteropHelper.Source — retrieves the complete URL
- Application.GetCookie — retrieves browser cookies
💡 Why this matters: Any assembly marked with AllowPartiallyTrustedCallers and placed in the GAC can be called by partial-trust code, which can be a security loophole.
Full-Trust Browser Apps
To create a full-trust XBAP, modify the project file:
Change:
<TargetZone>Internet</TargetZone>
To:
<TargetZone>Custom</TargetZone>
In the ClickOnce application manifest, add:
<PermissionSet class="System.Security.PermissionSet" version="1"
ID="Custom" SameSite="site" Unrestricted="true"/>
Integrated Navigation: IE7 and later merge the journal for a streamlined interface. If the XBAP appears in an iframe, there is still a separate navigation bar.
Publishing: Use VS Publishing Wizard or Mage tool in SDK, copy files to a web server configured to serve them. Users can install by navigating to a URL without security prompts if standard permissions are sufficient.
Downloading Files on Demand
Files can be assigned to a download group in Visual Studio under Publish → Application Files in project properties. Use the System.Deployment.Application API to prompt download and receive notifications.
📌 Example: Load a group and navigate to Page2:
public partial class Page1 : Page
{
public Page1() { InitializeComponent(); }
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
if (ApplicationDeployment.IsNetworkDeployed)
{
ApplicationDeployment.CurrentDeployment.DownloadFileGroupCompleted +=
delegate {
Dispatcher.BeginInvoke(DispatcherPriority.Send,
new DispatcherOperationCallback(GoToPage2), null);
};
ApplicationDeployment.CurrentDeployment.DownloadFileGroupAsync("mygroup");
}
else
{
GoToPage2(null);
}
}
private object GoToPage2(object o)
{
return NavigationService.Navigate(new Uri("Page2.xaml", UriKind.Relative));
}
}
Loose XAML is sometimes more powerful than HTML for delivering content.
WPF Resources
WPF has two types of resources:
- Binary resources — what the .NET Framework considers a resource (including compiled XAML)
- Logical resources — XAML resource dictionaries
Binary resources can be:
- Embedded in assembly
- Loose files (may or may not be known at compile time)
- Localizable or non-localizable
🔑 Definition — Build Action: Resource vs Content:
- Resource: Embedded in the assembly binary
- Content: Loose file deployed alongside the assembly
- ❌ Do not use EmbeddedResource — WPF doesn't fully support it
📐 Pack URIs for accessing resources:
pack://application:,,,/logo.jpg— for embedded resourcespack://siteoforigin:,,,/slideshow.gif— for content files at the site of origin
📌 Example: Assigning binary resources in XAML:
<Image Height="21" Source="previous.gif"/>
Or explicitly using pack URI:
<Image Height="21" Source="pack://siteoforigin:,,,/slideshow.gif"/>
📌 Example: From procedural code:
Image image = new Image();
image.Source = new BitmapImage(new Uri("pack://application:,,,/logo.jpg"));
💡 Why this matters: Subfolders can be used for embedded resources, but from procedural code, you cannot use XAML shortcuts—you must use full pack URIs.
⭐ Key Takeaways
XBAPs deliver WPF content in browsers with partial-trust restrictions, requiring careful API selection to avoid security exceptions—use isolated storage (512KB limit) and browser dialogs for file access. Full-trust XBAPs require modifying project settings and adding unrestricted permission sets to the manifest. Files can be downloaded on demand using the ApplicationDeployment API with download groups for efficient loading. WPF resources include binary resources (embedded or content) accessed via pack URIs—embed for localization, use content for loose files. Always use Build Action "Resource" or "Content" (not "EmbeddedResource") and understand pack URI syntax for both XAML and procedural code access.
🧠 Quick Revision Questions
- What property checks whether a WPF application is running as an XBAP in a browser?
- How much isolated storage is available by default in a partial-trust XBAP?
- What XML changes are required to convert an XBAP from partial-trust to full-trust?
- Which namespace contains the ApplicationDeployment class for downloading file groups?
- What is the difference between "Content" and "Resource" build actions for WPF binary resources?
📘 Lecture 28 — Localizing Binary Resources and Logical Resources
📖 Overview: This lecture covers two major WPF topics: how to localize binary resources using satellite assemblies and the LocBaml tool, and how to use logical resources (including static vs dynamic resources) to share objects like brushes across elements. Understanding these concepts is essential for building professional, multi-language WPF applications and for efficient UI styling.
🗂️ Topics Covered
Localizing binary resources using satellite assemblies, setting UICulture in project files, using LocBaml to parse and generate localized resource assemblies, and testing localization by changing thread culture. Then, the lecture introduces WPF logical resources: defining and using resources like SolidColorBrush and LinearGradientBrush in XAML, using StaticResource and DynamicResource markup extensions, resource lookup behavior, sharing vs non-sharing with x:Shared, and procedural code equivalents for resource access. Finally, it contrasts static and dynamic resources with examples of system colors and equivalent C# code.
📝 Lecture Summary
Localizing Binary Resources
Localizing binary resources means separating them into satellite assemblies and using LocBaml to manage string localization. To build a satellite assembly automatically, set the UICulture in the project file. Open the project file in a text editor and add <UICulture>en-US</UICulture> under the property group (affects Debug, Release, etc.). After rebuilding, you’ll find an en-US folder containing the satellite assembly named assemblyname.resources.dll. Also mark the assembly with the NeutralResourcesLanguage attribute matching the default culture.
Next, apply a Uid directive to any element needing localization. Run msbuild /t:updateuid projectname.csproj to generate unique IDs. Then run Locbaml /parse projectname.g.en-US.resources /out:en-US.csv to extract strings to a CSV file. After editing/translating the CSV, run Locbaml /generate Project-Name.resources.dll /trans:fr-CA.csv /cul:fr-CA to create the localized satellite assembly. Copy the assembly with a name matching the locale (e.g., fr-CA). To test, set System.Threading.Thread.CurrentThread.CurrentUICulture and CurrentCulture to an instance of the desired CultureInfo.
💡 Why this matters: Without this technique, every language version would require a separate codebase; satellite assemblies allow single-codebase multi-language deployment.
🔑 Definition — Satellite assembly: A DLL containing localized resources (like strings and images) for a specific culture, deployed alongside the main application assembly.
📌 Example: For a project named MyApp with default culture en-US, setting <UICulture>en-US</UICulture> creates en-US\MyApp.resources.dll. To localize to French-Canadian, edit the CSV then generate fr-CA\MyApp.resources.dll.
Logical Resources Introduction
Logical resources are introduced by WPF. They are arbitrary .NET objects stored and named in an element’s Resources property. They are meant to be shared by multiple child objects. Both FrameworkElement and FrameworkContentElement have a Resources property. Resources are often Style or data providers. First examples use simple brushes. Much like CSS, they allow sharing objects across many elements.
For example, without resources, every button must repeat Background="Yellow" and BorderBrush="Red". With a Window.Resources dictionary, you define:
<Window.Resources>
<SolidColorBrush x:Key="backgroundbrush">Yellow</SolidColorBrush>
<SolidColorBrush x:Key="borderbrush">Red</SolidColorBrush>
</Window.Resources>
Then apply via Background="{StaticResource backgroundbrush}" on each button.
You can also use a LinearGradientBrush as a resource:
<LinearGradientBrush x:Key="backgroundbrush" StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="Blue" Offset="0"/>
<GradientStop Color="White" Offset="0.5"/>
<GradientStop Color="Red" Offset="1"/>
</LinearGradientBrush>
Now changing the brush in one place affects all elements using that resource.
🔑 Definition — StaticResource markup extension: Looks up a resource by key at load time by walking the logical tree (element → parent → application level → system resources). The closest resource to the element is chosen. For maximum sharing, define resources at root level or in Application.Resources.
⚠️ Important: For multi-threaded scenarios, use either Frozen objects or set x:Shared="False" to avoid thread-safety issues.
Static vs Dynamic Resources
StaticResource is resolved once at load time; DynamicResource is reapplied every time the resource changes. There is nothing special about the resources themselves — only whether you want to see updates. DynamicResource has more overhead but supports demand loading (resources loaded only when needed). Dynamic resources can only be used to set dependency property values. Static resources can even abstract whole controls.
💡 Why this matters: Choose StaticResource for performance when the resource never changes; choose DynamicResource for themes, system colors, or resources that change at runtime.
🔑 Definition — Dependency property: A property that is registered with the WPF property system and supports features like data binding, styling, and animation.
Resource Sharing and Forward References
With StaticResource, forward references are not supported — the resource must be defined before it is used. Also, an Image control can only have one parent, so you cannot share the same instance across multiple elements unless you use x:Shared="False".
For example, with x:Shared="False", a new instance of the Image is created each time:
<Window.Resources>
<Image x:Shared="False" x:Key="zoom" Height="21" Source="zoom.gif"/>
</Window.Resources>
<StackPanel>
<StaticResource ResourceKey="zoom"/>
<StaticResource ResourceKey="zoom"/>
<StaticResource ResourceKey="zoom"/>
</StackPanel>
With DynamicResource, forward references are possible because the lookup happens at runtime:
<Window Background="{DynamicResource backgroundbrush}">
<Window.Resources>
<SolidColorBrush x:Key="backgroundbrush">Yellow</SolidColorBrush>
</Window.Resources>
</Window>
Procedural Code Resource Access
In C#, you can add resources and access them in several ways:
-
Add resources directly to Window.Resources:
Window.Resources.Add("backgroundbrush", new SolidColorBrush(Colors.Yellow)); -
FindResource method (walks the logical tree):
button.Background = (Brush)button.FindResource("backgroundbrush");⚠️ The button must be added to the visual tree before calling FindResource.
-
SetResourceReference method (dynamic behavior):
button.SetResourceReference(Button.BackgroundProperty, "backgroundbrush"); -
Direct dictionary access (no tree walk):
button.Background = (Brush)window.Resources["backgroundbrush"];
System Colors and Equivalent Patterns
Several XAML patterns exist for accessing system colors, each with C# equivalents:
| XAML | C# Equivalent |
|---|---|
Background="SystemColors.WindowBrush" | button.Background = (Brush)new BrushConverter().ConvertFrom("SystemColors.WindowBrush"); |
Background="{x:Static SystemColors.WindowBrush}" | button.Background = SystemColors.WindowBrush; |
Background="{StaticResource SystemColors.WindowBrushKey}" | button.Background = (Brush)FindResource("SystemColors.WindowBrushKey"); |
Background="{StaticResource {x:Static SystemColors.WindowBrush}}"} | button.Background = (Brush)FindResource(SystemColors.WindowBrush); |
Background="{StaticResource {x:Static SystemColors.WindowBrushKey}}" | button.Background = (Brush)FindResource(SystemColors.WindowBrushKey); |
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" | button.SetResourceReference(Button.BackgroundProperty, SystemColors.WindowBrushKey); |
⭐ Key Takeaways
Resources are extremely important in professional WPF applications. They enable localization, increase productivity by consolidating and sharing objects across multiple elements, and are the foundation for styles and data binding. For localization, use UICulture, LocBaml, and satellite assemblies. For logical resources, remember that StaticResource resolves at load time and is faster, while DynamicResource resolves at runtime and supports updates. Use x:Shared="False" to create multiple instances of a resource that cannot be shared (e.g., UI elements with single-parent constraints). System colors can be accessed via multiple XAML markup patterns, with StaticResource being the most common for theming.
🧠 Quick Revision Questions
- What is the purpose of setting
<UICulture>in a WPF project file, and what does it generate during build? - What command extracts resource strings to a CSV file for localization editing?
- What is the difference between StaticResource and DynamicResource in terms when the resource is resolved?
- Why must you use
x:Shared="False"when defining an Image as a resource if you want to use it multiple times in the same parent? - In C#, what method should you call on a Button to get a resource that walks the logical tree, and why must the button be in the visual tree first?
📘 Lecture 29 — Data Binding
📖 Overview: This lecture explores data binding in WPF, a mechanism that connects arbitrary .NET objects (like collections, XML files, or database tables) to visual elements. It explains how binding simplifies UI development by automating synchronization between data sources and target properties, eliminating manual iteration and refresh logic.
🗂️ Topics Covered
The lecture covers the fundamentals of data binding, including setting up bindings in procedural and XAML code, binding to collections and .NET properties, using DataContext for implicit sources, and applying String Formatting, Data Templates, and Value Converters to customize how data is displayed in UI elements like ListBox and Label.
📝 Lecture Summary
Chapter 29: Lecture 29 – Data Binding
Data binding connects arbitrary objects together, where "data" can be any .NET object: a collection object, XML file, web service, database table, custom object, or even a WPF element like a Button. The classic scenario is visually representing items (e.g. from a ListBox or DataGrid) from an XML file, database, or in-memory collection. Instead of iterating and adding items manually, you tell the ListBox to get its data from another source, keep them up to date, and format them.
Binding binds two properties together and keeps a communication channel open. You set up a Binding once and let it handle all the synchronization.
🔑 Definition — Binding: A connection between two properties (source and target) that automatically synchronizes values, with source being the object/property providing data and target being the dependency property consuming it.
📌 Example — Procedural Code Binding:
// In MainWindow constructor
Binding binding = new Binding();
binding.Source = treeview;
binding.Path = new PropertyPath("selectedItem.Header");
// Attach to target property
Currentfolder.SetBinding(TextBlock.TextProperty, binding);
When an item with no header is selected, a default value is returned (no exception raised).
📌 Example — XAML Markup Extension:
<TextBlock x:Name="currentfolder"
Text="{Binding ElementName=treeview, Path=selectedItem.Header}" />
Alternative with Source:
<TextBlock Text="{Binding Source={x:Reference treeview}, Path=selectedItem.Header}" />
📌 Example — Binding for "Nothing selected":
<TextBlock Text="{Binding TargetNullValue=Nothing is selected.}" />
Key interfaces: System.ComponentModel.INotifyPropertyChanged — the source object should implement this interface (which has a single PropertyChanged event) for proper notification. The target must be a dependency property.
Binding to Collections
When binding a ListBox to a collection, ItemsSource (a dependency property) is used instead of Items (which is not a dependency property). The source property must implement INotifyCollectionChanged (also known as ObservableCollection).
📌 Example — Binding ListBox to Collection:
<ListBox x:Name="picturebox"
ItemsSource="{Binding Source={StaticResource photos}}" />
The DisplayMemberPath property can be used to improve display:
<ListBox x:Name="picturebox" DisplayMemberPath="Name"
ItemsSource="{Binding Source={StaticResource photos}}" />
💡 Why this matters: You cannot mix Items and ItemsSource, but you always retrieve from Items. For richer display, use Data Templates or Value Converters.
Synchronization with Current Item
The IsSynchronizedWithCurrentItem property synchronizes selection across multiple ListBox controls:
<ListBox IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name" ... />
<ListBox IsSynchronizedWithCurrentItem="True" DisplayMemberPath="DateTime" ... />
<ListBox IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Size" ... />
Note: scrolling is not synchronized, only the first selection.
Implicit Data Source: DataContext
The implicit data source is provided by DataContext. You set DataContext on a parent element and then don't specify Source or ElementName.
📌 Example — Using DataContext:
<StackPanel DataContext="{StaticResource photos}">
<Label x:Name="numitemslabel" Content="{Binding Path=Count}" />
<ListBox x:Name="picturebox" DisplayMemberPath="Name" ItemsSource="{Binding}" />
</StackPanel>
String Formatting
String Formatting works only if the target property is a string. Use {} to escape the initial brace.
📌 Example — String Formatting:
<TextBlock Text="{Binding StringFormat={}{0} item(s), Source={StaticResource photos}, Path=Count}" />
Or using element syntax:
<TextBlock.Text>
<Binding Source="{StaticResource photos}" Path="Count">
<Binding.StringFormat>{0} item(s)</Binding.StringFormat>
</Binding>
</TextBlock.Text>
String formatting can be used even without data binding:
<ListBox ItemStringFormat="{}{0:C}"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:Int32>-9</sys:Int32>
<sys:Int32>9</sys:Int32>
<sys:Int32>1234</sys:Int32>
<sys:Int32>1234567</sys:Int32>
</ListBox>
Data Templates
With Data Template, UI is auto-applied to arbitrary .NET objects when rendered. By setting these properties, you can swap in a complete new visual tree easily in XAML.
📌 Example — Basic Data Template:
<ListBox x:Name="picturebox" ItemsSource="{Binding Source={StaticResource photos}}">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="placeholder.jpg" Height="35" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
With a template, there is an implicit DataContext. Templates can be shared as resources and can even be auto-applied to some type by setting its DataType property. There is also HierarchicalDataTemplate that understands hierarchies (used with TreeView or Menu).
📌 Example — Template with Binding:
<ListBox x:Name="picturebox" ItemsSource="{Binding Source={StaticResource photos}}">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Path=fullpath}" Height="35" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Value Converters
Value Converters morph the source value into something else, introducing custom logic that can change type (e.g., a brush based on some enumeration).
🔑 Definition — IValueConverter: An interface with two methods — Convert (source → target) and ConvertBack (target → source) — that transform values during binding.
📌 Example — CountToBackgroundConverter:
<Window.Resources>
<local:CountToBackgroundConverter x:Key="myconverter" />
</Window.Resources>
<Label Background="{Binding Path=Count, Converter={StaticResource myconverter},
Source={StaticResource photos}}" />
public class CountToBackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (targetType != typeof(Brush))
throw new InvalidOperationException("The target must be a Brush!");
int num = int.Parse(value.ToString());
return (num == 0 ? Brushes.Yellow : Brushes.Transparent);
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
⭐ Key Takeaways
The most critical concept from this lecture is that data binding eliminates manual synchronization by establishing an automatic communication channel between source and target properties. You must understand that the target property must be a dependency property, and source objects should implement INotifyPropertyChanged for proper updates. For collections, use ItemsSource with ObservableCollection (which implements INotifyCollectionChanged). The DataContext provides an implicit data source that propagates through the element tree, while DisplayMemberPath offers simple display control. For richer customization, use Data Templates to define visual trees for data items, String Formatting for text output, and Value Converters (implementing IValueConverter) for transforming values.
🧠 Quick Revision Questions
- What interface must a source object implement to properly notify property changes in WPF data binding, and what event does it provide?
- When binding a ListBox to a collection, why must you use
ItemsSourceinstead ofItems? - How does
DataContextsimplify XAML binding when you have multiple controls bound to the same data source? - What is the purpose of a
ValueConverterin WPF binding, and what two methods must it implement? - With a DataTemplate, what becomes the implicit DataContext inside the template, and why can't you mix
ItemsandItemsSource?
📘 Lecture 30 — Customizing Collection View and Data Providers
📖 Overview: This lecture explores advanced techniques for customizing collection views in WPF, including sorting, grouping, filtering, and navigation capabilities. It also introduces data providers (XmlDataProvider and ObjectDataProvider) for binding to various data sources, along with LINQ integration for modern data binding approaches.
🗂️ Topics Covered
The lecture covers customizing collection views with ICollectionView interface, SortDescriptions for sorting data, PropertyGroupDescription for grouping, Filter predicates for filtering, and navigation methods for managing current items. It also discusses CollectionViewSource for creating custom views, data providers including XmlDataProvider and ObjectDataProvider, and data binding with LINQ queries.
📝 Lecture Summary
Customizing Collection View
When IsSynchronizedWithCurrentItem is set to true, WPF inserts a default "view" that implements the ICollectionView interface. This view provides built-in support for sorting, grouping, filtering, and navigation operations on collections. Multiple views can exist for the same source object, allowing different presentations of the same data.
💡 Why this matters: Collection views separate data presentation from data storage, enabling flexible UI patterns without modifying the underlying collection.
Sorting with SortDescriptions
The SortDescriptions property is a collection of SortDescription objects that specify fields and sort order. Multiple sort criteria can be combined, such as sorting by DateTime first, then by Name. A Clear() method returns the view to unsorted state. The example shows three buttons to sort by Name, DateTime, and Size with toggle functionality:
SortDescription sort = new SortDescription("Name", ListSortDirection.Ascending);
view.SortDescriptions.Add(new SortDescription("datetime", ListSortDirection.Descending));
view.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
🔑 Definition — ICollectionView: An interface providing support for sorting, grouping, filtering, and navigation over a collection without modifying the source collection itself.
📐 Formula: View.SortDescriptions.Add(new SortDescription(propertyName, ListSortDirection)) → Adds a sorting rule that can combine multiple properties with ascending/descending order.
📌 Example: The sorthelper method gets the default view using CollectionViewSource.GetDefaultView(this.FindResource("photos")), checks if already sorted ascending by the current property, and toggles by clearing and adding descending order, otherwise clears and adds ascending order.
Grouping with PropertyGroupDescription
The GroupDescriptions property contains PropertyGroupDescription objects, but grouping has no visual effect without setting the GroupStyle property on the ListBox. The example shows grouping photos by DateTime:
view.GroupDescriptions.Clear();
view.GroupDescriptions.Add(new PropertyGroupDescription("datetime"));
XAML requires a GroupStyle with a HeaderTemplate to display grouping:
<ListBox x:Name="pictureBox" ItemsSource="{Binding Source={StaticResource photos}}">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="1">
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold"/>
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListBox.GroupStyle>
</ListBox>
🔑 Definition — PropertyGroupDescription: An object that specifies which property to group by, optionally using a value converter to transform the grouping key.
📌 Example: A custom DateTimeToDateConverter converts DateTime values to "MM/dd/yyyy" format for grouping photos by date only, ignoring time:
view.GroupDescriptions.Add(new PropertyGroupDescription("datetime", new DateTimeToDateConverter()));
Filtering with Predicate
The Filter property accepts a Predicate<object> delegate. It is null by default. The example shows filtering to display only photos from the last 7 days using anonymous methods:
view.Filter = delegate(object o) {
return ((o as Photo).datetime - DateTime.Now).Days <= 7;
};
Alternative syntax using lambda expressions:
view.Filter = (o) => { return ((o as Photo).datetime - DateTime.Now).Days <= 7; };
🔑 Definition — Filter: A property of type Predicate<object> that determines which items from the source collection are visible in the view.
Navigation with CurrentItem Management
Navigation refers to managing the current item within the collection view. The ICollectionView interface provides CurrentItem, CurrentPosition properties, and methods for changing them. The example shows previous/next button handlers with wrapping behavior:
void previous_Click(object sender, RoutedEventArgs e) {
ICollectionView view = CollectionViewSource.GetDefaultView(this.FindResource("photos"));
view.MoveCurrentToPrevious();
if (view.IsCurrentBeforeFirst) view.MoveCurrentToLast();
}
void next_Click(object sender, RoutedEventArgs e) {
ICollectionView view = CollectionViewSource.GetDefaultView(this.FindResource("photos"));
view.MoveCurrentToNext();
if (view.IsCurrentAfterLast) view.MoveCurrentToFirst();
}
Binding paths for navigation include:
"{Binding Path=/}" <!-- Current item -->
"{Binding Path=/datetime}" <!-- Current item's DateTime property -->
"{Binding Path=Photos/}" <!-- Current item of nested collection -->
"{Binding Path=Photos/datetime}" <!-- Current item's DateTime in nested collection -->
🔑 Definition — CurrentItem: The item in the collection view that is tracked for navigation purposes, used in master/detail scenarios.
CollectionViewSource for Custom Views
CollectionViewSource can create new views that are applied to targets. It has its own SortDescriptions, GroupDescriptions properties, and a Filter event usable from XAML:
<CollectionViewSource x:Key="viewSource"
Filter="viewSource_Filter"
Source="{StaticResource photos}">
<CollectionViewSource.SortDescriptions>
<componentModel:SortDescription PropertyName="datetime" Direction="Descending"/>
</CollectionViewSource.SortDescriptions>
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="datetime"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
The event handler in code-behind:
void viewSource_Filter(object sender, FilterEventArgs e) {
e.Accepted = ((e.Item as Photo).datetime - DateTime.Now).Days <= 7;
}
💡 Why this matters: IsSynchronizedWithCurrentItem is true by default for custom views created with CollectionViewSource, requiring explicit false setting, which differs from default views.
Data Providers in WPF
Data source objects can be arbitrary - databases, registry, Excel spreadsheets, etc. Two generic data-binding-friendly ways to expose common items are XmlDataProvider and ObjectDataProvider. Starting with WPF 3.5 SP1, data binding works with LINQ queries, providing an easier alternative to WPF data providers.
XmlDataProvider binds to inline XML or external XML files:
<Window.Resources>
<XmlDataProvider x:Key="dataProvider" xpath="gamestats">
<x:XData>
<gamestats xmlns="">
<gamestat Type="Beginner"><highscore>1203</highscore></gamestat>
<gamestat Type="Intermediate"><highscore>1089</highscore></gamestat>
<gamestat Type="Advanced"><highscore>541</highscore></gamestat>
</gamestats>
</x:XData>
</XmlDataProvider>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Source={StaticResource dataProvider}, XPath=gamestat/highscore}"/>
</Grid>
🔑 Definition — XmlDataProvider: A data provider that binds to XML documents, using XPath for path navigation instead of the Path property.
📌 Example: To access attributes, use XPath="gamestat/@Type" to populate the list with Type attribute values. External files use: <XmlDataProvider x:Key="dataProvider" xpath="gamestats" Source="gamestats.xml"/>
XML namespace pollution is avoided with xmlns="". Objects from System.Xml allow using Path and XPath together. Hierarchical binding to XML trees requires HierarchicalDataTemplate.
⭐ Key Takeaways
Sorting must be applied before grouping, with the first sorting criterion matching the grouping criterion for meaningful output. Navigation through current item management works only when IsSynchronizedWithCurrentItem=true, otherwise SelectedItem and CurrentItem remain separate. CollectionViewSource provides full XAML-based view customization with its own SortDescriptions, GroupDescriptions, and Filter event. XmlDataProvider enables binding to XML data using XPath instead of Path, supporting both inline and external XML sources. Since WPF 3.5 SP1, LINQ queries can be used directly as binding sources, offering a cleaner alternative to traditional data providers for many scenarios.
🧠 Quick Revision Questions
- What interface is implemented by the default view when IsSynchronizedWithCurrentItem is true?
- What is the method used to remove all sorting from a collection view?
- Which XAML element must be set on a ListBox for grouping to have a visual effect?
- What type is the Filter property on ICollectionView, and what does it return?
- What XAML attribute does XmlDataProvider use instead of Path for specifying data navigation?
📘 Lecture 31 — Hierarchical Data Templates, ObjectDataProvider, Binding Modes, Validation & RSS Reader
📖 Overview: This lecture covers advanced data binding techniques in WPF, including hierarchical data templates for XML data, the ObjectDataProvider for .NET objects, binding modes and triggers, validation rules, and the construction of a complete RSS reader application without code-behind. Understanding these concepts enables building sophisticated, data-driven user interfaces with minimal code.
🗂️ Topics Covered
The lecture begins with hierarchical data templates using XmlDataProvider for tree structures, then covers the ObjectDataProvider for declarative .NET object instantiation and method binding. It explains binding modes, update source triggers, and validation rules including custom validation, exception validation, and data error validation. Finally, it demonstrates building a complete RSS reader application using only XAML.
📝 Lecture Summary
Hierarchical Data Templates with XmlDataProvider
The lecture explains how to use HierarchicalDataTemplate to display XML data hierarchically in a TreeView. For each node type, a HierarchicalDataTemplate is defined, and for leaf nodes, a regular DataTemplate is used. The ItemsSource property on the template specifies children using {Binding xpath=*}. The DataType attribute corresponds to the XML node name and is used internally as a key, eliminating the need for an explicit key.
🔑 Definition — HierarchicalDataTemplate: A WPF template that extends DataTemplate to allow specifying child items via the ItemsSource property, enabling hierarchical data display in TreeViews.
📐 Formula: <HierarchicalDataTemplate DataType="nodeName" ItemsSource="{Binding xpath=*}"> → This template applies to all XML nodes named "nodeName" and populates their children from all child XML elements.
📌 Example: For game stats XML:
<gamestats xmlns="">
<gamestat Type="Beginner">
<highscore>1203</highscore>
</gamestat>
</gamestats>
- Template for
gamestatsnode: displays italic "All Game Stats" - Template for
gamestatnode: displays bold 20pt text from@Typeattribute - Template for
highscoreleaf node: displays blue text from the inner text ({.})
ObjectDataProvider for .NET Objects
ObjectDataProvider wraps a .NET object as a data source, offering advantages over direct binding: declarative instantiation with parameterized constructors, method binding, and asynchronous data binding options. WPF provides two ways to mark async: the IsAsync property on Binding, and the IsAsynchronous property on XmlDataProvider and ObjectDataProvider (false by default for ObjectDataProvider, true by default for XmlDataProvider). When IsAsync is true, the source property is invoked on a background thread.
- 🔑 Definition — ObjectDataProvider: A WPF class that creates and uses a .NET object as a data source for binding, supporting constructor parameters and method binding.
- 📌 Example — Wrapping a collection:
<Window.Resources>
<local:Photos x:Key="photos"/>
<ObjectDataProvider x:Key="dataprovider"
ObjectInstance="{StaticResource photos}"/>
</Window.Resources>
- 📌 Example — Internal instantiation with constructor parameter:
<ObjectDataProvider x:Key="dataprovider"
ObjectType="{x:Type local:Photos}">
<ObjectDataProvider.ConstructorParameters>
<sys:Int32>23</sys:Int32>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
- 📌 Example — Binding to a method:
<ObjectDataProvider x:Key="dataprovider"
ObjectType="{x:Type local:Photos}"
MethodName="getfoldername"/>
Binding Modes and Update Source Trigger
Binding.Mode determines the direction of data flow:
- OneWay: Target updates when source changes
- TwoWay: Both target and source update each other
- OneWayToSource: Source updates when target changes (opposite of OneWay)
- OneTime: Target receives a snapshot at binding initiation; source changes are not reflected
UpdateSourceTrigger controls when the source gets updated in TwoWay binding:
- PropertyChanged: Updates on every target property value change
- LostFocus: Updates when focus leaves the target element (default)
- Explicit: Requires calling
BindingExpression.UpdateSource()manually
💡 Why this matters: TwoWay binding is default for editable controls like TextBox, requiring the ConvertBack method in value converters. Only OneWayToSource triggers ConvertBack.
Validation Rules
Validation provides immediate user feedback when invalid data is entered. Without binding, custom logic was needed, but binding automates data pushing to the target. Two approaches exist:
- Custom ValidationRule class — Derives from
ValidationRuleand overridesValidate()method - Source exceptions — Using
ExceptionValidationRuleorDataErrorValidationRule
🔑 Definition — ValidationRule: A base class for creating custom validation logic that is executed whenever the binding attempts to update the target.
🔑 Definition — ExceptionValidationRule: A built-in validation rule that catches exceptions thrown during source updates and shows them as validation errors.
🔑 Definition — DataErrorValidationRule: A built-in validation rule that checks if the source implements IDataErrorInfo interface.
📌 Example — Custom JPG validation rule:
public class JpgValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
string filename = value.ToString();
if (!File.Exists(filename))
return new ValidationResult(false, "Value is not a valid file.");
if (!filename.EndsWith(".jpg", StringComparison.InvariantCultureIgnoreCase))
return new ValidationResult(false, "Value is not a .jpg file.");
return new ValidationResult(true, null);
}
}
When validation fails, a default error adorner (thin red border) appears over the element, which can be customized via the Validation.ErrorTemplate attached property.
📌 Example — Setting validation in XAML:
<TextBox>
<TextBox.Text>
<Binding>
<Binding.ValidationRules>
<ExceptionValidationRule/>
<DataErrorValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Alternatively, using shortcuts: ValidatesOnExceptions="True" and ValidatesOnDataErrors="True".
Building an RSS Reader Without Code
The lecture demonstrates a complete RSS reader application using XmlDataProvider and master-detail binding. Key features include:
- A two-way TextBox for users to change the feed address, using
BindsDirectlyToSource="True"so the path doesn't refer to the RSS feed - UpdateSourceTrigger="PropertyChanged" to update on every keystroke
- A ListBox and Frame sharing the same data source for master-detail display
📌 Example — RSS Reader XAML:
<Window.Resources>
<XmlDataProvider x:Key="Feed"
Source="http://twitter.com/statuses/user_timeline/24326956.rss"/>
</Window.Resources>
<DockPanel DataContext="{Binding Source={StaticResource Feed}, XPath=/rss/channel/item}">
<TextBox DockPanel.Dock="Top"
Text="{Binding Source={StaticResource Feed},
BindsDirectlyToSource=true, Path=Source,
UpdateSourceTrigger=PropertyChanged}"/>
<Label DockPanel.Dock="Top"
Content="{Binding XPath=/rss/channel/title}"
FontSize="14" FontWeight="Bold"/>
<Label DockPanel.Dock="Top"
Content="{Binding XPath=/rss/channel/description}"/>
<ListBox DockPanel.Dock="Left" DisplayMemberPath="title"
ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="True" Width="300"/>
<Frame Source="{Binding XPath=link}"/>
</DockPanel>
⭐ Key Takeaways
The most critical concepts from this lecture are: (1) HierarchicalDataTemplate enables displaying XML hierarchies in TreeViews by specifying children through ItemsSource with XPath binding and using DataType as the template key; (2) ObjectDataProvider allows declarative instantiation of .NET objects with constructor parameters and method binding, plus async support through IsAsynchronous property; (3) Binding modes (OneWay, TwoWay, OneWayToSource, OneTime) control data flow direction, while UpdateSourceTrigger (PropertyChanged, LostFocus, Explicit) controls when the source is updated; (4) Validation can be implemented through custom ValidationRule classes or built-in ExceptionValidationRule/DataErrorValidationRule, with automatic error adorner display on validation failure; (5) A complete RSS reader can be built without code-behind using XmlDataProvider, master-detail binding with IsSynchronizedWithCurrentItem, and BindsDirectlyToSource for text box feed URL editing.
🧠 Quick Revision Questions
- What is the difference between HierarchicalDataTemplate and regular DataTemplate, and how does the DataType attribute work with XML nodes?
- List the four binding modes and explain the data flow direction for each, including which mode requires a ConvertBack method.
- What are the three values of UpdateSourceTrigger, and when does each update the source in TwoWay binding?
- How do you create a custom validation rule, and what happens visually when validation fails in a bound TextBox?
- Explain how the RSS reader example achieves master-detail binding between the ListBox and Frame without any code-behind.
📘 Lecture 32 — Concurrency and Threading
📖 Overview: This lecture introduces the concept of concurrency and threading in C#. It explains how to create and manage threads, handle shared and local state, ensure thread safety using locks, pass data to threads, and signal between threads. Understanding threading is crucial for building responsive and efficient applications.
🗂️ Topics Covered
The lecture covers the definition of concurrency and its comparison with events, the concept of threads and multithreading, creating and starting threads with the Thread class, thread life cycle methods like Join and Sleep, local versus shared state, thread safety and the lock statement, passing data to threads using lambda expressions and ParameterizedThreadStart, the issue of captured variables in loops, exception handling in threads, and signaling between threads using ManualResetEvent.
📝 Lecture Summary
Concurrency and Threading Basics
Concurrency means more than one thing happening at the same time. It is needed for responsive user interfaces, simultaneous requests, and parallel programming. An event is a notification that something happened in the same thread, whereas concurrency uses separate threads. A thread is an execution path that can proceed independently of others. Normal processes (programs in execution) have one thread. Multithreaded programs have more than one thread and can share data.
🔑 Definition — Thread: An execution path that can proceed independently of others within a process.
Creating a Thread
To create a thread, instantiate a Thread object and pass a method to its constructor, then call Start(). The main thread and the new thread run concurrently.
📌 Example:
class threadtest {
static void Main() {
Thread t = new Thread(writey);
t.Start();
for (int i = 0; i < 1000; i++) Console.Write("x");
}
static void writey() {
for (int i = 0; i < 1000; i++) Console.Write("y");
}
}
Typical Output: xxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxx
Output is interleaved due to time slicing (the OS gives each thread small time slices). On multicore, real parallelism occurs, but repeated blocks appear due to how Console handles output.
Thread Properties and Methods
IsAlive:trueonce the thread starts and until it ends.Name: A property to name a thread for debugging.Thread.CurrentThread: Returns the currently executing thread.Join(): Blocks the calling thread until the target thread finishes.Sleep(): Blocks the current thread for a specified time.
🔑 Definition — Blocked: A thread is blocked when it is waiting (e.g., via Join, Sleep). Use the ThreadState property to check.
📌 Example of Join:
static void Main() {
Thread t = new Thread(Go);
t.Start();
t.Join(); // Main waits for t to finish
Console.WriteLine("Thread t has ended!");
}
static void Go() { for (int i = 0; i < 1000; i++) Console.Write("y"); }
📌 Example of Sleep:
Thread.Sleep(TimeSpan.FromHours(1)); // Sleep for 1 hour
Thread.Sleep(500); // Sleep for 500 milliseconds
Local vs Shared State
Each thread has its own local state (method-level variables). When multiple threads access shared state (e.g., instance or static fields), issues arise.
📌 Local State Example:
static void Main() {
new Thread(Go).Start();
Go();
}
static void Go() {
for (int cycles = 0; cycles < 5; cycles++) Console.Write('?');
}
// Output: '??????????' (each thread has its own 'cycles')
📌 Shared State Example (instance field):
class threadtest {
bool _done;
static void Main() {
threadtest tt = new threadtest();
new Thread(tt.Go).Start();
tt.Go();
}
void Go() {
if (!_done) { _done = true; Console.WriteLine("Done"); }
}
}
Both threads can read _done as false and both may write "Done". This is a race condition.
📌 Shared State Example (static field):
class threadtest {
static bool _done;
static void Main() {
new Thread(Go).Start();
Go();
}
static void Go() {
if (!_done) { _done = true; Console.WriteLine("Done"); }
}
}
Same problem — can output "Done" twice.
Thread Safety with lock
To prevent race conditions, use the lock statement. It ensures that only one thread can execute the critical section at a time.
🔑 Definition — Thread Safety: Code that works correctly when accessed by multiple threads simultaneously.
📌 Example:
class threadsafe {
static bool _done;
static readonly object _locker = new object();
static void Main() {
new Thread(Go).Start();
Go();
}
static void Go() {
lock (_locker) {
if (!_done) { Console.WriteLine("Done"); _done = true; }
}
}
}
Now "Done" is output exactly once because the lock ensures mutual exclusion.
Passing Data to Threads
Two ways to pass data: using a lambda expression (easiest) or using ParameterizedThreadStart.
🔑 Definition — Lambda Expression: An anonymous function that can contain expressions and statements.
📌 Using Lambda:
static void Main() {
Thread t = new Thread(() => Print("Hello from t!"));
t.Start();
}
static void Print(string message) { Console.WriteLine(message); }
📌 Lambda with multiple statements:
new Thread(() => {
Console.WriteLine("I'm running on another thread!");
Console.WriteLine("This is so easy!");
}).Start();
📌 Using ParameterizedThreadStart:
static void Main() {
Thread t = new Thread(Print);
t.Start("Hello from t!");
}
static void Print(object messageObj) {
string message = (string)messageObj;
Console.WriteLine(message);
}
🔑 Definition — Delegates:
ThreadStart: Delegate for methods that take no arguments.ParameterizedThreadStart: Delegate for methods that take oneobjectargument.
Lambda Expressions and Captured Variables
Capturing a loop variable in a lambda causes all threads to share the same variable.
📌 Problem Example:
for (int i = 0; i < 10; i++)
new Thread(() => Console.Write(i)).Start();
// Typical Output: 0223557799 (unpredictable)
📌 Solution — Use a temporary variable:
for (int i = 0; i < 10; i++) {
int temp = i;
new Thread(() => Console.Write(temp)).Start();
}
// Output: 0123456789 (more predictable)
Exception Handling in Threads
An exception thrown in a thread cannot be caught in the calling thread. Each thread must handle its own exceptions.
📌 Wrong Way:
public static void Main() {
try {
new Thread(Go).Start();
}
catch (Exception ex) {
Console.WriteLine("Exception!"); // This never runs
}
}
static void Go() { throw null; }
📌 Correct Way:
public static void Main() {
new Thread(Go).Start();
}
static void Go() {
try {
// ...
throw null;
// ...
}
catch (Exception ex) {
// Handle exception within the thread
}
}
Thread Properties: Background and Priority
IsBackground: Iftrue, the thread does not keep the application alive. The process exits when all foreground threads end.Priority: Determines how much CPU time the thread gets relative to others.
Signaling Between Threads with ManualResetEvent
A ManualResetEvent is the simplest signaling mechanism.
🔑 Definition — ManualResetEvent: A synchronization primitive that allows one thread to signal another.
🔑 Definition — WaitOne(): Blocks the current thread until the signal is set. 🔑 Definition — Set(): Sends the signal, unblocking the waiting thread. 🔑 Definition — Reset(): Closes the signal (makes it unsignaled again).
📌 Example:
var signal = new ManualResetEvent(false);
new Thread(() => {
Console.WriteLine("Waiting for signal...");
signal.WaitOne();
signal.Dispose();
Console.WriteLine("Got signal!");
}).Start();
Thread.Sleep(2000);
signal.Set();
The child thread waits until the main thread calls signal.Set() after 2 seconds.
💡 Why this matters: ManualResetEvent provides a simple way to coordinate threads without busy waiting.
⭐ Key Takeaways
Threading allows multiple execution paths within a single process, enabling responsive UIs and parallel processing. Use lock with a dedicated object to ensure thread safety when accessing shared state. Always handle exceptions inside the thread body, as they cannot propagate to the creating thread. Pass data to threads most easily via lambda expressions, and be careful with captured loop variables by using a temporary copy. Use ManualResetEvent with WaitOne() and Set() for simple thread signaling.
🧠 Quick Revision Questions
- What is the difference between concurrency and events?
- How do you create and start a thread in C#?
- What does the
lockstatement do and why is it important for thread safety? - Why can't exceptions thrown in a thread be caught in the calling thread?
- What is the purpose of
ManualResetEventand how doWaitOne()andSet()work?
📘 Lecture 33 — Long Running Operations and Threading
📖 Overview: This lecture addresses the problem of UI unresponsiveness caused by long-running operations, which block the main UI thread. It introduces worker threads, thread marshaling via Dispatcher and SynchronizationContext, thread pools, and the Task-based asynchronous pattern including continuations and TaskCompletionSource.
🗂️ Topics Covered
The lecture covers UI thread marshaling using Dispatcher.BeginInvoke and Invoke, SynchronizationContext for generalized marshaling, ThreadPool for efficient thread reuse, Tasks as a higher-level abstraction over threads with return values and exception handling, continuations for chaining operations, and TaskCompletionSource for creating tasks that are manually driven.
📝 Lecture Summary
Long Running Operations and UI Responsiveness
Long running operations make applications unresponsive because the main thread is used for rendering the UI and responding to events. The solution is to start a worker thread and update the UI when finished. However, UI updation is usually possible only on the UI thread, so requests must be forwarded (marshaled) to the UI thread.
🔑 Definition — Thread Marshaling: The process of forwarding a request from a worker thread to the UI thread so that UI elements can be safely updated.
The low-level way to marshal is to call BeginInvoke or Invoke on the element's Dispatcher object. Invoke takes a delegate, queues it on the UI thread, but blocks until completion, allowing return values. BeginInvoke does the same without blocking, making it better when no return value is needed.
📌 Example: Using Dispatcher.BeginInvoke
Partial class mywindow : Window
{
Public mywindow()
{
Initializecomponent();
New Thread(Work).Start();
}
Void Work()
{
Thread.Sleep(5000);
Updatemessage("The answer");
}
Void updatemessage(string message)
{
Action action = () => txtmessage.Text = message;
Dispatcher.begininvoke(action);
}
}
SynchronizationContext
SynchronizationContext can be used for generalized thread marshaling, providing a more portable abstraction.
📌 Example: Using SynchronizationContext
Partial class mywindow : Window
{
Synchronizationcontext _uisynccontext;
Public mywindow()
{
Initializecomponent();
_uisynccontext = synchronizationcontext.Current;
New Thread(Work).Start();
}
Void Work()
{
Thread.Sleep(5000);
Updatemessage("The answer");
}
Void updatemessage(string message)
{
_uisynccontext.Post(_ => txtmessage.Text = message);
}
}
ThreadPool
The ThreadPool saves the time of thread creation. ThreadPool threads cannot be named and are difficult to debug. They are always background threads. Blocking can degrade performance. The Thread.CurrentThread.IsThreadPoolThread property checks if a thread is from the pool. ThreadPool creates or reduces real threads using a hill climbing algorithm to maximize CPU usage and reduce context switching.
📌 Example: Using ThreadPool
Threadpool.queueuserworkitem (notused => Console.writeline ("Hello"));
Task.Run (() => Console.writeline ("Hello from the thread pool"));
Tasks
There is no easy way to get a return value from a thread; you can join and use shared data. Exceptions are also difficult to handle. You cannot tell a thread to start something else when finished. Tasks provide a higher-level abstraction. Tasks can be chained using continuations, can use the ThreadPool, and with TaskCompletionSource enable a callback approach.
Starting a task is like creating a thread, except tasks are started right away (hot) and run in a thread pool. Task.Wait() is like Join. The generic subclass Task<TResult> allows returning values, and Task.Result blocks until the result is available.
📌 Example: Starting and waiting on a Task
Task.Run (() => Console.writeline ("Foo"));
New Thread (() => Console.writeline ("Foo")).Start();
Task task = Task.Run (() =>
{
Thread.Sleep (2000);
Console.writeline ("Foo");
});
Console.writeline (task.iscompleted);
Task.Wait();
📌 Example: Task with return value
Task<int> task = Task.Run(() => { Console.writeline("Foo"); return 3; });
// ...
Int result = task.Result;
Console.writeline (result);
Tasks propagate exceptions to whoever calls Wait() or accesses Result. Exceptions are wrapped in an AggregateException.
📌 Example: Task exception handling
Task task = Task.Run (() => { throw null; });
Try
{
Task.Wait();
}
Catch (aggregateexception aex)
{
If (aex.innerexception is nullreferenceexception)
Console.writeline ("Null!");
Else
Throw;
}
Continuations
Continuations allow you to specify what happens when a task finishes. There are two ways to attach continuations.
📐 Formula: task.continuewith(antecedent => { ... }) → Runs the delegate when the antecedent task completes.
📐 Formula: var awaiter = task.getawaiter(); awaiter.oncompleted(() => { ... }) → Another way to attach a continuation using the awaiter pattern.
📌 Example: Continuation using ContinueWith
Task<int> primenumbertask = Task.Run (() =>
Enumerable.Range (2, 3000000).Count (n =>
Enumerable.Range (2, (int)Math.Sqrt(n)-1).All (i => n % i > 0)));
Primenumbertask.continuewith (antecedent =>
{
Int result = antecedent.Result;
Console.writeline (result);
});
📌 Example: Continuation using awaiter
Var awaiter = primenumbertask.getawaiter();
Awaiter.oncompleted(() =>
{
Int result = awaiter.getresult();
Console.writeline (result);
});
If a synchronization context is present, the continuation runs on the UI thread.
TaskCompletionSource
TaskCompletionSource represents any operation that starts and finishes some time later. It creates a "slave" task that you manually drive and mark as finished. It is ideal for I/O-bound work. It provides all benefits of tasks (returns, exceptions, continuations) without blocking a thread. Create a task which you can wait on and attach continuations, controlled by these operations:
🔑 Definition — TaskCompletionSource<TResult>: A class that enables creation of a Task<TResult> that can be manually signaled as completed, faulted, or canceled.
📐 Formula: tcs.SetResult(result), tcs.SetException(exception), tcs.SetCanceled() → Methods that signal the task. Call exactly once.
💡 Why this matters: TaskCompletionSource allows you to create tasks for I/O-bound or timer-based operations without dedicating a thread, improving scalability.
📌 Example: Basic TaskCompletionSource with thread
Var tcs = new taskcompletionsource<int>();
New Thread (() => {
Thread.Sleep (5000); tcs.setresult (42);
}).Start();
Task<int> task = tcs.Task;
Console.writeline (task.Result) // prints 42 after 5s
📌 Example: Creating a custom Run method with TaskCompletionSource
Task<tresult> Run<tresult> (Func<tresult> function)
{
Var tcs = new taskcompletionsource <tresult>();
New Thread (() =>
{
Try { tcs.setresult (function()); }
Catch (Exception ex) { tcs.setexception (ex);}
}).Start();
Return tcs.Task;
}
// Usage
Task<int> task = Run (() => { Thread.Sleep (5000); return 42; });
📌 Example: Timer-based TaskCompletionSource (no blocking thread)
Task<int> getanswertolife()
{
Var tcs = new taskcompletionsource<int>();
Var timer = new System.Timers.Timer(5000) { autoreset = false };
Timer.Elapsed += delegate { timer.Dispose(); tcs.setresult(42); };
Timer.Start();
Return tcs.Task;
}
// Using with continuation
Var awaiter = getanswertolife().getawaiter();
Awaiter.oncompleted (() => Console.writeline (awaiter.getresult()));
📌 Example: Implementing Task.Delay with TaskCompletionSource
Task Delay(int milliseconds)
{
Var tcs = new taskcompletionsource<object>();
Var timer = new System.Timers.Timer(milliseconds) { autoreset = false };
Timer.Elapsed += delegate { timer.Dispose(); tcs.setresult(null); };
Timer.Start();
Return tcs.Task;
}
// Equivalent to Task.Delay
Delay(5000).getawaiter().oncompleted (() => Console.writeline (42));
Delay(5000).continuewith (ant => Console.writeline (42));
Task.Delay(5000).getawaiter().oncompleted (() => Console.writeline (42));
Task.Delay(5000).continuewith (ant => Console.writeline (42));
⭐ Key Takeaways
For the exam, remember that UI updates must occur on the UI thread using Dispatcher.BeginInvoke/Invoke or SynchronizationContext.Post. Tasks are a higher-level abstraction than threads, providing return values via Task.Result, exception handling via AggregateException, and continuations via ContinueWith or awaiter.OnCompleted. TaskCompletionSource enables creating manually-driven tasks ideal for I/O-bound operations without blocking a thread. Task.Delay is a non-blocking alternative to Thread.Sleep that works with continuations.
🧠 Quick Revision Questions
- What is the difference between Dispatcher.Invoke and Dispatcher.BeginInvoke in terms of blocking behavior?
- How does SynchronizationContext help with thread marshaling in a portable way?
- What problem does the Task class solve compared to raw threads?
- How are exceptions propagated in Tasks, and what wrapper exception type is used?
- What is TaskCompletionSource and why is it ideal for I/O-bound operations?
📘 Lecture 34 — Async and Await in C#
📖 Overview: This lecture continues the discussion on asynchronous programming in C#, focusing on the
asyncandawaitkeywords introduced in C# 5.0. It explains how these keywords simplify writing asynchronous code by eliminating the complex plumbing required with tasks, continuations, and state machines, and demonstrates their application in both console and UI applications.
🗂️ Topics Covered
The lecture covers making CPU-bound tasks asynchronous using Task.Run, the complexity of manual continuation-based async patterns, the introduction of C# 5.0's async and await keywords to simplify async code, the expansion of await into continuations by the compiler, capturing local state in async methods, and applying async patterns to UI applications to maintain responsiveness. It also includes examples of downloading web pages asynchronously and understanding the UI message loop's role in continuations.
📝 Lecture Summary
Creating CPU-Bound Async Tasks with Task.Run
The lecture begins with synchronous code that counts primes across ten million-number ranges. The synchronous GetPrimesCount method uses ParallelEnumerable.Range within Count to find primes between intervals. The DisplayPrimeCounts method loops through ten intervals, calling GetPrimesCount and printing results sequentially. The output shows prime counts for each million-number range from 0–999999 up to 9000000–9999999, ending with "Done!".
To make this asynchronous in a coarse-grained way, we wrap the entire synchronous DisplayPrimeCounts call in Task.Run. A more granular approach creates an async method GetPrimesCountAsync that returns Task<int>. This method runs the prime counting computation on a thread pool thread using Task.Run. When calling it, we get the awaiter from the returned task, then attach a continuation using Awaiter.OnCompleted to print the result when the task completes. However, this approach falls back to sequential behavior: the continuation calls the next iteration inside itself, leading to complex recursive structure.
Task<int> GetPrimesCountAsync(int start, int count)
{
return Task.Run(() =>
ParallelEnumerable.Range(start, count).Count(n =>
Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0)));
}
🔑 Definition — Awaiter: An object obtained from a task (via GetAwaiter()) that provides OnCompleted for attaching continuations and GetResult() to retrieve the task's result or propagate exceptions.
📐 Pattern: var awaiter = task.GetAwaiter(); awaiter.OnCompleted(() => { /* continuation */ }); → Attaches code to run after the task completes without blocking the calling thread.
Manual Async State Machine Pattern
To make DisplayPrimeCounts itself async while keeping sequential output, a manual state machine approach is needed. A method DisplayPrimeCountsFrom takes an index i, awaits the prime count for that interval, and in the continuation prints the result and either recursively calls itself with i+1 (if more intervals remain) or prints "Done". This pattern becomes verbose and error-prone.
To encapsulate this, the lecture introduces a PrimesStateMachine class using a TaskCompletionSource<object>. The class exposes a public Task property. The DisplayPrimeCountsFrom method follows the same recursive continuation pattern, but when all intervals are processed (i >= 10), it calls _tcs.SetResult(null) to signal completion. This manual approach is complex and difficult to maintain.
class PrimesStateMachine
{
TaskCompletionSource<object> _tcs = new TaskCompletionSource<object>();
public Task Task { get { return _tcs.Task; } }
public void DisplayPrimeCountsFrom(int i)
{
var awaiter = GetPrimesCountAsync(i * 1000000 + 2, 1000000).GetAwaiter();
awaiter.OnCompleted(() =>
{
Console.WriteLine(awaiter.GetResult());
if (++i < 10) DisplayPrimeCountsFrom(i);
else { Console.WriteLine("Done"); _tcs.SetResult(null); }
});
}
}
🔑 Definition — TaskCompletionSource<T>: A class that lets you create a Task<T> and manually control its completion by calling SetResult, SetException, or SetCanceled. It's used when implementing custom async patterns or state machines.
💡 Why this matters: Manual state machines are tedious to write correctly. C# 5.0's async/await automates this entire pattern, generating the state machine code automatically.
C# 5.0 Async and Await Keywords
C# 5.0 introduces the async and await keywords, which eliminate all the manual plumbing for asynchronous code. The await keyword simplifies attaching continuations, handling synchronous completion (when the task is already completed), and other details. The compiler automatically transforms await expressions into the equivalent awaiter-and-continuation code.
The expansion of await works as follows:
var result = await expression;
statement(s);
Expands to:
var awaiter = expression.GetAwaiter();
awaiter.OnCompleted(() =>
{
var result = awaiter.GetResult();
statement(s);
});
The async keyword can be applied to methods returning void, Task, or Task<TResult>. When a method is marked async and hits an await, control returns to the caller (the method is non-blocking). A continuation is attached to the awaited task. When the task completes, execution resumes: if an exception occurred, it's rethrown; otherwise the return value is assigned to the await expression.
async Task DisplayPrimeCounts()
{
for (int i = 0; i < 10; i++)
Console.WriteLine(await GetPrimesCountAsync(i * 1000000 + 2, 1000000) +
" primes between " + (i * 1000000) + " and " + ((i + 1) * 1000000 - 1));
Console.WriteLine("Done!");
}
🔑 Definition — async keyword: A modifier applied to method declarations that tells the compiler to treat await as a special keyword, transforming the method into a state machine that can suspend and resume execution.
🔑 Definition — await keyword: An operator that asynchronously waits for a task to complete. It suspends the execution of the current method until the awaited task finishes, returning control to the caller in the meantime. After the task completes, execution resumes from where it left off.
Capturing Local State with Await
A key power of await is its ability to capture local state. When execution resumes in the continuation after an await, local variables retain their values from before the await. The compiler translates async methods into state machines that automatically preserve this state across suspension points. If there's a sync context (e.g., on a UI thread), the continuation runs on the same thread; otherwise it runs on any available thread pool thread.
💡 Why this matters: Without async/await, preserving local state across asynchronous operations requires complex manual state management (like the PrimesStateMachine class). The compiler handles this automatically, making async code as simple as synchronous code.
Async in UI Applications
The lecture demonstrates applying async/await to a WPF UI application to keep it responsive during CPU-bound work. The synchronous version has a Go() method that iterates through ranges, calls the synchronous GetPrimesCount, and updates the _results TextBlock. This blocks the UI thread, making the application unresponsive.
The asynchronous version uses GetPrimesCountAsync (which runs the computation on a thread pool via Task.Run) and await in the Go() method. The Go() method is marked async void (appropriate for event handlers). Before the loop, the Go button is disabled (_button.IsEnabled = false). Inside the loop, each await releases the UI thread, allowing it to process other events. After the loop completes, the button is re-enabled. This simple change keeps the UI responsive while the prime calculations run in parallel.
async void Go()
{
_button.IsEnabled = false;
for (int i = 1; i < 5; i++)
_results.Text += await GetPrimesCountAsync(i * 1000000, 1000000) +
" primes between " + (i * 1000000) + " and " + ((i + 1) * 1000000 - 1) +
Environment.NewLine;
_button.IsEnabled = true;
}
An I/O-bound example downloads web pages asynchronously using WebClient.DownloadDataTaskAsync, which returns Task<byte[]>. The Go() method iterates through URLs, awaits each download, updates the results TextBlock with page lengths, catches any WebException, and ensures the button is re-enabled in a finally block. Despite the async nature, exception handling and finalization work exactly as they would in synchronous code.
async void Go()
{
_button.IsEnabled = false;
string[] urls = "www.albahari.com www.oreilly.com www.linqpad.net".Split();
int totalLength = 0;
try
{
foreach (string url in urls)
{
var uri = new Uri("http://" + url);
byte[] data = await new WebClient().DownloadDataTaskAsync(uri);
_results.Text += "Length of " + url + " is " + data.Length + Environment.NewLine;
totalLength += data.Length;
}
_results.Text += "Total length: " + totalLength;
}
catch (WebException ex)
{
_results.Text += "Error: " + ex.Message;
}
finally { _button.IsEnabled = true; }
}
Understanding the UI Message Loop
The lecture explains what happens "underneath" the UI message loop. The UI thread runs a message loop: while (!ThisApplication.Ended) { /* wait for message; dispatch */ }. When an event handler (like click) fires, Go() runs until it hits await, at which point control returns to the message loop. The compiler's expansion of await ensures a continuation is set up. Because the await occurs on a UI thread, the continuation is posted on the sync context, which ensures it runs via the message loop. This means Go() runs pseudo-concurrently with the UI thread — true concurrency happens only while DownloadDataTaskAsync (or similar async operations) are executing on background threads.
The alternative coarse-grained approach uses Task.Run(() => Go()) to run the entire loop on a thread pool thread, with Dispatcher.BeginInvoke to marshal UI updates back to the UI thread. This is more complex and difficult when progress reporting is needed.
_button.Click += (sender, args) =>
{
_button.IsEnabled = false;
Task.Run(() => Go());
};
void Go()
{
for (int i = 1; i < 5; i++)
{
int result = GetPrimesCount(i * 1000000, 1000000);
Dispatcher.BeginInvoke(new Action(() =>
_results.Text += result + " primes between " + (i * 1000000) +
" and " + ((i + 1) * 1000000 - 1) + Environment.NewLine));
}
Dispatcher.BeginInvoke(new Action(() => _button.IsEnabled = true));
}
🔑 Definition — Synchronization Context (Sync Context): An abstraction that represents a "context" for executing code, typically associated with a specific thread (like the UI thread). The awaiter captures the sync context at the point of await and posts the continuation to it, ensuring UI updates happen on the correct thread.
⭐ Key Takeaways
The critical points to remember from this lecture are: C# 5.0's async and await keywords dramatically simplify asynchronous programming by letting you write code that looks synchronous but behaves asynchronously — the compiler automatically generates the state machine that handles continuations, exception propagation, and local state preservation. await releases the calling thread while the awaited operation runs, then resumes execution on the captured sync context (typically the UI thread for UI applications). For CPU-bound work, use Task.Run inside an async method to offload computation to the thread pool, then await the result. For I/O-bound work, use built-in async methods like WebClient.DownloadDataTaskAsync. Mark event handlers with async void (not async Task) since they must return void, but all other async methods should return Task or Task<T>. The compiler's expansion of await mirrors the manual awaiter/continuation pattern but handles all edge cases automatically, making async code as maintainable as synchronous code.
🧠 Quick Revision Questions
-
What is the meaning of the
awaitkeyword in C# 5.0, and what does the compiler expand it into? -
Why must event handlers in UI applications be marked
async voidrather thanasync Task? -
What is the role of the synchronization context in async/await for UI applications?
-
How does the
TaskCompletionSource<T>class relate to the state machine generated by the compiler for async methods? -
In the manual async pattern using continuations, why does the
DisplayPrimeCountsFrommethod call itself recursively inside theOnCompletedcallback, and how doesasync/awaiteliminate this complexity?
📘 Lecture 35 — Async Programming Patterns and Task Combinators
📖 Overview: This lecture covers advanced async programming patterns in C#, including returning Task from async methods, parallel execution of async operations, cancellation patterns, progress reporting, and the Task-based Async Pattern (TAP). It explains how the compiler transforms async methods internally using TaskCompletionSource and demonstrates the equivalence between async and synchronous code structures.
🗂️ Topics Covered
The lecture covers returning Task from async void functions, the internal implementation using TaskCompletionSource, comparing async and synchronous code structures, parallel execution with Task.WhenAny, async lambda expressions, cancellation using CancellationToken, progress reporting with IProgress<T>, Task-based Async Pattern (TAP) methods, and task combinators for handling multiple async operations.
📝 Lecture Summary
Async Method Return Types and Internal Implementation
Async void methods can return Task without explicitly returning it, enabling async call chains. The compiler internally uses TaskCompletionSource to implement methods returning Tasks. When returning Task<TResult>, the internal TCS is signaled with the result value. The compiler transforms the async method into a state machine that creates a TaskCompletionSource, wires up continuations on await points, and returns the TCS.Task to the caller.
🔑 Definition — TaskCompletionSource (TCS): A type that creates a Task which can be manually controlled by calling SetResult, SetException, or SetCanceled methods.
📐 Formula: Task<TResult> async method → internally creates TCS, signals with tcs.SetResult(value) on completion
📌 Example: The compiler transforms async Task printanswertolife() into code that creates a TaskCompletionSource<object>, calls Task.Delay(5000).GetAwaiter().OnCompleted() to set a continuation, and in that continuation calls tcs.SetResult(null) after computing the answer.
Async vs Synchronous Code Comparison
The lecture demonstrates that async/await preserves the same programming model as synchronous code. The three steps are: write synchronous code, add async and await keywords, and change return types to Task and Task<TResult> so methods become awaitable. This means developers only need Task.Run for CPU-bound parallel tasks and TaskCompletionSource for I/O-bound operations; the compiler handles the rest.
💡 Why this matters: This intentional design makes async code as easy to write and reason about as synchronous code, while maintaining non-blocking behavior.
📌 Example: The async call chain shows:
async Task Go()
{
await printanswertolife();
Console.WriteLine("Done");
}
async Task printanswertolife()
{
int answer = await getanswertolife();
Console.WriteLine(answer);
}
async Task<int> getanswertolife()
{
await Task.Delay(5000);
return 21 * 2;
}
This mirrors the synchronous version where each method calls the next directly, but with non-blocking await.
Parallelism with Async
To run two async tasks in parallel, don't await each immediately. Store the tasks in variables, then await both. This provides true concurrency at the bottom-level operations, though if there's a synchronization context, only pseudo-concurrency is achieved (switching only on await). This means you can increment a shared variable without locking, but you can't assume the same value before and after await.
🔑 Definition — Pseudo-concurrency: When async operations on a UI thread only yield control at await points, running continuations on the same thread rather than truly parallel threads.
📌 Example:
var task1 = printanswertolife();
var task2 = printanswertolife();
await task1;
await task2;
Both tasks start executing immediately, and the code awaits them both later.
Async Lambda Expressions
Async lambdas can be created for event handlers, delegates, and inline methods. Event handlers must return void, so they use async void. For other scenarios, async lambdas can return Task or Task<TResult>.
🔑 Definition — Async lambda: An anonymous function that uses the async modifier, enabling await inside the lambda body.
📌 Example: Button click handler with async lambda:
myButton.Click += async(sender, args) =>
{
await Task.Delay(1000);
myButton.Content = "Done";
};
This is equivalent to: async void buttonHandler(object sender, EventArgs args)
Cancellation
Cancellation is implemented using CancellationTokenSource and CancellationToken. Most async methods have built-in cancellation support. The CancellationToken is polled by calling ThrowIfCancellationRequested() which throws OperationCanceledException if cancellation was requested. Many async methods accept CancellationToken directly, like Task.Delay(1000, cancellationToken).
🔑 Definition — CancellationToken: A struct that propagates notification that operations should be canceled. It has an IsCancellationRequested property and ThrowIfCancellationRequested() method.
📌 Example: Creating cancellation with timeout:
var cancelSource = new CancellationTokenSource(5000);
try { await Foo(cancelSource.Token); }
catch (OperationCanceledException ex)
{ Console.WriteLine("Cancelled"); }
This automatically cancels the operation after 5 seconds.
Progress Reporting
Progress reporting is implemented using the IProgress<T> interface and Progress<T> class. The Progress<T> class captures the SynchronizationContext at creation time, ensuring that progress callbacks run on the correct thread (e.g., UI thread). The IProgress<T> interface has a single method Report(T value).
💡 Why this matters: Progress<T> solves thread safety issues by marshaling progress reports back to the original synchronization context, making UI updates safe.
🔑 Definition — IProgress<T>: An interface with a Report(T value) method that enables progress reporting from async operations.
📌 Example:
var progress = new Progress<int>(i => Console.WriteLine(i + " %"));
await Foo(progress);
async Task Foo(IProgress<int> onProgressPercentChanged)
{
await Task.Run(() =>
{
for (int i = 0; i < 1000; i++)
{
if (i % 10 == 0)
onProgressPercentChanged.Report(i / 10);
}
});
}
Task-based Async Pattern (TAP)
TAP defines the standard pattern for async methods in .NET. A TAP method: returns a "hot" (already started) Task or Task<TResult>, has an "Async" suffix (except for combinators), accepts CancellationToken and/or IProgress<T> if supporting cancellation/progress, returns quickly with minimal synchronous phase, and doesn't tie up a thread if I/O-bound.
📌 Example: TAP method signature: public Task<string> DownloadStringAsync(Uri address, CancellationToken cancellationToken, IProgress<int> progress)
Task Combinators
Task combinators enable working with multiple tasks. Task.WhenAny returns the first task to complete from a collection of tasks. Task.WhenAll waits for all tasks to complete. WhenAny is useful for timeouts and race conditions.
🔑 Definition — Task.WhenAny: A combinator that returns a Task that completes when any of the supplied tasks complete.
📌 Example: Using WhenAny for timeout:
Task<string> task = someAsyncFunc();
Task winner = await Task.WhenAny(task, Task.Delay(5000));
if (winner != task) throw new TimeoutException();
string result = await task;
⭐ Key Takeaways
Async methods can return Task or Task<TResult> which the compiler implements using TaskCompletionSource, enabling async call chains. The async/await pattern intentionally mirrors synchronous code structure, making it easy to convert between the two styles. For parallelism, start multiple tasks without awaiting until all are collected, enabling true concurrent execution of I/O operations. Cancellation uses CancellationTokenSource/CancellationToken with ThrowIfCancellationRequested(), and many built-in methods accept cancellation tokens directly. The Task-based Async Pattern (TAP) standardizes async methods with "Async" suffix, hot task return, and optional cancellation/progress support, while Task combinators like WhenAny enable sophisticated patterns like timeouts and race conditions.
🧠 Quick Revision Questions
- How does the compiler internally implement an async method that returns Task<T>?
- What is the difference between pseudo-concurrency and true concurrency in async operations?
- How do you properly run two async tasks in parallel and wait for both to complete?
- What is the purpose of the Progress<T> class and how does it handle thread marshaling?
- How can you implement a timeout for an async operation using Task.WhenAny and Task.Delay?
📘 Lecture 36 — Task Combinators, Parallel Loops, and Concurrent Collections
📖 Overview: This lecture covers advanced asynchronous programming patterns using task combinators like
WhenAnyandWhenAll, introduces the Task Parallel Library (TPL) for data parallelism, and explains concurrent collections for thread-safe operations. Understanding these concepts is crucial for building efficient, responsive applications that leverage multicore processors.
🗂️ Topics Covered
Task combinators (WhenAny and WhenAll) for coordinating multiple asynchronous operations, implementing timeouts with task combinators, error handling with aggregation, parallel loops (Parallel.For, Parallel.ForEach) with loop states and local values, concurrent collections (ConcurrentBag, ConcurrentStack, ConcurrentQueue, ConcurrentDictionary), BlockingCollection for producer-consumer patterns, and building a producer-consumer queue implementation.
📝 Lecture Summary
Task Combinators: WhenAny and WhenAll
Task combinators allow coordination of multiple asynchronous tasks. Task.WhenAny completes when any of the provided tasks completes, returning the first completed task. Task.WhenAll waits for all tasks to complete and aggregates exceptions. The difference from sequential awaiting (await task1; await task2; await task3;) is that WhenAll runs tasks concurrently. If one task faults with WhenAll, you still get an aggregate exception containing all errors.
🔑 Definition — Task.WhenAny: A combinator that returns a task that completes when any of the supplied tasks completes, returning that task.
📐 Usage: Task winner = await Task.WhenAny(someoperation, Task.Delay(5000)); → Wait for either operation to complete or timeout
📌 Example: Implementing timeout:
Task<string> task = SomeAsyncFunc();
Task winner = await Task.WhenAny(task, Task.Delay(5000));
if (winner != task) throw new TimeoutException();
string result = await task;
🔑 Definition — **Task.WhenAll**: A combinator that waits for all supplied tasks to complete and returns their results as an array.
📐 Usage: `int[] results = await Task.WhenAll(task1, task2);` → Returns array of results in same order
📌 Example: Downloading multiple files:
```csharp
async Task<int> GetTotalSize(string[] uris)
{
IEnumerable<Task<int>> downloadTasks = uris.Select(async uri =>
(await new WebClient().DownloadDataTaskAsync(uri)).Length);
int[] contentLengths = await Task.WhenAll(downloadTasks);
return contentLengths.Sum();
}
🔑 Definition — AggregateException: When tasks fault in WhenAll, exceptions are wrapped in an AggregateException with an InnerExceptions collection.
📐 Example: Task all = Task.WhenAll(task1, task2); try { await all; } catch { Console.WriteLine(all.Exception.InnerExceptions.Count); } → Shows count of all exceptions
💡 Why this matters: Using WhenAll instead of sequential awaits improves performance by running independent operations concurrently.
Task Parallel Library (TPL) and Parallel Loops
TPL exploits multicore processors for real parallel tasks through three steps: partition work into small chunks, process them in parallel, and collate results in a thread-safe manner. Data parallelism is easier than task parallelism and scales well because the same code starts and ends parallelism in one place. The Parallel class provides three static methods: Parallel.Invoke, Parallel.For, and Parallel.ForEach.
🔑 Definition — Parallel.Invoke: Executes multiple actions in parallel, batching them efficiently for processors.
📐 Usage: public static void Invoke(params Action[] actions);
📌 Example:
Parallel.Invoke(
() => new WebClient().DownloadFile("http://www.linqpad.net", "lp.html"),
() => new WebClient().DownloadFile("http://www.jaoo.dk", "jaoo.html"));
🔑 Definition — Parallel.For: Parallel equivalent of a for loop that partitions iterations across processors.
📐 Usage: public static ParallelLoopResult For(int fromInclusive, int toExclusive, Action<int> body);
📌 Example:
Parallel.For(0, 100, i => Foo(i)); // Parallel version of for (int i=0; i<100; i++) Foo(i);
🔑 Definition — Parallel.ForEach: Parallel equivalent of a foreach loop.
📐 Usage: public static ParallelLoopResult ForEach<TSource>(IEnumerable<TSource> source, Action<TSource> body);
📌 Example:
Parallel.ForEach("Hello, world", c => Console.WriteLine(c.ToString() + c));
Loop Counters, Breaking Out, and Per-Thread Counters
The ParallelLoopState class provides Break() and Stop() methods for controlling loop execution, plus per-thread local state for avoiding lock contention. Break() ensures all iterations before the break point execute (sequential equivalent), while Stop() stops as soon as possible. Local values avoid locking by maintaining thread-local accumulators combined at the end.
🔑 Definition — ParallelLoopState: Provides methods to control parallel loop execution.
📐 Properties: Break(), Stop(), IsExceptional, IsStopped, LowestBreakIteration, ShouldExitCurrentIteration
📌 Example of Break:
Parallel.ForEach("Hello, world", (c, loopState) =>
{
if (c == ',') loopState.Break();
else Console.Write(c);
}); // Output: Hlloe (not sequential because parallel)
🔑 Definition — Local values in Parallel.For: An overload that allows thread-local state for accumulation without shared locks.
📐 Usage: Parallel.For<TLocal>(int fromInclusive, int toExclusive, Func<TLocal> localInit, Func<int, ParallelLoopState, TLocal, TLocal> body, Action<TLocal> localFinally);
📌 Example:
object locker = new object();
double grandTotal = 0;
Parallel.For(1, 10000000,
() => 0.0, // local init
(i, state, localTotal) => localTotal + Math.Sqrt(i), // body
localTotal => { lock (locker) grandTotal += localTotal; } // local finally
);
Concurrent Collections
Concurrent collections (ConcurrentStack<T>, ConcurrentQueue<T>, ConcurrentBag<T>, ConcurrentDictionary<TKey,TValue>) are optimized for concurrent access. They're thread-safe but conventional collections outperform them except in highly concurrent scenarios. Thread-safe collection doesn't guarantee thread-safe code (enumerating while another thread modifies gives mixed results). Stack, Queue, Bag are implemented with linked lists (less memory efficient). The IProducerConsumerCollection<T> interface provides TryAdd and TryTake methods.
🔑 Definition — ConcurrentBag<T>: An unordered collection that maintains a linked list per thread for minimal contention on Add operations.
🔑 Definition — BlockingCollection<T>: A wrapper that waits (blocks) instead of returning false, with optional bounded capacity. Defaults to a queue.
📐 Usage: BlockingCollection<Action> _taskQ = new BlockingCollection<Action>();
📌 Example: Producer-consumer queue implementation:
public class PCQueue : IDisposable
{
BlockingCollection<Action> _taskQ = new BlockingCollection<Action>();
public PCQueue(int workerCount)
{
for (int i = 0; i < workerCount; i++)
Task.Factory.StartNew(Consume);
}
public void Enqueue(Action action) { _taskQ.Add(action); }
void Consume()
{
foreach (Action action in _taskQ.GetConsumingEnumerable())
action();
}
public void Dispose() { _taskQ.CompleteAdding(); }
}
🔑 Definition — GetConsumingEnumerable: Used with BlockingCollection to iterate over items, blocking when empty, and completing when CompleteAdding is called.
⭐ Key Takeaways
The most critical concepts from this lecture: Task.WhenAny enables timeout patterns while Task.WhenAll aggregates results and exceptions from concurrent tasks. Parallel.For/ForEach with ParallelLoopState allow fine-grained control including Break/Stop and per-thread local values for efficient parallel accumulation without shared locks. Concurrent collections like ConcurrentBag and BlockingCollection provide thread-safe storage and are essential for producer-consumer patterns, with BlockingCollection's GetConsumingEnumerable simplifying worker thread implementation. For exam, understand the difference between WhenAll vs sequential awaits, Break vs Stop semantics, and how local values in Parallel.For eliminate lock contention.
🧠 Quick Revision Questions
- What is the difference between
Task.WhenAnyandTask.WhenAll? When would you use each? - How does
ParallelLoopState.Break()differ fromParallelLoopState.Stop()in parallel loops? - Explain how per-thread local values in
Parallel.Forimprove performance over locking. - What is a
BlockingCollection<T>and how doesGetConsumingEnumerable()work? - Why might concurrent collections be slower than conventional collections in low-contention scenarios?
📘 Lecture 37 — Event Driven Programming on the Web and Mobile
📖 Overview: This lecture transitions from multithreading and task parallelism to event-driven programming on the web and mobile platforms. It covers the evolution from static HTML websites to dynamic, interactive web applications powered by JavaScript, the roles of HTML, CSS, and JavaScript as web technologies, and introduces jQuery as a library to simplify JavaScript development and handle browser incompatibilities.
🗂️ Topics Covered
This lecture covers client-side programming and event handling on the web, distinguishes static HTML sites from interactive JavaScript-driven sites, discusses the history and standardization of JavaScript, introduces jQuery as a JavaScript library, explains the structural (HTML), presentation (CSS), and behavioral (JS) layers of web development, covers basic HTML structure and CSS syntax, differentiates client-side vs server-side programming, explains how to embed JavaScript in web pages using <script> tags, covers basic JavaScript syntax (variables, arrays, functions, control flow), and introduces jQuery fundamentals including using CDNs, the document ready function, and basic element selection and manipulation.
📝 Lecture Summary
Tasks and Multithreading Recap
The lecture begins by finishing the discussion on tasks and multithreading, which are important for event-driven and visual programming to remain responsive. Key points from the previous lecture include the Task Parallel Library with Parallel.Invoke, Parallel.For, and Parallel.ForEach. Important concepts covered were the loop counter, Break, Stop, and per-thread counter. The discussion also included concurrent collections like ConcurrentStack, ConcurrentQueue, ConcurrentBag, and ConcurrentDictionary, ending with a producer-consumer queue implemented with tasks.
Web and Mobile Event-Driven Programming
The lecture introduces event-driven programming on the web and mobile, covering client-side programming and event-handling, with a note that server-side web programming is covered in other courses.
Static vs Interactive Websites
Initially, websites were static using HTML (Hypertext Markup Language) which only displayed information. Today, websites achieve the interactivity of desktop applications because of JavaScript. JavaScript enables animation, interactivity, and dynamic visual effects. Examples include: immediately displaying error messages for wrong data, updating totals when items are added to a shopping cart, running slideshows instead of static image lists, expanding and collapsing information, and showing popup tooltips. This provides immediate feedback without the delay of server-side processing and without constant page reloading, making the experience feel like desktop programs. An example cited is Google Maps, where JavaScript enables zooming in and out without reloading, unlike older map sites.
JavaScript History
JavaScript was introduced in 1995 by Netscape, making it about as old as the web itself. Initially, it was used for hobby-like features such as flies following the mouse or moving stock ticker messages. Many scripts didn't work across all browsers and often crashed them. JavaScript has nothing to do with Java; it was originally named LiveScript but renamed to associate with the then-popular Java. Initial interoperability problems existed between Netscape and Internet Explorer (IE), with incompatible features being added. Microsoft introduced JScript, their version of JavaScript for IE. These issues are mostly handled today through standardization, though some quirks remain. The official standardization name is ECMAScript. JavaScript was refueled by high-profile sites like Google using it extensively over the last decade. Now, JavaScript is even used for non-web scripting, including Flash ActionScript (which is based on it), widgets, and phone apps.
jQuery
jQuery is a JavaScript library intended to make JavaScript programming easier by solving JavaScript's complexity and browser incompatibilities. Tasks that would take hundreds of lines of code can be done in a single line of code (LOC) using jQuery. Many advanced features are available as jQuery plugins, and jQuery is used on millions of websites.
The Three Web Layers
The lecture introduces the three layers of web development:
- HTML: The structural layer
- CSS: The presentation layer
- JavaScript: The behavioral layer
HTML Structure
HTML uses simple commands called tags. The <!DOCTYPE html> declaration (for HTML5) tells the browser how to render the page and what standards to follow. There are five types of HTML in use: HTML 4.01 Transitional, HTML 4.01 Strict, XHTML 1.0 Transitional, XHTML 1.0 Strict, and HTML5. All current browsers understand them all. HTML uses starting and closing tags like XML. At least three tags are required: the <html> root tag, the <head> tag containing the title and other metadata, and the <body> tag containing all parts to be rendered in the browser window.
<p></p>is a paragraph<strong></strong>is for emphasis (bold)<a href="http://..."></a>is a hyperlink (XML attribute and value) Validating HTML means checking if all tags are appropriately closed etc.
Example HTML Document
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<title>Hey, I am the title of this web page.</title>
</head>
<body>
Hey, I am some body text on this web page.
</body>
</html>
CSS (Cascading Style Sheets)
Originally there was only HTML. CSS is a formatting language. HTML is only for structure (e.g., <h1>, <h2> are both headings with different importance, <ul> is an unordered list). CSS adds design. A CSS style is a rule telling the web browser how to display an element. For example, a CSS rule can make all <h1> tags 36 pixels tall, in Courier font, and in orange. CSS can do more powerful things like adding borders, changing margins, and controlling exact placement on a web page. JavaScript can add, remove, or change CSS properties based on user input or mouse clicks, and can even animate from one property to another (e.g., yellow to red, or across the screen by changing position).
CSS Syntax: A single CSS style is a rule that tells how to format—make "this" look like "that". It consists of a selector and a declaration block.
- Selector: Can be a headline, paragraph of text, photo, etc.
- Declaration block: Can turn text blue, add red border around a paragraph, position the photo at center of page, etc.
- Example:
p { color: red; font-size: 1.5em; } - Each declaration is a property-value pair followed by a semicolon (
;).
JavaScript (JS) Functionality
JavaScript lets a page re-act. It enables smart web forms that let users know when they miss important information, makes elements appear or disappear, or move around a webpage. It can even load new content from a web server without reloading, creating more engaging and effective websites.
Client-side vs Server-side Programming
Client-side programming uses programming languages for the web browser. The alternative is server-side programming languages like PHP, .NET, ASP, ColdFusion, Ruby on Rails, etc. These run on the web server to handle logic such as accessing databases, processing credit cards, and sending emails. Visitors must wait until the server response comes back. Client-side languages can react immediately, making them more responsive.
Other client-side technologies include applets, Silverlight, and Flash. These often require a plugin or start slowly due to downloading. Sometimes it's difficult to tell if an effect is done with Flash or JavaScript (for example, Yahoo Maps was originally Flash and then rewritten). Right-clicking can reveal if the Flash Player is in use.
Ajax brings client-side and server-side together: JavaScript talks to the server, downloads content, and updates the webpage (as seen in Google Maps). JavaScript is a programming language that can also be used on the server side (e.g., Node.js supports JavaScript on the server-side).
Compiled vs Interpreted Languages
JavaScript is a scripted (interpreted) language. The JavaScript interpreter is built into web browsers. The web browser has two main components:
- A layout or rendering engine (for understanding HTML and CSS)
- A JavaScript interpreter
To tell the web browser about JavaScript, we use a <script></script> tag.
JavaScript in HTML (HTML4 vs HTML5)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>My Web Page</title>
<script type="text/javascript">
</script>
</head>
<!Doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>My Web Page</title>
<script>
</script>
</head>
JavaScript is usually placed in the <head> section, but it's okay to put it anywhere and in multiple tags. The script can also be placed after </body> so the script is loaded after the page is displayed. External script files can also be used, which are easy to share. Separate script tags are used if you want inline code, and the src attribute is used for external files. Multiple external files can be used.
External JavaScript Files
<!Doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>My Web Page</title>
<script src="navigation.js"></script>
</head>
<!Doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>My Web Page</title>
<script src="navigation.js"></script>
<script src="slideshow.js"></script>
<script>Alert('hello world!');</script>
</head>
Basic JavaScript Example
<script>
document.write('<p>Hello world!</p>');
</script>
The document.write function writes content directly into the webpage.
jQuery Example with Fade-in
<link href="../_css/site.css" rel="stylesheet">
<script src="../_js/jquery-1.6.3.min.js"></script>
<script>
$(function () {
$('body').hide().fadeIn(3000);
});
</script>
This example hides the body and then fades it in over 3000 milliseconds (3 seconds).
JavaScript Syntax
JavaScript has syntax similar to C++ and C#:
- Variables: Created using
var x. Names begin with a letter,$, or_. - Arrays:
var days = ['Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat', 'Sun'];alert(days[0]);displays "Mon"var playlist = [];creates an empty arrayvar prefs = [1, 223, 'www.oreilly.com', false];creates a mixed-type arrayprefs.push('test');adds to the endprefs.unshift('test');inserts at the startshift()gets/removes the first element (queue using push/shift)pop()removes the last element (stack)
- Functions:
alert()andprompt()are built-in functions.
JavaScript Function Example
var TAX = .08;
function calculateTotal(quantity, price) {
var total = quantity * price * (1 + TAX);
var formattedTotal = total.toFixed(2);
return formattedTotal;
}
var saleTotal = calculateTotal(2, 16.95);
document.write('Total cost is: $' + saleTotal);
JavaScript Control Flow
JavaScript supports if, while, for, and do...while statements (similar to C#). Function declarations use the function keyword. There are no types for variables or return values.
jQuery Fundamentals
Many JavaScript programs need to:
- Select elements
- Add new content
- Hide and show content
- Modify tag attributes
- Determine the value of form fields
- React to user actions
The details can be complicated, especially with browser interoperability. Libraries offer a set of functions to make these tasks easy. jQuery is only about 30KB when compressed, easy to learn, used on millions of sites, free, and has a large developer community with many plugins.
CDNs (Content Delivery Networks) can be used instead of hosting jQuery yourself. Google's CDN is very popular. Using CDNs means the library is often cached in the user's browser.
jQuery CDN Examples
<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.6.3.min.js"></script>
<script src="http://code.jquery.com/jquery-1.6.3.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script>
A local copy can also be used:
<script src="js/jquery-1.6.3.min.js"></script>
Document Ready Function
The $(document).ready() function waits until the HTML of the webpage loads and then runs the code. This is necessary because the browser processes in order, and to work with elements, they must be downloaded first.
$(document).ready(function () {
// your programming goes here
});
The shortcut for this is:
$(function () {
// your programming goes here
}); // end ready
Dynamically Changing Webpages
Dynamically changing the webpage is the key idea. It enables effects like mouse-over popups, click-based expand/collapse, and showing detail information. For example, a date picker is run by JavaScript but is itself made of HTML and CSS. The JavaScript just makes the presentation interactive. Two steps:
- Select something (an element)
- Do something with it (change a property, add/remove an element, add/remove extra info, add/remove the
classattribute, or a combination of these)
⭐ Key Takeaways
- JavaScript enables interactive, desktop-like experiences on the web by providing immediate feedback without server delays or page reloads, evolving from static HTML to dynamic client-side programming.
- The web development stack has three layers: HTML for structure, CSS for presentation, and JavaScript for behavior, with jQuery simplifying JavaScript development by handling browser incompatibilities and reducing code complexity.
- JavaScript is an interpreted language embedded in web browsers using
<script>tags, with syntax similar to C++/C# but with dynamic typing and no variable types, supporting arrays, functions, control flow, and event handling. - jQuery's
$(document).ready()function ensures code runs only after the HTML document is fully loaded, which is essential for working with page elements, and jQuery simplifies element selection and manipulation in two steps: select something, then do something with it. - Using CDNs for jQuery libraries improves performance through caching, and client-side programming (JavaScript) complements server-side programming (PHP, .NET) by handling immediate user interactions while servers handle database access and complex processing.
🧠 Quick Revision Questions
- What are the three layers of web development according to this lecture, and what role does each play?
- What is the purpose of jQuery, and how does it address the problems of writing JavaScript code?
- Explain the difference between client-side and server-side programming as discussed in this lecture.
- What does the
$(document).ready()function do in jQuery, and why is it important? - How do JavaScript arrays work? Give an example of creating an array, adding an element to it, and accessing an element by index.
📘 Lecture 38 — HTML DOM and jQuery
📖 Overview: This lecture introduces jQuery as a powerful JavaScript library for HTML DOM manipulation. It covers how jQuery simplifies element selection, content modification, attribute manipulation, event handling, and chaining compared to traditional JavaScript DOM methods, making cross-browser web development more efficient.
🗂️ Topics Covered
This lecture covers jQuery selectors (basic and advanced including ID, element, class, descendant, child, sibling, and attribute selectors), jQuery filters (:even, :odd, :first, :last, :not, :has, :contains, :hidden, :visible), jQuery functions for adding, modifying, and removing content (html(), text(), append(), prepend(), after(), remove(), replaceWith()), attribute manipulation (addClass(), removeClass(), toggleClass(), css(), attr(), removeAttr()), the each() method for iterating over selections, and jQuery event handling (mouse events, document/window events, form events, keyboard events) including ready(), hover(), toggle(), and click().
📝 Lecture Summary
HTML DOM vs jQuery Selectors
HTML DOM is much like XML DOM. JavaScript provides ways to select elements, but some browsers allow selecting by CSS while others do not (cross-browser issues). Traditional DOM methods include document.getElementById('banner') and document.getElementsByTagName('a'). To select <a> tags with class navbutton, you must select all <a> tags, then iterate and find those with the right class. In jQuery, $(‘selector’) simplifies this significantly — for example, $(‘#banner’) selects the tag with id banner, and $(‘#banner’).html(‘<h1>JavaScript was here</h1>’) replaces its content. Html is a jQuery helper function.
Basic Selectors
Basic selectors include ID selectors, element selectors, and class selectors.
📌 Example with ID selector:
<p id="message">Special message</p>
var messagepara = document.getElementById(’message’);
var messagepara = $(’#message’);
📌 Example with element selector:
var linkslist = document.getElementsByTagName(’a’);
var linkslist = $(’a’);
📌 Example with class selector:
$(’.submenu’)
$(’.submenu’).hide();
Advanced Selectors
Advanced selectors include:
- Descendant selectors:
$(’#navbar a’)— selects all<a>elements inside#navbar - Child selectors:
$(’body > p’)— selects<p>elements that are direct children ofbody - Adjacent sibling:
$(’h2 + div’)— selects<div>immediately following an<h2> - Attribute selectors:
$(’img[alt]’),$(’input[type=”text”]’),$(’a[href^=”mailto:”]’),$(’a[href$=”.pdf”]’),$(’a[href*=”missingmanuals.com”]’), plus form element selectors covered later
📌 Example with attribute selector:
$(’a[href$=”.pdf”]’) selects all anchor tags whose href attribute ends with .pdf.
jQuery Filters
jQuery filters include :even, :odd, :first, :last, :not, :has, :contains, :hidden, :visible.
📌 Examples:
$(’.striped tr:even’) // selects even rows in a striped table
$(’a:not(.navbutton)’) // selects anchor tags that do NOT have class navbutton
$(’li:has(a)’) // selects list items that contain an anchor tag (different from descendant)
$(’a:contains(Click Me!)’) // selects anchor tags containing the text "Click Me!"
$(’div:hidden’).show() // shows all hidden divs
jQuery Selection Behavior
jQuery selections do not end up with DOM lists but rather jQuery equivalents. jQuery automatically loops over all matched elements.
📌 Chaining functions:
$(’#popup’).width(300).height(300)
$(’#popup’).width(300).height(300).text(’Hi!’).fadeIn(100)
Chaining allows multiple jQuery methods to be called sequentially on the same selection.
jQuery Functions to Add/Modify Content
Consider this example HTML:
<div id="container">
<div id="errors">
<h2>Errors:</h2>
</div>
</div>
📌 Examples of content manipulation:
alert($(’#errors’).html()); // gets HTML content of #errors
$(’#errors’).html(’<p>There are four errors in this form</p>’); // replaces HTML content
$(’#errors h2’).text(’No errors found’); // replaces text content
$(’#errors’).append(’<p>There are four errors in this form</p>’); // adds content at end
$(’#errors’).prepend(’<p>There are four errors in this form</p>’); // adds content at beginning
$(’#username’).after(’<span class="error">User name required</span>’); // adds after element
$(’#popup’).remove(); // removes element entirely
$(’#product101’).replaceWith(<p>Added to cart</p>’); // replaces element with new content
$(’a[href^="http://"]’).addClass(’externallink’); // adds class to matching elements
💡 Why this matters: These content manipulation methods provide powerful ways to dynamically update webpage content without reloading the page, forming the foundation of modern interactive web applications.
Attribute Manipulation
Attributes can be manipulated with addClass(), removeClass(), toggleClass(), and css().
📌 Examples:
var bgColor = $(’#main’).css(’background-color’); // gets CSS property
$(’body’).css(’font-size’, ’200%’); // sets single CSS property
$(’p.highlight’).css(’border’, ’1px solid black’); // sets single CSS property
var baseFont = $(’body’).css(’font-size’);
baseFont = parseInt(baseFont, 10);
$(’body’).css(’font-size’, baseFont * 2); // doubles font size
$(’#highlighteddiv’).css({’background-color’:’#FF0000’,’border’:’2px solid #FE0037’}); // sets multiple properties
For changing HTML attributes, css() and addClass() are just shortcuts. The general-purpose methods are attr() and removeAttr():
var imageFile = $(’#banner img’).attr(’src’); // gets attribute value
$(’#banner img’).attr(’src’, ’images/newimage.png’); // sets attribute value
$(’body’).removeAttr(’bgcolor’); // removes attribute
Acting on Each Element with each()
When you want to do something special with each element in a selection, use each() with an anonymous function. Use this for the current element as a DOM object, and $(this) for the current element as a jQuery selection.
📌 Example:
$(’selector’).each(function () {
// code goes in here
});
$(’a[href^=http://]’).each(function () {
var extLink = $(this).attr(’href’);
$(’#biblist’).append(’<li>’ + extLink + ’</li>’);
});
Events
Events are things that happen to a webpage — page loading, mouse movement, key presses — and you respond to them with event handlers.
Types of events:
- Mouse events:
click,dblclick,mousedown,mouseup,mouseover,mouseout,mousemove - Document/window events:
load,resize,scroll,unload - Form events:
submit,reset,change,focus,blur - Keyboard events:
keypress(fires repeatedly),keydown,keyup
Steps for event handling:
- Select elements
- Assign an event
- Pass a function to the event
📌 Example:
$(’#menu’).mouseover(function () {
$(’#submenu’).show();
}); // end mouseover
ready() vs. load() Event
The ready() event fires when the DOM is fully loaded, while the load() event fires when all content (including images) has finished loading.
📌 Example of document ready:
$(function() {
// do something on document ready
});
jQuery Event Methods
Hover combines mouseover and mouseout. Toggle is similar to hover except it works on and off by clicks. The Event object is passed to all functions handling events.
📌 Comprehensive event example:
<script>
$(document).ready(function () {
$(’html’).dblclick(function () {
alert(’ouch’);
}); // end double click
$(’a’).mouseover(function () {
var message = "<p>You moused over a link</p>";
$(’.main’).append(message);
}); // end mouseover
$(’#button’).click(function () {
$(this).val("Stop that!");
}); // end click
}); // end ready
</script>
📌 Hover example:
$(’#menu’).hover(function () {
$(’#submenu’).show();
}, function () {
$(’#submenu’).hide();
}); // end hover
⭐ Key Takeaways
jQuery simplifies DOM manipulation by providing cross-browser compatible selectors that automatically loop over matched elements, eliminating the need for manual iteration over DOM lists. The $() function serves as the core selector using CSS-like syntax for ID, class, element, descendant, child, sibling, and attribute selections, along with powerful filters like :even, :odd, :hidden, and :contains. jQuery enables efficient chaining of methods and provides comprehensive content manipulation methods (html, text, append, prepend, after, remove, replaceWith), attribute manipulation (css, addClass, attr, removeAttr), and event handling (click, mouseover, hover, ready) — all following a consistent three-step pattern: select elements, assign event, pass function. The each() method with $(this) allows custom iteration over selections, while understanding the difference between ready() (DOM loaded) and load() (all content loaded) is essential for proper page initialization.
🧠 Quick Revision Questions
- What is the jQuery equivalent of
document.getElementById('message')anddocument.getElementsByTagName('a')? - How would you select all anchor tags whose href attribute ends with ".pdf" using jQuery?
- What is the difference between
$(’#errors’).html(),$(’#errors’).text(),$(’#errors’).append(), and$(’#errors’).prepend()? - Explain the three-step process for handling events in jQuery, and provide an example using the
hover()method. - What is the purpose of the
each()method in jQuery, and how do you reference the current element as a jQuery object inside it?
📘 Lecture 39 — Event Properties and jQuery Animations, Forms, and Ajax
📖 Overview: This lecture covers advanced jQuery event handling, including event properties like
String.fromCharCode(evt.which)and methods to stop default browser behavior (evt.preventDefault()andreturn false). It also explains event bubbling and how to stop it, generic event binding, jQuery animations (fade, slide, and custom animate), form handling, and introduces Ajax for asynchronous page updates.
🗂️ Topics Covered
The lecture covers event properties and methods (String.fromCharCode, evt.preventDefault, return false, evt.stopPropagation), generic event binding with .bind() and .unbind(), the FAQ toggle example with .next() and .fadeIn(), jQuery animations (fadeIn, fadeOut, slideDown, slideUp, slideToggle, custom animate()), easing, animation chaining with .delay(), a photo gallery example using fadeIn and fadeOut, form handling with .val(), submit() event, focus, blur, change events, and an introduction to Ajax.
📝 Lecture Summary
Event Properties and Methods
The lecture begins with event properties and methods. You can use String.fromCharCode(evt.which) to get the character from a keypress event. To stop normal browser behavior (e.g., following a link, submitting a form), you use evt.preventDefault() or return false. To remove event handlers, use $('.tabbutton').unbind('click'). JavaScript has default event bubbling, where events propagate from the target element up to the document. You can stop this with evt.stopPropagation().
🔑 Definition — evt.preventDefault(): A method that stops the default action of an event (e.g., following a link, submitting a form).
📐 Formula: evt.preventDefault() → Prevents the browser's default behavior for that event.
📌 Example: To stop a link from navigating: $('a').click(function(evt) { evt.preventDefault(); });
🔑 Definition — evt.stopPropagation(): A method that stops the event from bubbling up to parent elements.
📐 Formula: evt.stopPropagation() → Prevents the event from triggering handlers on ancestor elements.
📌 Example: To stop a click on a child from triggering the parent's click handler: $('.child').click(function(evt) { evt.stopPropagation(); });
Generic way to bind events: Use .bind() with or without custom data. $('#selector').bind('click', mydata, functionName) passes data to the handler, while $('selector').bind('click', functionName) is equivalent to $('selector').click(functionName).
📐 Formula — Event positioning: var xpos = evt.pageX; var ypos = evt.pageY; → Gets the mouse X and Y coordinates relative to the document.
📌 Example:
$(document).click(function (evt) {
var xpos = evt.pageX;
var ypos = evt.pageY;
alert('X:' + xpos + ' Y:' + ypos);
});
📐 Formula — Binding multiple events: $(document).bind('click keypress', function () { ... }) → Binds both click and keypress events to the same function.
📌 Example:
$(document).bind('click keypress', function () {
$('#lightbox').hide();
});
You can also bind multiple events with an object syntax:
$('#theelement').bind({
'click': function() { /* do something interesting */ },
'mouseover': function() { /* do something else interesting */ }
});
FAQ Example with Toggle
The FAQ example demonstrates using .toggle() with two functions to create an accordion-like effect. When a heading (h2) is clicked, the next .answer element fades in or out, and a close class is toggled.
📌 Example — FAQ Toggle:
$(document).ready(function() {
$('.answer').hide();
$('#main h2').toggle(
function() {
$(this).next('.answer').fadeIn();
$(this).addClass('close');
},
function() {
$(this).next('.answer').fadeOut();
$(this).removeClass('close');
}
);
});
jQuery Animations
jQuery provides built-in animation methods. Options include 'slow', 'fast', 'normal', or a number of milliseconds (defaults: slow=600, normal=400, fast=200). Methods include fadeIn(), fadeOut(), fadeToggle(), slideDown(), slideUp(), and slideToggle().
📐 Formula — Fade animation: $('element').fadeOut('slow'); → Fades out the element over a slow duration.
📌 Example: $('element').fadeIn(300);
📐 Formula — Slide animation: $('#login form').slideDown(300); → Slides down the form element over 300ms.
📌 Example — Login Slider:
$(document).ready(function () {
$('#open').toggle(
function () {
$('#login form').slideDown(300);
$(this).addClass('close');
},
function () {
$('#login form').fadeOut(600);
$(this).removeClass('close');
}
);
});
Generic Animate: The .animate() method can animate any numeric CSS property. Note that CSS property names with hyphens must be converted to camelCase (e.g., border-left-width becomes borderLeftWidth). You can also use += and -= for relative changes.
📐 Formula — Custom animate: $('#message').animate({ left: '650px', opacity: 0.5, fontSize: '24px' }, 1500); → Animates the element over 1500ms, moving it to left 650px, changing opacity to 0.5, and font size to 24px.
📌 Example — Relative movement:
$('#moveit').click(function () {
$(this).animate({ left: '+=50px' }, 1000);
});
Easing: jQuery supports 'linear' and 'swing' easing. You can also pass a callback function to run when the animation finishes.
📐 Formula — Easing and callback: $('#element').slideUp(1000, 'linear'); → Slides up with linear easing.
📌 Example — Animation with callback:
$('#photo').fadeIn(1000, function () {
$('#caption').fadeIn(1000);
});
📌 Example — Chaining animations:
$('#photo').width(0).height(0).css('opacity', 0);
$('#caption').hide();
$('#photo').animate(
{ width: '200px', height: '100px', opacity: 1 },
1000,
function() { $('#caption').fadeIn(1000); }
);
Chaining effects: $('#photo').fadeIn(1000).delay(10000).fadeOut(250); → Fades in, waits 10 seconds, then fades out.
Photo Gallery Example
This example shows how to create a simple photo gallery using event prevention and animation.
📌 Example — Photo Gallery:
$('#gallery a').click(function(evt) {
evt.preventDefault();
var imgPath = $(this).attr('href');
var oldImage = $('#photo img');
var newImage = $('<img src="' + imgPath + '">');
newImage.hide();
$('#photo').prepend(newImage);
newImage.fadeIn(1000);
oldImage.fadeOut(1000, function(){
$(this).remove();
});
});
$('#gallery a:first').click();
Forms
Form handling with jQuery uses .val() to get or set input values. The submit() event is used to validate forms before submission. Other form events include focus, blur, click, and change (for menus).
🔑 Definition — .val(): Gets or sets the value of form elements (input, select, textarea).
📌 Example — Calculate total:
<input name="quantity" type="text" id="quantity">
<input name="total" type="text" id="total">
var unitCost = 9.95;
var amount = $('#quantity').val();
var total = amount * unitCost;
total = total.toFixed(2);
$('#total').val(total);
Submit Event: Use .submit() to validate a form before it is sent to the server. Return false to prevent submission.
📌 Example — Form validation:
$(document).ready(function() {
$('#signup').submit(function() {
if ($('#username').val() == '') {
alert('Please supply a name in the Name field.');
return false;
}
});
});
Focus, Blur, Click, Change: These events are used for interactive form elements. The change event is especially useful for reacting to selections from a drop-down menu.
💡 Why this matters: Client-side validation with events like submit, focus, and blur improves user experience by providing immediate feedback, reducing server load, and preventing invalid data submission.
Ajax Introduction
The lecture concludes with an introduction to Ajax. Not everything can be done at the client side; pages may need to disappear and reappear. Ajax (Asynchronous JavaScript and XML) lets a webpage ask for information and update itself when the information arrives. The term was coined in 2005 for interactive sites coming from Google (e.g., Google Maps, Gmail).
🔑 Definition — Ajax: Asynchronous JavaScript and XML — a technique that allows web pages to request and receive data from a server without reloading the page.
📐 Formula: $.ajax({ url: 'server.php', success: function(data) { /* update page */ } }); → Sends a request and handles the response asynchronously.
📌 Example: Google Maps and Gmail use Ajax to update parts of the page without full page reloads.
⭐ Key Takeaways
The most critical concepts from this lecture are: (1) You can stop default browser behavior with evt.preventDefault() and stop event bubbling with evt.stopPropagation(). (2) jQuery animations include fadeIn(), fadeOut(), slideDown(), slideUp(), and a generic animate() for any numeric CSS property — always convert hyphenated CSS names to camelCase. (3) Animations can be chained with .delay() and accept easing (linear/swing) and callback functions. (4) Form handling uses .val() for values and events like submit, focus, blur, and change for validation and interactivity. (5) Ajax allows asynchronous server communication without page reload, enabling modern interactive web applications like Google Maps and Gmail.
🧠 Quick Revision Questions
- What is the difference between
evt.preventDefault()andevt.stopPropagation()in jQuery? - How would you animate a div's opacity from 0 to 1 while moving it 100px to the right over 2 seconds using jQuery?
- What does
$(this).next('.answer').fadeIn()do in the FAQ example? - How do you get the value of an input field and set the value of another input field using jQuery?
- What does Ajax allow a web page to do that traditional form submission cannot?
📘 Lecture 40 — Ajax Basics
📖 Overview: This lecture introduces Ajax (Asynchronous JavaScript and XML), a powerful technique for creating responsive web applications that can exchange data with servers without reloading the entire page. It explains how jQuery simplifies Ajax operations, covering the
load(),get(),post()methods, data serialization, error handling, and JSON format.
🗂️ Topics Covered
The lecture covers the XMLHttpRequest object and its role in Ajax communication, jQuery's load() method for loading HTML content, $.get() and $.post() methods for sending data to servers, data formatting with query strings and object literals, form serialization, callback functions for processing server responses, error handling, and JSON as a lightweight data exchange format.
📝 Lecture Summary
In Last Lecture...
We discussed event objects and their properties, binding and unbinding events, jQuery animations (easing, chaining), FAQ sections, login sliders, photo galleries, forms and form selectors. Now we move to the real power: Ajax.
What Can Be Done with Ajax
Ajax allows displaying new HTML content without reloading the page, submitting forms and instantly displaying results, and logging in without leaving the page. Examples include star rating widgets and browsing through database information (like scrolling on Facebook or Twitter). Nothing radical—same functionality can be achieved with HTML and server-side programming, but Ajax makes pages feel more responsive and desktop-like.
How Ajax Works
JS, server-side programming, and web browser all work together:
- Web browser: Uses the XMLHttpRequest object (also called XHR) that makes Ajax possible by talking to the web server and getting a response
- JS: Sends request, waits for response, processes response, updates web page
- Web server: Receives request and responds as HTML, plain text, XML, or JSON. Application servers handle more complicated tasks
- A web server is needed for Ajax examples
Creating XMLHttpRequest
var newxhr = new XMLHttpRequest();
// Browser incompatibilities exist
// Call open to specify data type and destination
newxhr.open('GET', 'shop.php?Productid=34');
// Can use GET or POST
// Write a callback function that will remove, add, change elements
// Send data
newxhr.send(null); // For GET
newxhr.send('q=javascript'); // For POST
Response handling: Callback invoked when XHR receives status, text response, and possibly XML response:
- status = 200/304: All OK
- status = 404: File not found
- status = 500: Internal server error
- status = 403: Access forbidden
- responseText: Has text of JSON or HTML
- responseXML: Less commonly used
jQuery Simplifies Ajax
The simplest jQuery method is the load() function, which loads HTML into an area of a webpage:
// Load news in a div from a web server
$('#headlines').load('todays_news.html');
// Can only load from same site... Relative URLs possible
// Possible to add only a part of the loaded content
$('#headlines').load('todays_news.html #news');
// Example with click event
$('#newslinks a').click(function() {
var url = $(this).attr('href');
$('#headlines').load(url + ' #newsitem');
return false;
});
GET() and POST() Methods
$.get(url, data, callback);$.post(url, data, callback);
Need server side to do anything else. Server may not return HTML (e.g., database records as XML or JSON). jQuery handles differences of GET and POST. No selector needed—they stand by themselves.
$.get('ratemovie.php', 'rating=5');
$.post('ratemovie.php', 'rating=5');
Formatting Data
Can send a product number, entire form, or signup data. Format as query string or JS object literal.
Query string format:
URL: http://www.chia-vet.com/products.php?Prodid=18&sessid=1234
GET has a limit (often thousands of characters).
$.get('ratemovie.php', 'rating=5');
$.post('ratemovie.php', 'rating=5');
$.post('ratemovie.php', 'rating=5&user=Bob'); // Query string
// Incorrect (must escape special characters)
'favfood=Mac & Cheese' // Incorrect
// Properly escaped
'favfood=Mac%20%26%20Cheese'
// Using encodeURIComponent
var querystring = 'favFood=' + encodeURIComponent('Mac & Cheese');
$.post('foodchoice.php', querystring);
Object literal format (better way):
{
name1: 'value1',
name2: 'value2'
}
$.post('rankmovie.php', { rating: 5 });
var data = { rating: 5 };
$.post('rankmovie.php', data);
var data = {
rating: 5,
user: 'Bob'
};
$.post('rankmovie.php', data);
// Chaining
var data = $.post('rankmovie.php', {
rating: 5,
user: 'Bob'
});
Form Serialization
Serialize using name/value of form elements:
var formdata = $('#login').serialize();
$.get('login.php', formdata, loginresults);
Processing Data Returned
Callback first argument is data. Servers often use XML or JSON. Second argument is a string about status ("success").
Example: Movie rating:
function processResponse(data, status) {
var newhtml;
newhtml = '<h2>Your vote is counted</h2>';
newhtml += '<p>The average rating for this movie is ';
newhtml += data + '.</p>';
$('#message').html(newhtml);
}
// Using with get
$('#message a').click(function() {
var href = $(this).attr('href');
var querystring = href.slice(href.indexOf('?') + 1);
$.get('rate.php', querystring, processResponse);
return false; // Stop the link
});
Error Handling
$.get(url, data, successFunction).error(errorFunction);
$.get('rate.php', querystring, processResponse).error(errorResponse);
function errorResponse() {
var errormsg = "Your vote could not be processed right now.";
errormsg += "Please try again later.";
$('#message').html(errormsg);
}
JSON (JavaScript Object Notation)
JSON is a method for exchanging data. It's JS, so it's quick and easy for JS. No XML-like parsing needed. JSON is a JS object literal (MUST use quotations if names have spaces etc.).
// Example JSON object
{
firstname: 'Frank',
lastname: 'Smith',
phone: '503-555-1212'
}
// With quotes (needed for names with spaces)
{
'firstname': 'Frank',
'lastname': 'Smith',
'phone': '503-555-1212'
}
Server returns a string formatted like a JSON object literal. jQuery provides the $.getJSON() method. Callback will receive a JSON object.
var bday = {
person: 'Raoul',
date: '10/27/1980'
};
bday.person // 'Raoul'
bday.date // '10/27/1980'
Object literals can be composed of other object literals:
var data = {
contact1: {
firstname: 'Frank',
lastname: 'Smith',
phone: '503-555-1212'
},
contact2: {
firstname: 'Peggy',
lastname: 'Jones',
phone: '415-555-5235'
}
};
data.contact1.firstname // 'Frank'
// Iterating over JSON
$.each(JSON, function(name, value) {
// name and value
});
Like $.get() but data passed to callback:
$.getJSON('contacts.php', 'limit=2', processContacts);
💡 Why this matters: JSON is the most common data format for modern web APIs. It's lightweight, native to JavaScript, and much easier to work with than XML.
⭐ Key Takeaways
Ajax allows web pages to communicate with servers without full page reloads, making applications feel more responsive and desktop-like. The core browser object is XMLHttpRequest (XHR), which jQuery wraps into simple methods like load(), get(), and post(). Data can be sent as query strings or JavaScript object literals, and forms can be serialized using serialize(). Callback functions process server responses, and error handling is done with the .error() method chain. JSON is the preferred data format because it's native JavaScript and requires no parsing, with jQuery's $.getJSON() method providing direct access.
🧠 Quick Revision Questions
- What is the XMLHttpRequest object and what are its three key roles in Ajax communication?
- How does jQuery's
load()method differ from$.get()and$.post()in terms of usage and what they return? - What are the four possible HTTP status codes for XMLHttpRequest responses and what does each mean?
- How do you properly escape special characters in a query string, and why is using an object literal better?
- What is JSON, why is it preferred over XML for JavaScript applications, and how do you send and receive JSON data using jQuery?
📘 Lecture 41 — Objective-C Messaging and Syntax
📖 Overview: This lecture introduces Objective-C, an extension of C that incorporates Smalltalk-style messaging for object-oriented programming. It covers the core syntax for creating, initializing, and manipulating objects, along with key concepts like subclassing, properties, and dynamic typing, which are fundamental for iOS and macOS development.
🗂️ Topics Covered
The lecture begins with the basics of Objective-C messaging syntax, including allocation and initialization of objects. It then demonstrates working with collections like NSMutableArray and NSArray, and explores string manipulation with NSString and logging with NSLog. The discussion moves to subclassing from NSObject, defining instance variables, and implementing getter and setter methods. More advanced topics include instance methods (including overriding description), initializers with multiple parameters, class methods for factory-style object creation, dynamic typing and runtime error handling, and finally, simplified accessor declarations using properties.
📝 Lecture Summary
Objective-C Messaging Basics
Objective-C introduces Smalltalk-style messaging to C, developed in the early 1980s for NeXT computers. It is a simple extension of C. To create an object, you send it an alloc message, followed by an init message. These can be combined with nested message sends.
🔑 Definition — alloc: A class method that allocates memory for a new object instance.
🔑 Definition — init: An instance method that initializes a newly allocated object.
📐 Formula: NSMutableArray *arrayInstance = [[NSMutableArray alloc] init]; → Allocates and initializes an NSMutableArray object in one step.
Message syntax: [receiver selector arguments]
📌 Example: [arrayInstance addObject:anotherObject]; — sends the addObject: message to arrayInstance with anotherObject as the argument.
Pairing of labels and arguments is a feature of Objective-C.
📌 Example: In other languages: arrayInstance.replaceObjectsinRangeWithObjectsfromArrayrange(anotherArray, anotherRange);
In Objective-C: [arrayInstance replaceObjectsInRange:aRange withObjectsFromArray:anotherArray range:anotherRange];
Destroy objects using [arrayInstance release]; and set to nil: arrayInstance = nil; to avoid dangling pointers. Sending a message to nil is safe (returns nil). Nil is like null.
Working with Collections: NSMutableArray Example
A complete program demonstrates creating, populating, and iterating over an NSMutableArray.
int main (int argc, const char* argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *items = [[NSMutableArray alloc] init];
[items addObject:@"One"];
[items addObject:@"Two"];
[items addObject:@"Three"];
[items insertObject:@"Zero" atIndex:0];
for(int i = 0; i < [items count]; i++) {
NSLog(@"%@", [items objectAtIndex:i]);
}
[items release];
items = nil;
[pool drain];
return 0;
}
@ is a shortcut for creating NSString objects. NSLog uses format strings for output.
🔑 Definition — NSString: An Objective-C class for working with strings.
📌 Example: NSString *myString = @"Hello, World!"; — creates an NSString using the @ literal.
int len = [myString length]; — gets the string length.
len = [@"Hello, World!" length]; — works directly on a string literal.
myString = [[NSString alloc] initWithString:@"Hello, World!"]; — alternative creation method.
NSArray and NSMutableArray hold references to objects, not primitives or C structures. Use [NSNull null] to store nil-like values.
📐 Formula: int numberOfObjects = [array count]; — get the count of objects.
[array insertObject:object atIndex:numberOfObjects]; — inserts at end. Cannot add beyond end (throws exception).
[array addObject:[NSNull null]]; — adds a null placeholder.
NSString *object = [array objectAtIndex:0]; — retrieves an object by index.
💡 Why this matters: Collections (NSArray, NSDictionary, NSSet) form the backbone of data management in Objective-C applications.
Subclassing and Instance Variables
The root class of the entire Objective-C hierarchy is NSObject. Objective-C keywords start with @. Instance variables are declared within curly braces.
#import <Foundation/Foundation.h>
@interface Possession: NSObject
{
NSString *possessionName;
NSString *serialNumber;
int valueInDollars;
NSDate *dateCreated;
}
@end
🔑 Definition — Interface: Declares a class and its instance variables and methods in a .h file.
🔑 Definition — Instance Variables: Variables that belong to each instance of a class.
Getters and Setters (Accessor Methods)
Methods are declared in the interface and implemented in the implementation file.
@interface Possession: NSObject
{
// instance variables
}
- (void)setPossessionName:(NSString *)str;
- (NSString *)possessionName;
// ... other methods
@end
@implementation Possession
- (NSString *)possessionName {
return possessionName;
}
- (void)setPossessionName:(NSString *)newPossessionName {
possessionName = newPossessionName;
}
@end
📌 Example: Creating and using a Possession object:
Possession *p = [[Possession alloc] init];
[p setPossessionName:@"Red Sofa"];
NSString *str = [p possessionName];
NSLog(@"%@", str); // Prints "Red Sofa"
Instance Methods and Overriding description
Instance methods (indicated by -) can be overridden. The description method returns an NSString representation of the object.
📌 Example: Overriding description:
- (NSString *)description {
NSString *descriptionString = [[NSString alloc] initWithFormat:@"%@ (%@): Worth $%d, recorded on %@",
possessionName, serialNumber, valueInDollars, dateCreated];
return descriptionString;
}
Initializers
Initializers follow the init naming convention. id represents any object type. Every object has an isa pointer to its class, enabling method dispatch (like a vtable).
🔑 Definition — isa pointer: A pointer in every object that points to its class, used for dynamic method lookup.
📌 Example: Multi-parameter initializer:
- (id)initWithPossessionName:(NSString *)name valueInDollars:(int)value serialNumber:(NSString *)snumber {
self = [super init];
if (self) {
[self setPossessionName:name];
[self setSerialNumber:snumber];
[self setValueInDollars:value];
dateCreated = [[NSDate alloc] init];
}
return self;
}
Class Methods
Class methods (indicated by +) are called on the class itself, not instances.
📌 Example: randomPossession class method:
+ (id)randomPossession {
NSArray *randomAdjectiveList = [NSArray arrayWithObjects:@"Fluffy", @"Rusty", @"Shiny", nil];
NSArray *randomNounList = [NSArray arrayWithObjects:@"Bear", @"Spork", @"Mac", nil];
int adjectiveIndex = rand() % [randomAdjectiveList count];
int nounIndex = rand() % [randomNounList count];
NSString *randomName = [NSString stringWithFormat:@"%@ %@",
[randomAdjectiveList objectAtIndex:adjectiveIndex],
[randomNounList objectAtIndex:nounIndex]];
int randomValue = rand() % 100;
NSString *randomSerialNumber = [NSString stringWithFormat:@"%c%c%c%c%c",
'0' + rand() % 10, 'A' + rand() % 26, '0' + rand() % 10, 'A' + rand() % 26, '0' + rand() % 10];
Possession *newPossession = [[self alloc] initWithPossessionName:randomName
valueInDollars:randomValue
serialNumber:randomSerialNumber];
return newPossession;
}
Dynamic Typing and Runtime Errors
Objective-C is dynamically typed. Sending an unrecognized message causes a runtime error.
📌 Example: [items doSomethingWeird]; produces: *** -[NSCFArray doSomethingWeird]: unrecognized selector sent to instance 0x104b40
Objective-C has try-catch for runtime errors, but using loops is often better for programmer errors.
Properties
Properties provide simplified accessor declarations using @property and @synthesize.
🔑 Definition — @property: Declares a property with optional attributes (e.g., nonatomic, copy).
🔑 Definition — @synthesize: Tells the compiler to generate getter and setter methods.
📌 Example:
@interface Possession: NSObject
@property (nonatomic, copy) NSString *possessionName;
@property int valueInDollars;
@end
@implementation Possession
@synthesize possessionName, valueInDollars;
@end
The copy attribute creates a copy of the object being set, while mutableCopy creates a mutable copy.
⭐ Key Takeaways
Objective-C extends C with Smalltalk-style messaging where objects are created by sending alloc and init messages, and all method calls use square bracket syntax with labeled arguments. Memory management requires paired release and nil assignments to avoid dangling pointers, while collections like NSMutableArray and NSArray hold object references (use [NSNull null] for nil values). Subclassing from NSObject allows defining instance variables, getters/setters, initializers (with super init pattern), and class methods (marked with +), while the @property and @synthesize keywords provide automatic accessor generation. Dynamic typing means runtime errors occur for unrecognized selectors, but Objective-C handles nil messages safely.
🧠 Quick Revision Questions
- What is the syntax for creating and initializing an NSMutableArray object in Objective-C?
- How do you safely destroy an Objective-C object to avoid a dangling pointer?
- What is the purpose of the
@propertydirective, and what attributes can it take? - How does a class method differ from an instance method in terms of declaration syntax and usage?
- What happens when you send a message to
nilin Objective-C, and why is this behavior useful?
📘 Lecture 42 — Alloc and dealloc methods. Manual reference counting. Obj knows owner count retaincount. Retain and release methods. Should you release a created object that is returned ?. Want to say don’t release but i don’t want to be the owner. Autorelease. Added to nsautoreleasepool. Nsobject *x = [[[nsobject alloc] init] autorelease];.
📖 Overview: This lecture covers manual memory management in Objective‑C, focusing on reference counting, the retain and release methods, and the autorelease mechanism. The lecture then transitions to iOS programming, explaining the Model‑View‑Controller (MVC) pattern and demonstrating how to build a simple quiz app using Xcode and Interface Builder.
🗂️ Topics Covered
The lecture begins with memory management concepts: alloc, dealloc, manual reference counting, retain, release, autorelease, and the NSAutoreleasePool. It then shifts to iOS development, covering Xcode projects, Xib/Nib files, the MVC pattern, Interface Builder outlets and actions (IBOutlet, IBAction), and the complete implementation of a quiz application with questions and answers arrays.
📝 Lecture Summary
Alloc and dealloc methods. Manual reference counting. Obj knows owner count retaincount. Retain and release methods.
Every Objective‑C object has a retain count (also called owner count). When an object is created with alloc, init, new, or copy, its retain count is 1, and the caller owns the object. To take ownership of an object you did not create, you call retain, which increments the retain count. When you no longer need the object, you call release or autorelease, which decrements the retain count. When the retain count reaches zero, the object is deallocated (its dealloc method is called). You must never release objects you do not own.
🔑 Definition — Retain Count: The integer value stored in an object that indicates how many owners the object has. Each retain increments it; each release decrements it. When it hits 0, dealloc is called.
📐 Rule: If the method name contains init, new, or copy, you own the returned object. Otherwise, you do not own it and must assume it will be autoreleased.
📌 Example:
Nsobject *x = [[[nsobject alloc] init] autorelease];
Here, alloc gives ownership, but autorelease hands that ownership to the nearest NSAutoreleasePool, which will release the object at the end of the current run loop iteration.
Should you release a created object that is returned? Want to say don’t release but i don’t want to be the owner. Autorelease.
When a method creates an object and returns it to the caller, you face a dilemma: you don’t want to release it immediately (the caller needs it), but you also don’t want to be the permanent owner. The solution is autorelease. By sending autorelease instead of release, you mark the object for release later, usually when the current NSAutoreleasePool is drained. The caller can then use the object without worrying about ownership; if the caller wants to keep it, they call retain.
🔑 Definition — Autorelease: A mechanism that defers the release of an object until the end of the current run‑loop cycle. The object is added to the nearest NSAutoreleasePool, which sends it a release when drained.
📌 Example: The description method creates a temporary string, autoreleases it, and returns it:
- (Nsstring *)description {
Nsstring *descriptionstring = [[nsstring alloc] initwithformat:@"\%@ (\%@): Worth \$\%d, Recorded on \%@",
Possessionname, Serialnumber, Valueindollars, Datecreated];
return [descriptionstring autorelease];
}
A more concise version uses a class convenience method that internally autoreleases:
- (Nsstring *)description {
return [nsstring stringwithformat:@"\%@ (\%@): Worth \$\%d, Recorded on \%@",
Possessionname, Serialnumber, Valueindollars, Datecreated];
}
Retain count rules. Init, new, copy in name. Assume you own. Any other means. Assume in autorelease. If you don’t own and want to make sure, call retain. No longer need and own than release or autorelease. When 0 count, dealloc called.
The core memory management rules:
- Ownership rules: If you create an object using
alloc,init,new, orcopy, you own it. For any other method (e.g., class factory methods), you do not own it; it is assumed to be in an autorelease pool. - Taking ownership: If you do not own an object but want to guarantee it stays alive, call
retain. - Releasing ownership: When you own an object and no longer need it, call
releaseorautorelease. - Deallocation: When the retain count drops to zero, the runtime automatically calls
dealloc; you must overridedeallocto release the object’s own instance variables.
🔑 Formula: Own ? → retain (if you want to keep it) No longer need? → release or autorelease
📌 Example: A typical setter that uses retain/release:
- (void)setpossessionname:(nsstring *)str {
[str retain];
[possessionname release];
Possessionname = str;
}
And the dealloc method that releases all owned instance variables:
- (void)dealloc {
[possessionname release];
[serialnumber release];
[datecreated release];
[super dealloc];
}
Protocols i.e. Interfaces.
Protocols in Objective‑C are similar to interfaces in other languages. A protocol declares a set of methods that a class can choose to implement. It allows unrelated classes to respond to the same messages without inheritance.
Last lecture about ajax. Really learned how similar event driven programming is in JS than wpf. What about mobile. Same paradigm, different language. Same concepts, different incarnation. Ios programming. Learn objective-C. Event driven programming. Well see examples but you may not be able to try them. Need a mac computer and Xcode, the ios simulator. Let’s make a simple app.
The lecture shifts to iOS programming, noting that event‑driven programming in iOS uses the same paradigm as JavaScript and WPF but with Objective‑C. To build iOS apps, you need a Mac with Xcode and the iOS Simulator. The example project is a quiz app that displays a question and reveals the answer.
A quiz showing a question and revealing the answer. Create new project (window based applica.). Name it.
You start by creating a Window‑Based Application in Xcode. The user interface is defined in a Xib file (compiled to a Nib file). An iOS application is a directory containing executables and resources, similar to other platforms.
🔑 Definition — Xib/Nib: An XML file (.xib) that describes the user interface; when compiled, it becomes a .nib (Nextstep Interface Builder) file that is loaded at runtime.
Xib editing. Select window to add controls. Drag buttons. Give them names.
Use Interface Builder to select the Window, drag UI controls (like buttons and labels) onto the window, and set their properties (e.g., names, text).
Mvc pattern. View objects. Visible things. Uiview subclasses. Model objects. Hold data and know nothing about interface. Often use standard containers. Controllers keep things in sync.
The Model‑View‑Controller (MVC) pattern separates the app into three layers:
- View objects: Visible UI elements (subclasses of
UIView). - Model objects: Hold data and business logic; they know nothing about the interface. Often use standard container classes (e.g.,
NSMutableArray). - Controller objects: Keep the model and view in sync, responding to user interactions and updating the display.
Iboutlet, ibaction.
In Interface Builder, IBOutlet is a keyword used to connect a property in code to a visual element in the Xib. IBAction is a keyword used to mark a method as capable of receiving events from controls (e.g., button taps).
Interface and implementation of the quiz app delegate.
The quiz app uses a delegate object (conforming to <UIApplicationDelegate>) to manage the application’s lifecycle and UI logic.
Interface (QuizAppDelegate.h):
@interface QuizAppDelegate : Nsobject <UIApplicationDelegate> {
int currentquestionindex;
// The model objects
Nsmutablearray *questions;
Nsmutablearray *answers;
// The view objects
IBOutlet UILabel *questionfield;
IBOutlet UILabel *answerfield;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
- (IBAction)showquestion:(id)sender;
- (IBAction)showanswer:(id)sender;
@end
Implementation (QuizAppDelegate.m):
init: Call super init, then allocate and initializequestionsandanswersasNSMutableArrayobjects. Populate them with questions and answers.showquestion:: Incrementcurrentquestionindex; if past the last question, wrap back to 0. Get the question string, log it, set thequestionfieldtext, and clear theanswerfield.showanswer:: Get the answer for the current index and set it as theanswerfieldtext.
📌 Example – Implementation of the quiz controller:
@implementation QuizAppDelegate
- (id)init {
self = [super init];
if(self) {
questions = [[Nsmutablearray alloc] init];
answers = [[Nsmutablearray alloc] init];
[questions addobject:@"What is 7 + 7?"];
[answers addobject:@"14"];
[questions addobject:@"What is the capital of Vermont?"];
[answers addobject:@"Montpelier"];
[questions addobject:@"From what is cognac made?"];
[answers addobject:@"Grapes"];
}
return self;
}
- (IBAction)showquestion:(id)sender {
currentquestionindex++;
if (currentquestionindex == [questions count]) {
currentquestionindex = 0;
}
Nsstring *question = [questions objectatindex:currentquestionindex];
Nslog(@"displaying question: %@", question);
[questionfield settext:question];
[answerfield settext:@"???"];
}
- (IBAction)showanswer:(id)sender {
Nsstring *answer = [answers objectatindex:currentquestionindex];
[answerfield settext:answer];
}
@end
⭐ Key Takeaways
Memory management in Objective‑C relies on manual reference counting: you own objects created with alloc/init/new/copy and must release them when done. For objects returned from other methods, you assume they are autoreleased; retain them if you need to keep them longer. The autorelease mechanism defers deallocation, simplifying memory management in return values. In iOS development, the MVC pattern structures apps into model, view, and controller layers. Interface Builder uses IBOutlet and IBAction to connect code to UI elements. The quiz app example demonstrates how to create a window‑based app, connect buttons to actions, and manipulate labels programmatically.
🧠 Quick Revision Questions
- What is the retain count of an object immediately after
allocandinit? - When should you call
autoreleaseinstead ofrelease? - According to the memory‑management rules, when do you own a returned object?
- What is the purpose of
IBOutletandIBActionin Xcode? - In the quiz app example, why is the
initmethod necessary, and what is the purpose of thecurrentquestionindexvariable?
📘 Lecture 43 — Core Location Framework
📖 Overview: This lecture covers the Core Location framework for determining geographical position in iOS applications, building a "Where Am I" app. It introduces location managers, delegation patterns, map views, and annotations, demonstrating how to create location-aware iPhone applications.
🗂️ Topics Covered
Objective C memory management rules, writing a quiz app, xib and nib files, interface editor, MVC pattern, IBOutlet IBAction, Connection Inspector, init-showquestion-showanswer methods, Core Location Framework, CLLocationManager properties including distanceFilter and desiredAccuracy, delegation pattern, protocols, MKMapView for displaying maps, and MKAnnotation for adding location markers.
📝 Lecture Summary
Core Location Framework
The Core Location Framework provides classes for finding geographical position. Key properties include distanceFilter (minimum distance in meters before update is sent) and desiredAccuracy (how accurate the location reading should be). A CLLocationManager object is created to manage location services.
🔑 Definition — CLLocationManager: The object that starts and stops the delivery of location-related events to your app.
📐 Formula: distanceFilter = kCLDistanceFilterNone → no minimum distance threshold for updates
📐 Formula: desiredAccuracy = kCLLocationAccuracyBest → highest possible accuracy regardless of power/time
📌 Example: Creating and configuring a location manager:
locationmanager = [[CLLocationManager alloc] init];
[locationmanager setDistanceFilter:kCLDistanceFilterNone];
[locationmanager setDesiredAccuracy:kCLLocationAccuracyBest];
[locationmanager startUpdatingLocation];
Delegation Pattern
Delegation is a design pattern and an object-oriented approach to callbacks. It allows callback methods to share data between objects. A delegate can only be sent messages specified in its protocol. For every object that can have a delegate, there is a corresponding protocol (@protocol).
🔑 Definition — Delegate: An object that acts on behalf of another object, receiving messages when events occur.
The location manager sends messages to its delegate:
locationManager:didUpdateToLocation:fromLocation:- sent when new location data arriveslocationManager:didFailWithError:- sent when location cannot be found
📌 Example: Implementing delegate methods:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
NSLog(@"%@", newLocation);
}
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error {
NSLog(@"Could not find location: %@", error);
}
💡 Why this matters: Delegation allows one object to handle events from another without tight coupling, enabling reusable components.
Protocols
Protocols are like interfaces in other languages - they contain no implementation. Protocols for delegation are called delegate protocols. Methods can be marked as @optional (not required to implement) or @required (must implement). By default, methods are required.
🔑 Definition — Protocol (@protocol): A list of method declarations that can be adopted by a class.
Key points:
respondsToSelector:checks if an object implements a specific method- Classes must declare protocols they implement in their interface declaration
📌 Example: Protocol declaration and checking:
if ([[self delegate] respondsToSelector:updateMethod]) {
[[self delegate] locationManager:self didUpdateToLocation:newLocation fromLocation:oldLocation];
}
Memory and Delegation
Delegates are never retained to avoid retain cycles. The delegate property uses the assign attribute (weak reference) instead of retain or strong.
🔑 Definition — Retain cycle: A situation where two objects hold strong references to each other, preventing either from being deallocated.
📌 Example: Proper memory management with delegates:
- (void)dealloc {
if ([locationManager delegate] == self) {
[locationManager setDelegate:nil];
}
[locationManager release];
[window release];
[super dealloc];
}
Displaying a Map with MKMapView
An MKMapView displays the map and labels for recorded locations. Key visual elements include:
- MKAnnotationView - appears as icons on the map
- UIActivityIndicatorView - indicates the device is working
- UITextField - allows user input to label locations
To show user location on the map, set the showsUserLocation property to YES on the MKMapView.
📌 Example: Setting up map view with user location:
[worldView setShowsUserLocation:YES];
Map View Delegate and Region Zooming
The MKMapViewDelegate protocol provides methods for map events. mapView:didUpdateUserLocation: is called when the user's location updates. To zoom the map, create an MKCoordinateRegion with a center coordinate and span in meters.
🔑 Definition — MKCoordinateRegionMakeWithDistance: Creates a coordinate region centered on a point with specified width and height in meters.
📌 Example: Zooming to user location:
- (void)mapView:(MKMapView *)mv didUpdateUserLocation:(MKUserLocation *)u {
CLLocationCoordinate2D loc = [u coordinate];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(loc, 250, 250);
[worldView setRegion:region animated:YES];
}
MKAnnotation Protocol and MapPoint Class
The MKAnnotation protocol provides an interface for annotating the map. A custom class (MapPoint) conforms to this protocol with required properties like coordinate and optional properties like title.
🔑 Definition — MKAnnotation: A protocol for objects that can be displayed as annotations on a map view.
📌 Example: Creating a custom annotation class:
@interface MapPoint : NSObject <MKAnnotation> {
NSString *title;
CLLocationCoordinate2D coordinate;
}
- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t;
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@end
Adding Annotations and Location Finding
The complete workflow for adding a location annotation involves:
- Finding location using CLLocationManager
- Creating a MapPoint with coordinates and title
- Adding the annotation to the MKMapView
- Cleaning up allocations
📌 Example: Complete location finding and annotation:
- (void)foundLocation:(CLLocation *)loc {
CLLocationCoordinate2D coord = [loc coordinate];
MapPoint *mp = [[MapPoint alloc] initWithCoordinate:coord
title:[locationTitleField text]];
[worldView addAnnotation:mp];
[mp release];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(coord, 250, 250);
[worldView setRegion:region animated:YES];
[locationTitleField setText:@""];
[activityIndicator stopAnimating];
[locationTitleField setHidden:NO];
[locationManager stopUpdatingLocation];
}
Handling Cached Location Data
CLLocationManager may return cached location data from previous sessions. To filter this out, check the timestamp property of the location and ignore data older than a threshold (e.g., 3 minutes = -180 seconds).
📌 Example: Filtering cached location data:
NSTimeInterval t = [[newLocation timestamp] timeIntervalSinceNow];
if (t < -180) {
// This is cached data, keep looking
return;
}
[self foundLocation:newLocation];
⭐ Key Takeaways
Core Location provides CLLocationManager for geographical positioning with distanceFilter and desiredAccuracy properties controlling update frequency and precision. The delegation pattern is central to iOS development, where delegates receive callback messages like didUpdateToLocation and didFailWithError through protocols that define optional and required methods. Memory management requires delegates to use weak references (assign) to avoid retain cycles, and the delegate should be set to nil before release. The MKMapView displays maps and can show user location via the showsUserLocation property, with region zooming through MKCoordinateRegionMakeWithDistance. Custom annotations conform to the MKAnnotation protocol with required coordinate property and optional title, and location timestamps must be checked to filter out old cached data (typically ignoring locations older than 3 minutes).
🧠 Quick Revision Questions
- What are the two key properties of CLLocationManager that control how frequently location updates are received?
- Why must delegates use weak references (assign) rather than strong references (retain)?
- How do you check if a delegate implements a particular method before calling it?
- What is the purpose of checking the timestamp when receiving new location data from CLLocationManager?
- Which two protocols must a class adopt to both receive location updates and act as a map view delegate?
📘 Lecture 44 — Touch Events, Drawing App, Blocks & GCD
📖 Overview: This lecture covers touch event handling for iOS mobile devices, demonstrating how to build a drawing app using touch event methods. It then introduces Blocks (anonymous functions) in Objective-C for dynamic color computation, and Grand Central Dispatch (GCD) for concurrent programming and thread management.
🗂️ Topics Covered
Touch events (began, moved, ended, cancelled) and UITouch objects; building a drawing app with Line model and TouchDrawView; handling single and multi-touch, double tap, and memory management; using Blocks for dynamic color calculation based on line angle and length; motion event handling for shake gesture; Grand Central Dispatch (GCD) for thread pools and concurrent execution; callback to UI thread using dispatch_async.
📝 Lecture Summary
Touch Events
Touch events are the hallmark of mobile devices. A finger or fingers touching the screen triggers touchesBegan:withEvent:. When a finger moves across the screen, touchesMoved:withEvent: is sent repeatedly. When a finger is removed, touchesEnded:withEvent: is called. If a system event (like an incoming phone call) interrupts a touch before it ends, touchesCancelled:withEvent: fires.
Events are added to the event queue. A UITouch object is created and tracked for each finger. A set of UITouches is passed – one per finger. Only the moving, beginning, or ending event is passed.
💡 Why this matters: Understanding the touch event lifecycle is fundamental for building interactive mobile applications.
Building the Drawing App – Data Model
The Line class stores two CGPoint values: begin and end, representing the start and end points of a drawn stroke.
@interface Line: NSObject{
CGPoint begin;
CGPoint end;
}
@property (nonatomic) CGPoint begin;
@property (nonatomic) CGPoint end;
@end
Building the Drawing App – TouchDrawView
The TouchDrawView is a UIView subclass that manages drawing. It has two collections:
- linesInProcess (NSMutableDictionary): maps UITouch keys to currently being drawn lines
- completeLines (NSMutableArray): stores finished lines
In initWithCoder:, both collections are allocated and setMultipleTouchEnabled:YES enables multi-touch support.
In clearAll, both collections are cleared and setNeedsDisplay triggers a redraw.
The drawRect: method uses UIGraphicsGetCurrentContext() to get the graphics context, sets line width to 10.0 and line cap to kCGLineCapRound. Complete lines are drawn in black; lines in process are drawn in red.
Touch Event Handling in the Drawing App
In touchesBegan:withEvent:, for each UITouch:
- If
[t tapCount] > 1(double tap),clearAllis called and method returns - The touch object is wrapped in an
NSValueto use as dictionary key - A new Line is created with
beginandendboth set to the touch location - The line is added to
linesInProcess
⚠️ Note: There is a memory leak in this method (to be found using Instruments in next chapter).
In touchesMoved:withEvent:, for each touch, the corresponding line’s end point is updated to the new location, then setNeedsDisplay redraws.
In touchesEnded:withEvent: and touchesCancelled:withEvent:, both call endTouches: helper method.
The endTouches: method:
- For each touch, retrieves the line from
linesInProcess - If line exists (not nil from double tap), adds it to
completeLinesand removes fromlinesInProcess - Calls
setNeedsDisplay
Using Blocks for Dynamic Colors
The Line class is extended with a *UIColor color property. In drawRect:, instead of using a fixed color, the code calls [[line color] set] for complete lines.
The method transformLineColorsWithBlock: takes a block parameter (UIColor* (^)(Line *))colorForLine. It iterates over completeLines, calls the block to compute a color, and sets it on each line.
The colorize method creates a block variable named colorScheme:
UIColor* (^colorScheme)(Line *) = ^(Line *l){
float dx = [l end].x - [l begin].x;
float dy = [l end].y - [l begin].y;
// If dx is near zero, red = 1.0, otherwise use slope
float r = (fabs(dx) < 0.001 ? 1.0 : fabs(dy/dx));
// If dy is near zero, green = 1.0, otherwise use inverse slope
float g = (fabs(dy) < 0.001 ? 1.0 : fabs(dx/dy));
// blue = length over 300
float b = hypot(dx, dy) / 300.0;
return [UIColor colorWithRed:r green:g blue:b alpha:1];
};
[self transformLineColorsWithBlock:colorScheme];
🔑 Block — An anonymous function in Objective-C that captures variables by value, similar to anonymous functions in C#. Blocks are an alternative to callbacks and are used with GCD.
Shake Gesture – Motion Events
To respond to shaking:
- Override
canBecomeFirstResponderto returnYES - In
didMoveToWindow, call[self becomeFirstResponder] - Override
motionBegan:withEvent:to call[self colorize]
Grand Central Dispatch (GCD)
GCD is a technology for managing concurrent code execution using dispatch queues — pools of threads managed by the system. Blocks are used with GCD for task-like execution.
Traditional threading using NSThread:
- (void) calculationThreadEntry{
@autoreleasepool {
NSUInteger counter = 0;
while ([[NSThread currentThread] isCancelled] == NO){
[self doCalculation];
counter++;
if (counter >= 1000) break;
}
}
}
- (BOOL)application:didFinishLaunchingWithOptions: {
[NSThread detachNewThreadSelector:@selector(calculationThreadEntry)
toTarget:self
withObject:nil];
// ...
}
Calling back on UI thread with GCD:
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^(void) {
[[[UIAlertView alloc] initWithTitle:@"GCD"
message:@"GCD is amazing!"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil, nil] show];
});
Using concurrent queue with synchronous dispatch:
void (^printFrom1To1000)(void) = ^{
NSUInteger counter = 0;
for (counter = 1; counter <= 1000; counter++){
NSLog(@"Counter = %lu - Thread = %@",
(unsigned long)counter,
[NSThread currentThread]);
}
};
dispatch_queue_t concurrentQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(concurrentQueue, printFrom1To1000);
dispatch_sync(concurrentQueue, printFrom1To1000);
🔑 dispatch_async — Schedules a block for asynchronous execution on a dispatch queue (returns immediately). 🔑 dispatch_sync — Schedules a block for synchronous execution on a dispatch queue (waits until block completes). 🔑 dispatch_get_main_queue() — Returns the serial dispatch queue associated with the application’s main thread. 🔑 dispatch_get_global_queue() — Returns a concurrent dispatch queue with specified priority.
⭐ Key Takeaways
Touch events form the foundation of mobile interaction; the four touch methods (began, moved, ended, cancelled) must be understood to handle user input properly. The drawing app demonstrates how to manage touch state using a dictionary for in-progress lines and an array for completed lines, with memory management being a critical concern. Blocks provide a powerful way to encapsulate functional behavior, such as dynamic color computation based on geometric properties of lines. GCD offers a clean, block-based API for concurrent programming, replacing traditional threading with dispatch queues. Finally, always update UI on the main thread using dispatch_async(dispatch_get_main_queue(), ^{...}) to avoid crashes and unpredictable behavior.
🧠 Quick Revision Questions
- What are the four touch event methods and when is each called?
- How does the drawing app distinguish between lines that are currently being drawn and completed lines?
- Why is the touch object wrapped in an NSValue when used as a dictionary key?
- In the
colorizemethod, how is the red, green, and blue component computed based on line geometry? - What is the difference between
dispatch_asyncanddispatch_syncwhen working with GCD queues?
📘 Lecture 45 — Let's download an image asynchronously.
📖 Overview: This lecture demonstrates asynchronous image downloading using Grand Central Dispatch (GCD) in iOS development. It covers thread management, concurrent queues, and UI updates, followed by generating, storing, sorting random numbers, and a comprehensive course review.
🗂️ Topics Covered
The lecture covers asynchronous image downloading with dispatch queues, concurrent and main queue synchronization, file management for storing random numbers, generating 10,000 random numbers, sorting them using blocks, and concludes with a full course overview spanning from C++ message loops through C#, WPF, XAML, threading, JavaScript, jQuery, Ajax, JSON, and Objective-C.
📝 Lecture Summary
Let's download an image asynchronously.
The lecture begins by demonstrating how to download an image asynchronously using Grand Central Dispatch (GCD). This approach prevents the user interface from freezing during network operations.
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
The code creates a concurrent queue with default priority. Inside a dispatch_async block, it creates a __block UIImage variable. A dispatch_sync on the concurrent queue handles the actual download using NSURLConnection to fetch image data from Apple's website. After downloading, another dispatch_sync on the main queue (dispatch_get_main_queue()) updates the UI by creating a UIImageView and adding it to the view controller's view.
🔑 Definition — dispatch_async: Submits a block for asynchronous execution on a dispatch queue, returning immediately without waiting for the block to complete.
🔑 Definition — dispatch_sync: Submits a block for synchronous execution on a dispatch queue, blocking the current thread until the block completes.
📐 Formula/Pattern: dispatch_async(background_queue, ^{ dispatch_sync(background_queue, { /* work */ }); dispatch_sync(main_queue, { /* update UI */ }); });
📌 Example: The code downloads an iPad image from http://images.apple.com/mobileme/features/images/ipad_findyouripad_20100518.jpg. It creates an NSURL from the string, sends a synchronous request using [NSURLConnection sendSynchronousRequest:], and stores the data as a UIImage. On the main queue, it creates a UIImageView with the downloaded image and adds it to self.view.
💡 Why this matters: This pattern ensures network operations don't block the UI thread, while all UI updates happen safely on the main thread.
Generate 10k random numbers if needed. Read 10K random numbers, sort and display. UITableView.
This section demonstrates file management and data processing using GCD. The fileLocation method creates a path in the documents directory for a file named list.txt. The hasFileAlreadyBeenCreated method checks if this file exists using NSFileManager.
- (NSString *) fileLocation {
NSArray *folders = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
if ([folders count] == 0) { return nil; }
NSString *documentsFolder = [folders objectAtIndex:0];
return [documentsFolder stringByAppendingPathComponent:@"list.txt"];
}
Inside a dispatch_async block on the concurrent queue, the code checks if the file exists. If not, it generates 10,000 random numbers using arc4random(), stores them in an NSMutableArray, and writes the array to disk using writeToFile:atomically:. A subsequent dispatch_sync reads the file, loads the array, and sorts it in ascending order using sortUsingComparator: with a block that compares NSNumber objects using compare:.
🔑 Definition — arc4random: A function that generates cryptographically secure random numbers, returning an unsigned integer.
🔑 Definition — sortUsingComparator: An NSMutableArray method that sorts elements using a block that returns an NSComparisonResult.
📌 Example: The code generates 10,000 random numbers using arc4random() % ((unsigned int)RAND_MAX + 1), wraps each in [NSNumber numberWithUnsignedInt:], and stores them in an array. It writes this array to list.txt. Later, it reads the file with initWithContentsOfFile:, then sorts using [randomNumbers sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { return [((NSNumber *)obj1) compare:((NSNumber *)obj2)]; }].
Dispatch after dispatches after a delay. Timers. Dependencies. Group of tasks.
This section briefly mentions additional GCD features including dispatch_after (for delayed execution), timers, dependencies between blocks, and dispatch groups for managing task groups.
Course Overview: Where did we start from. Where to go from here.
The course overview summarizes the entire semester, covering:
- Started with handling multiple input sources in C++, message loop refactoring, events, event processing, source/target/event object, Visual Studio and C# language features, OOP examples in C#
- Discussed delegates, events, exception handling, attributes, collections, XML documents, WPF history and XAML, property elements and markup extensions, mixing XAML and procedural code
- Discussed logical and visual trees, dependency properties, change notifications, property value inheritance, attached properties, sizing/positioning/transforming elements
- Discussed transforms, panels (StackPanel, WrapPanel, Canvas, DockPanel, Grid), content overflow, clipping, scaling, scrolling
- Discussed events, input events, attached events, touch events (manipulation - high level), commands, persisting and restoring
- Covered resources (binary and logical, static vs dynamic), data binding (binding object, binding markup extension, binding to collections, implicit DataContext), DataTemplates and value converters, customizing collection view (sorting, filtering, grouping, navigating), data providers (XML and Object Data Providers)
- Concurrency and threads, captured variables, synchronization context and tasks, continuations, task completion source, sync vs async
- Coarse-grained vs fine-grained sync, async/await keywords in C# 5.0, parallelism, cancellation and progress reporting, task combinators and Task Parallel Library (Parallel.Invoke, For, ForEach), concurrent collections
- JavaScript history and jQuery library, HTML/CSS/JS, client-side vs server-side, DOM, jQuery selectors and filters, changing attributes and elements, events and animations, Ajax, XMLHttpRequest (GET PUT LOAD), JSON
- Mobile development, Objective-C history, call syntax and OO concepts, properties, retain count and memory management, Xib/Nib, IBOutlet, IBAction, Interface Builder
- Button events and a QA app, protocols and delegates, location and map kits, touch events and blocks, GCD and multithreading
⭐ Key Takeaways
- Always perform network operations (like image downloading) on a background concurrent queue using
dispatch_asyncto avoid blocking the UI, then update the UI on the main queue usingdispatch_syncordispatch_async. - When downloading data, create
__blockvariables to make them mutable inside block contexts, and always handle error cases (no data, download error, successful data). - For file persistence, use
NSSearchPathForDirectoriesInDomainsto get the documents directory, and usewriteToFile:atomically:for saving arrays andinitWithContentsOfFile:for reading them. - Sort collections using
sortUsingComparator:with block-based comparison logic, and usearc4random()for generating random numbers in Objective-C. - The course progression goes from C++ fundamentals through C#/WPF/XRML, threading/async patterns, JavaScript/jQuery/Ajax, to Objective-C/iOS development with GCD for multithreading.
🧠 Quick Revision Questions
- Why must you use
dispatch_sync(dispatch_get_main_queue(), ...)when updating the UI after an asynchronous download? - What is the purpose of the
__blockstorage qualifier in the image downloading example? - How does the code determine whether it needs to generate new random numbers or read existing ones from disk?
- What method is used to sort the random numbers array, and how does the comparator block work?
- List three GCD features mentioned beyond
dispatch_asyncanddispatch_sync.