>Building Silverlight Chord Factory: Part 2 – The ViewModel

>

This is part 2 in my series of posts on building the latest MVVM incarnation of my ChordFactory hobby project; in Part 1 I discussed modelling musical chord and scale data in XML – the Model in the MVVM (Model-View-ViewModel) pattern. In this post I will look at the next part of the pattern – the ViewModel.

Loading the data

In the MVVM pattern, data is held in properties of the ViewModel to allow the Views to data-bind UI elements directly onto those properties. In the ChordFactory application, the mechanics of loading the data from the XML and building the Chords and Scales collections to supply the View-Model are implemented using the Repository pattern with individual repository classes, deriving from a repository base class, for the Chords and the Scales collections. These classes implement private static methods to load their respective data and then surface the collections retrieved via public methods that return ObservableCollections of Chords and Scales respectively.

REPOSITORY BASE CLASS
  1. public class RepositoryBase
  2. {
  3.     protected static Stream GetResourceStream(string resourceFile)
  4.     {
  5.         Uri uri = new Uri(resourceFile, UriKind.RelativeOrAbsolute);
  6.  
  7.         StreamResourceInfo info = Application.GetResourceStream(uri);
  8.         if (info == null || info.Stream == null)
  9.             throw new ArgumentException("Missing resource file: " + resourceFile);
  10.  
  11.         return info.Stream;
  12.     }
  13. }

CHORD REPOSITORY
  1. public class ChordRepository : RepositoryBase
  2. {
  3.     private readonly ObservableCollection<Chord> observableChords = new ObservableCollection<Chord>();
  4.  
  5.     public ChordRepository(string chordDataFile)
  6.     {
  7.         LoadChords(chordDataFile).ForEach(observableChords.Add);
  8.     }
  9.  
  10.     private static List<Chord> LoadChords(string chordDataFile)
  11.     {
  12.         using (Stream stream = GetResourceStream(chordDataFile))
  13.         using (XmlReader xmlRdr = XmlReader.Create(stream))
  14.             return (from chordElem in XDocument.Load(xmlRdr).Element("Chords").Elements("Chord")
  15.                     select
  16.                         Chord.CreateChord((string) chordElem.Element("Description"),
  17.                                           chordElem.Element("NoteList").Elements("NoteIndex").Select(
  18.                                               x => int.Parse(x.Value)).ToList())).ToList();
  19.     }
  20.  
  21.     public ObservableCollection<Chord> GetChords()
  22.     {
  23.         return observableChords;
  24.     }
  25. }

SCALE REPOSITORY
  1. public class ScaleRepository : RepositoryBase
  2. {
  3.     private readonly ObservableCollection<Scale> observableScales = new ObservableCollection<Scale>();
  4.  
  5.     public ScaleRepository(string scaleDataFile)
  6.     {
  7.         LoadScales(scaleDataFile).ForEach(observableScales.Add);
  8.     }
  9.  
  10.     private static List<Scale> LoadScales(string scaleDataFile)
  11.     {
  12.         using (Stream stream = GetResourceStream(scaleDataFile))
  13.         using (XmlReader xmlRdr = XmlReader.Create(stream))
  14.             return (from ScaleElem in XDocument.Load(xmlRdr).Element("Scales").Elements("Scale")
  15.                     select
  16.                         Scale.CreateScale((string)ScaleElem.Element("Description"),
  17.                                           ScaleElem.Element("NoteList").Elements("NoteIndex").Select(
  18.                                               x => int.Parse(x.Value)).ToList())).ToList();
  19.     }
  20.  
  21.     public ObservableCollection<Scale> GetScales()
  22.     {
  23.         return observableScales;
  24.     }
  25. }

Data in the ViewModel

The ViewModel uses the Repositories to load the data and provides it as bindable collections together with other bindable properties such as the currently selected items in the collections and implementation of change notification so that bound UI can respond to updates in the ViewModel.

CHORDS VIEW-MODEL
  1. public class ChordsViewModel : INotifyPropertyChanged
  2. {
  3.     private readonly ObservableCollection<Chord> chords;
  4.     private readonly ObservableCollection<Scale> scales;
  5.     private List<int> selectedChord;
  6.     private List<int> selectedScale;
  7.     private RootNotes rootNote;
  8.     private Inversion inversion;
  9.  
  10.     public event PropertyChangedEventHandler PropertyChanged;
  11.  
  12.     private readonly List<Inversion> inversions = new List<Inversion>
  13.                                                       {
  14.                                                           Inversion.Basic,
  15.                                                           Inversion.First,
  16.                                                           Inversion.Second,
  17.                                                           Inversion.Third,
  18.                                                           Inversion.Fouth
  19.                                                       };
  20.  
  21.     
  22.     public ChordsViewModel()
  23.     {
  24.         chords = new ChordRepository("/Openfeature.ChordFactory;component/Data/chords.xml").GetChords();
  25.         scales = new ScaleRepository("/Openfeature.ChordFactory;component/Data/scales.xml").GetScales();
  26.     }
  27.  
  28.     public ObservableCollection<Chord> Chords
  29.     {
  30.         get { return chords; }
  31.     }
  32.  
  33.     public ObservableCollection<Scale> Scales
  34.     {
  35.         get { return scales; }
  36.     }
  37.  
  38.     public List<Inversion> Inversions { get { return inversions; } }
  39.  
  40.     public List<int> SelectedChord
  41.     {
  42.         get { return selectedChord; }
  43.         private set
  44.         {
  45.             selectedChord = value;
  46.             OnPropertyChanged("SelectedChord");
  47.         }
  48.     }
  49.  
  50.     public List<int> SelectedScale
  51.     {
  52.         get { return selectedScale; }
  53.         private set
  54.         {
  55.             selectedScale = value;
  56.             OnPropertyChanged("SelectedScale");
  57.         }
  58.     }
  59.  
  60.     public Inversion Inversion
  61.     {
  62.         get { return inversion; }
  63.         set
  64.         {
  65.             inversion = value;
  66.             OnPropertyChanged("Inversion");
  67.         }
  68.     }
  69.  
  70.     public RootNotes RootNote
  71.     {
  72.         get { return rootNote; }
  73.         set
  74.         {
  75.             rootNote = value;
  76.             OnPropertyChanged("RootNote");
  77.         }
  78.     }
  79.  
  80.     public void ChordSelectionChanged(object sender, SelectionChangedEventArgs e)
  81.     {
  82.         SelectedChord = ((Chord)e.AddedItems[0]).Notes;
  83.     }
  84.  
  85.     public void InversionSelectionChanged(object sender, SelectionChangedEventArgs e)
  86.     {
  87.         Inversion = (Inversion)e.AddedItems[0];
  88.     }
  89.  
  90.     public void RootNoteChanged(object sender, SelectionChangedEventArgs e)
  91.     {
  92.         RootNote = (RootNotes)e.AddedItems[0];
  93.     }
  94.  
  95.     public void ScaleSelectionChanged(object sender, SelectionChangedEventArgs e)
  96.     {
  97.         SelectedScale = ((Scale)e.AddedItems[0]).Notes;
  98.     }
  99.  
  100.     private void OnPropertyChanged(string propertyName)
  101.     {
  102.         if (PropertyChanged != null)
  103.         {
  104.             PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  105.         }
  106.     }
  107. }

The Chords and Scales collection properties of the ViewModel are bound to UI elements, (initially ComboBoxes – UI/UX enhancement will have to come later), and their SelectionChanged events wired back to the ViewModel using the CallDataMethod behaviour from the excellent Expression Blend Samples on Codeplex:

COLLECTION PROPERTIES BINDINGS
  1. <StackPanel x:Name="BoundData" Orientation="Horizontal" Margin="10,10,10,20" >
  2.     <ComboBox x:Name="ChordsList" ItemsSource="{Binding Chords}" DisplayMemberPath="Description" Margin="10,0">
  3.         <i:Interaction.Triggers>
  4.             <i:EventTrigger EventName="SelectionChanged">
  5.                 <si:CallDataMethod Method="ChordSelectionChanged" />
  6.             </i:EventTrigger>
  7.         </i:Interaction.Triggers>
  8.     </ComboBox>
  9.     <ComboBox x:Name="ScalesList" ItemsSource="{Binding Scales}" DisplayMemberPath="Description" Margin="10,0">
  10.         <i:Interaction.Triggers>
  11.             <i:EventTrigger EventName="SelectionChanged">
  12.                 <si:CallDataMethod Method="ScaleSelectionChanged" />
  13.             </i:EventTrigger>
  14.         </i:Interaction.Triggers>
  15.     </ComboBox>
  16.     <ComboBox x:Name="InversionsList" Margin="10,0" ItemsSource="{Binding Inversions}">
  17.         <i:Interaction.Triggers>
  18.             <i:EventTrigger EventName="SelectionChanged">
  19.                 <si:CallDataMethod Method="InversionSelectionChanged" />
  20.             </i:EventTrigger>
  21.         </i:Interaction.Triggers>
  22.     </ComboBox>
  23. </StackPanel>

The ViewModel handles selection changes and sets its SelectedChord and SelectedScale properties appropriately. The piano keyboard which displays the notes from the selected chord and scale is written as a Silverlight Control and it too has SelectedChord and SelectedScale properties; these bind to the properties on the ViewModel with the same name. The keyboard control also responds to left-mouse clicks in order to allow the selection of the root note of the chord or scale.

So now I have my data loaded and in a bindable ViewModel, creating a user interface in XAML to represent it to the user of the Silverlight ChordFactory is next, together with some stuff about testing. That’s for part 3.

Posted in .NET, C#, Chord, CodePlex, Music, MVVM, Piano, Scale, Silverlight, XAML | Leave a comment

>Building Silverlight Chord Factory: Part 1 – The Data Model

>Back in November 2008 I posted about converting my reference app Chord Factory to Silverlight – Chord Factory is a piano chord finder utility that I wrote years ago and I have been porting from one development platform to the next as a series of protracted learning exercises ever since. It started out as a VB6 WinForms app and has been through various .NET incarnations since. The Silverlight version went through partially complete Silverlight 2.0 and 3.0 versions in a fairly monolithic architecture and I always meant to produce a few posts about the code once I had it complete and had a published version on my website.
But before I had managed that (see the most recent incomplete version here) I became interested in overhauling the component design and re-architecting it as another learning exercise, this time to help me work through the concepts of producing loosely-coupled component designs using MVC/MVVM patterns and looking at concerns like unit testing & testability together with an attempt at expressing the data structures involved in XML.
I long since gave up trying to make the UI pretty – whilst I think I can distinguish good UI/UX from bad I am no designer, but I have managed to produce something that fits some of the MVVM principles and allowed me to understand how MVVM works and makes for testable components and clean data-binding. And before I get distracted again and feel the urge to overhaul the code again and douse everything in MEF, it is time for those long-delayed posts.

Representing note sequences as data

Without straying too much into music theory, chords and musical scales are basically sequences of notes – the 8 notes in a scale (e.g. the scale of C Major: C-D-E-F-G-A-B-C) or the 3, 4 or more notes in a chord (e.g. the chord of C Major: C-E-G) can be stored and positions or offsets in a sequence. However the progressive sequence is measured in pitch-steps of semitones from the starting note i.e. including the black notes (looking at the piano keyboard, the semitone sequence starting from the note C is: C, C#, D, Eb, E, F, F#, G, Ab, A, B, C, C#, D, Eb… and so on up the keyboard). So to store the sequence for the scale of C Major we need to store offsets for the 1st, 3rd, 5th, 6th, 8th, 10th, 11th and 12th semitones, and for the chord of C Major we need to store the offsets for the 1st, 5th and 7th. With this information for a scale or a chord we have the means to select or highlight on the UI representation of our piano keyboard the notes that make it up, starting at the base or root note – C in the previous examples.
We can now store these sequences of offsets for all the standard scales and chords that musicians have come up with and use them to produce representations of any variation from a particular root note. An XML schema for these sequences is simple to produce: 

CHORD DATA SCHEMA
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <xs:schema targetNamespace="http://tempuri.org/XMLSchema.xsd"
  3.            elementFormDefault="qualified"
  4.            xmlns="http://tempuri.org/XMLSchema.xsd"
  5.            xmlns:mstns="http://tempuri.org/XMLSchema.xsd"
  6.            xmlns:xs="http://www.w3.org/2001/XMLSchema">
  7.   <xs:element name="ChordFactoryData">
  8.     <xs:complexType>
  9.       <xs:sequence>
  10.         <xs:element name="Chords" type="Chords" />
  11.         <xs:element name="Scales" type="Scales" />
  12.       </xs:sequence>
  13.     </xs:complexType>
  14.   </xs:element>
  15.   <xs:complexType name="Chords">
  16.     <xs:sequence>
  17.       <xs:element name="Chord" minOccurs="1" maxOccurs="unbounded" type="NoteSequence" />
  18.     </xs:sequence>
  19.   </xs:complexType>
  20.   <xs:complexType name="Scales">
  21.     <xs:sequence>
  22.       <xs:element name="Scale" minOccurs="1" maxOccurs="unbounded" type="NoteSequence" />
  23.     </xs:sequence>
  24.   </xs:complexType>
  25.   <xs:complexType name="NoteSequence">
  26.     <xs:sequence>
  27.       <xs:element name="SequenceType" type="SequenceType" maxOccurs="1" minOccurs="1" />
  28.       <xs:element name="Description" type="xs:string" maxOccurs="1" minOccurs="1" />
  29.       <xs:element name="NoteList" type="NoteList" maxOccurs="1" minOccurs="1" />
  30.     </xs:sequence>
  31.   </xs:complexType>
  32.   <xs:complexType name="NoteList">
  33.     <xs:sequence>
  34.       <xs:element name="NoteIndex" type="xs:int" minOccurs="1" maxOccurs="unbounded" />
  35.     </xs:sequence>
  36.   </xs:complexType>
  37.   <xs:simpleType name="SequenceType">
  38.     <xs:restriction base="xs:string">
  39.       <xs:enumeration value="Chord" />
  40.       <xs:enumeration value="Scale" />
  41.     </xs:restriction>
  42.   </xs:simpleType>
  43. </xs:schema>

 

The XML for a Major Chord would then look like this:

MAJOR CHORD
  1. <chords>
  2.   <chord>
  3.     <sequencetype>Chord</sequencetype>
  4.     <description>Major</description>
  5.     <notelist>
  6.       <noteindex>0</noteindex>
  7.       <noteindex>4</noteindex>
  8.       <noteindex>7</noteindex>
  9.     </notelist>
  10.   </chord>
  11.  
  12.   …more chords…
  13.  
  14. </chords>

 

and a Major Scale would look like this:

MAJOR SCALE
  1. <scales>
  2.   <scale>
  3.     <sequencetype>Scale</sequencetype>
  4.     <description>Major</description>
  5.     <notelist>
  6.       <noteindex>0</noteindex>
  7.       <noteindex>2</noteindex>
  8.       <noteindex>4</noteindex>
  9.       <noteindex>5</noteindex>
  10.       <noteindex>7</noteindex>
  11.       <noteindex>9</noteindex>
  12.       <noteindex>11</noteindex>
  13.       <noteindex>12</noteindex>
  14.     </notelist>
  15.   </scale>
  16.  
  17.   …more scales…
  18.  
  19. </scales>

 

Data Classes

In my MVVM implementation this Chord and Scale data is loaded into models that represent the Chord or the Scale and I can then produce collections of instances of all the Chords and Scales. Here are the simple models in C#:

DATA MODEL CLASSES
  1. public abstract class NoteSequence
  2. {
  3.     public List<int> Notes { get; set; }
  4.     public string Description { get; set; }
  5. }
  6.  
  7. public class Chord : NoteSequence
  8. {
  9.     public static Chord CreateNewChord()
  10.     {
  11.         return new Chord();
  12.     }
  13.  
  14.     public static Chord CreateChord(string description, List<int> notes)
  15.     {
  16.         return new Chord
  17.         {
  18.             Notes = notes,
  19.             Description = description
  20.         };
  21.     }
  22.  
  23.     protected Chord() { }
  24. }
  25.  
  26. public class Scale : NoteSequence
  27. {
  28.     public static Scale CreateNewScale()
  29.     {
  30.         return new Scale();
  31.     }
  32.  
  33.     public static Scale CreateScale(string description, List<int> notes)
  34.     {
  35.         return new Scale
  36.         {
  37.             Notes = notes,
  38.             Description = description
  39.         };
  40.     }
  41.  
  42.     protected Scale() { }
  43. }

In implementation, the Chord and Scale classes are no different and it could be argued that the respective data could be loaded into two collections of the more general NoteSequence class (which would have to be changed from its current abstract incarnation of course); but I prefer to keep them as separate classes as they are logically different and might diverge in the future.

So I now represent my chord and scale data as XML and I have a straightforward model for instantiating it in data objects. The next stage is to load that data into a ViewModel using data access code that will deserialize the XML into the objects. That will be in part 2.

Posted in .NET, C#, Chord, Music, MVVM, Piano, Silverlight, silverlight 3, XAML | Leave a comment

>Five Minute Silverlight 4 Aides-Memoire #7 – IDataErrorInfo

>

The XAML:

DATA INPUT
  1. <Grid x:Name="LayoutRoot">
  2.   <Grid.Resources>
  3.     <local:Person x:Key="NewJoiner" Age="49" Forename="Fred" Surname="Flintstone" />
  4.   </Grid.Resources>
  5.   <Grid DataContext="{StaticResource NewJoiner}">
  6.     <Grid.ColumnDefinitions>
  7.       <columndefinition Width="1*" />
  8.       <columndefinition Width="5*" />
  9.     </Grid.ColumnDefinitions>
  10.     <Grid.RowDefinitions>
  11.       <rowdefinition Height="50" />
  12.       <rowdefinition Height="50" />
  13.       <rowdefinition Height="50" />
  14.     </Grid.RowDefinitions>
  15.     <Grid.Resources>
  16.       <Style TargetType="TextBlock">
  17.         <setter Property="Margin" Value="10" />
  18.         <setter Property="HorizontalAlignment" Value="Right" />
  19.         <setter Property="VerticalAlignment" Value="Center" />
  20.       </Style>
  21.       <Style TargetType="TextBox">
  22.         <setter Property="Margin" Value="10" />
  23.         <setter Property="Width" Value="154" />
  24.         <setter Property="HorizontalAlignment" Value="Left" />
  25.         <setter Property="VerticalAlignment" Value="Center" />
  26.       </Style>
  27.     </Grid.Resources>
  28.     <TextBlock Text="First Name" Grid.Row="0" Grid.Column="0" />
  29.     <TextBlock Text="Last Name" Grid.Row="1" Grid.Column="0" />
  30.     <TextBlock Text="Age" Grid.Row="2" Grid.Column="0" />
  31.     <textbox x:Name="Forename" Grid.Row="0" Grid.Column="1" Text="{Binding Forename, Mode=TwoWay, ValidatesOnDataErrors=True}" />
  32.     <textbox x:Name="Surname" Grid.Row="1" Grid.Column="1" Text="{Binding Surname, Mode=TwoWay, ValidatesOnDataErrors=True}" />
  33.     <textbox x:Name="Age" Grid.Row="2" Grid.Column="1" Text="{Binding Age, Mode=TwoWay, ValidatesOnDataErrors=True}" />
  34.   </Grid>
  35. </Grid>

The code:

IMPLEMENTATION
  1. public class Person : IDataErrorInfo
  2. {
  3.     public string Forename { get; set; }
  4.     public string Surname { get; set; }
  5.     public int Age { get; set; }
  6.     public string Error { get { return null; } }
  7.  
  8.     public string this[string columnName]
  9.     {
  10.         get
  11.         {
  12.             string error = null;
  13.             switch (columnName)
  14.             {
  15.                 case "Forename":
  16.                     if (string.IsNullOrEmpty(Forename))
  17.                         error = "Forename required";
  18.                     break;
  19.                 case "Surname":
  20.                     if (string.IsNullOrEmpty(this.Surname))
  21.                         error = "Surname required";
  22.                     break;
  23.                 case "Age":
  24.                     if (this.Age < 0 || this.Age > 130)
  25.                         error = "Invalid Age";
  26.                     break;
  27.             }
  28.             return error;
  29.         }
  30.     }
  31. }

The result:

image

Posted in .NET, C#, IDataErrorInfo, Silverlight, Silverlight 4 Beta, XAML | Leave a comment

>Five Minute Silverlight 4 Aides-Memoire #6 – Right Mouse Event Support

>

<Popup x:Name="PopupMenu" IsOpen="False">
<
ListBox x:Name="MenuItems">
<
ListBoxItem Content="Item 1" />
<
ListBoxItem Content="Item 2" />
<
ListBoxItem Content="Item 3" />
<
ListBoxItem Content="Item 4" />
</
ListBox>
</
Popup>
public MainPage()
{
InitializeComponent();
MouseRightButtonDown += (s, e) => e.Handled = true;
MouseRightButtonUp += MainPage_MouseRightButtonUp;
MenuItems.MouseLeftButtonUp += MenuItems_MouseLeftButtonUp;
}
void MainPage_MouseRightButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
this.PopupMenu.HorizontalOffset = e.GetPosition(this).X;
this.PopupMenu.VerticalOffset = e.GetPosition(this).Y;
this.PopupMenu.IsOpen = true;
}

void MenuItems_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
PopupMenu.IsOpen = false;
System.Diagnostics.Debug.WriteLine(((sender as ListBox).SelectedItem as ListBoxItem).Content);
}

Posted in .NET, C#, Right Mouse Event, Silverlight 4 Beta | Leave a comment

>Five Minute Silverlight 4 Aides-Memoire #5 – Rich Text Area Control

>

<RichTextArea x:Name="Editor">
<
RichTextArea.Blocks>
<
Paragraph>
<
Run Foreground="Blue" Text="This is "/>
<
Run Foreground="Red" FontWeight="Bold" Text="some sample " />
<
Run FontStyle="Italic" Text="text" />
</
Paragraph>
<
Paragraph>
<
Run Text="Here is "/>
<
Run TextDecorations="Underline" Text="some more" />
</
Paragraph>
</
RichTextArea.Blocks>
</
RichTextArea>

void UnderlineSelection_Click(object sender, RoutedEventArgs e)
{
var currentValue = this.Editor.Selection.GetPropertyValue(TextElement.TextDecorationsProperty);
TextDecorationCollection setValue = TextDecorations.Underline;
if (currentValue != DependencyProperty.UnsetValue)
setValue = (TextDecorationCollection)currentValue == setValue ? null : setValue;
this.Editor.Selection.SetPropertyValue(TextElement.TextDecorationsProperty, setValue);
}

void ItalicSelection_Click(object sender, RoutedEventArgs e)
{
var currentValue = this.Editor.Selection.GetPropertyValue(TextElement.FontStyleProperty);
FontStyle setValue = FontStyles.Italic;
FontStyle defaultValue = FontStyles.Normal;
if (currentValue != DependencyProperty.UnsetValue)
setValue = (FontStyle)currentValue == setValue ? defaultValue : setValue;
this.Editor.Selection.SetPropertyValue(TextElement.FontStyleProperty, setValue);
}

void BoldSelection_Click(object sender, RoutedEventArgs e)
{
var currentValue = this.Editor.Selection.GetPropertyValue(TextElement.FontWeightProperty);
FontWeight setValue = FontWeights.Bold;
FontWeight defaultValue = FontWeights.Normal;
if (currentValue != DependencyProperty.UnsetValue)
setValue = (FontWeight)currentValue == setValue ? defaultValue : setValue;
this.Editor.Selection.SetPropertyValue(TextElement.FontWeightProperty, setValue);
}

image

Posted in .NET, C#, RichTextArea, Silverlight 4 Beta | Leave a comment

>Five Minute Silverlight 4 Aides-Memoire #4 – Clipboard Access

>

private void PopButton_Click(object sender, RoutedEventArgs e)
{
var newPara = new Paragraph();

newPara.Inlines.Add(new Run { Text = Clipboard.GetText() });

this.rightRichTextArea.Blocks.Add(newPara);
}

private void PushButton_Click(object sender, RoutedEventArgs e)
{
Clipboard.SetText(this.leftRichTextArea.Selection.Text);
}

Posted in .NET, C#, Clipboard, Silverlight 4 Beta | Leave a comment

>Five Minute Silverlight 4 Aides-Memoire #3 – WebCam/Microphone Device Support

>

<UserControl.Resources>
<
Style x:Key="ListBoxStyle" TargetType="ListBox">
<
Setter Property="ItemTemplate">
<
Setter.Value>
<
DataTemplate>
<
Image Margin="5" Source="{Binding}" Stretch="UniformToFill" Height="80" VerticalAlignment="Center"/>
</
DataTemplate>
</
Setter.Value>
</
Setter>
<
Setter Property="ItemsPanel">
<
Setter.Value>
<
ItemsPanelTemplate>
<
StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</
ItemsPanelTemplate>
</
Setter.Value>
</
Setter>
</
Style>
</
UserControl.Resources>

<StackPanel Grid.Row="2" Grid.Column="0">
<
Button Content="Start WebCam" Click="StartWebCam_Click" Height="30" />
<
Button Content="Snapshot" Click="Snapshot_Click" Height="30" />
</
StackPanel>
<
Rectangle Grid.Row="0" Stretch="Fill" Grid.Column="1" x:Name="CapturedImage" />
<ListBox Grid.Row="1" Margin="10" Grid.RowSpan="2" Grid.Column="1" x:Name="Snapshots" Style="{StaticResource ListBoxStyle}"/>
private void StartWebCam_Click(object sender, RoutedEventArgs e)
{
if (!CaptureDeviceConfiguration.AllowedDeviceAccess & !CaptureDeviceConfiguration.RequestDeviceAccess())
return;

VideoBrush videoBrush = new VideoBrush();
videoBrush.SetSource(captureSource);
CapturedImage.Fill = videoBrush;
this.Snapshots.ItemsSource = snaps;
captureSource.Start();
}
private void Snapshot_Click(object sender, RoutedEventArgs e)
{
captureSource.AsyncCaptureImage((image) => { snaps.Add(image); });
}
Posted in .NET, C#, Silverlight, Silverlight 4 Beta, WebCam | Leave a comment

>Five Minute Silverlight 4 Aides-Memoire #2 – Printing

>

private void Print_Click(object sender, RoutedEventArgs e)
{
PrintDocument printDoc = new PrintDocument();

// Name that will show in the spooler...
printDoc.DocumentName = "Directory Tree";
printDoc.PrintPage += new EventHandler<PrintPageEventArgs>(printDoc_PrintPage);
printDoc.Print();
}

private void printDoc_PrintPage(object sender, PrintPageEventArgs e)
{
// Needs a UIElement (visual tree)
e.PageVisual = DirectoriesTreeView;
    // Setting true will ensure PrintPage is called again for subsequent pages
e.HasMorePages = false;
}
Posted in .NET, C#, Printing, Silverlight, Silverlight 4 Beta | Leave a comment

>Five Minute Silverlight 4 Aides-Memoire #1 – Drag and Drop

>

Build a hierarchical treeview of files and directories dropped from a Windows Explorer window. Requires elevated trust running out of browser to avoid security exceptions when accessing the directory information in the FileInfo objects returned from the e.Data.GetData call.

image

<Grid x:Name="LayoutRoot" AllowDrop="True" Drop="LayoutRoot_Drop">
<
my:TreeView x:Name="DirectoriesTreeView" Margin="50" Width="500" />
</
Grid>

private void LayoutRoot_Drop(object sender, DragEventArgs e)
{
if (!Application.Current.HasElevatedPermissions e.Data == null)
return;

FileInfo[] fileInfos = e.Data.GetData(DataFormats.FileDrop) as FileInfo[];
if (fileInfos == null)
return;
List<FileInfo> sortedList = new List<FileInfo>(fileInfos);

sortedList.Sort((x, y) =>
{
if (x.Attributes == FileAttributes.Directory && y.Attributes != FileAttributes.Directory)
return -1;
if (y.Attributes == FileAttributes.Directory && x.Attributes != FileAttributes.Directory)
return 1;
return x.Name.CompareTo(y.Name);
});

foreach (FileInfo fileInfo in sortedList)
{
var newNode = new TreeViewItem { Header = fileInfo.Name };
DirectoriesTreeView.Items.Add(newNode);
if (!fileInfo.Exists)
ProcessDirectory(fileInfo, newNode);
}
}
private static void ProcessDirectory(FileSystemInfo fileSystemInfo, TreeViewItem currentNode)
{
DirectoryInfo directoryInfo = new DirectoryInfo(fileSystemInfo.FullName);

foreach (FileSystemInfo childFileSystemInfo in directoryInfo.EnumerateFileSystemInfos())
{
var newNode = new TreeViewItem { Header = childFileSystemInfo.Name };
currentNode.Items.Add(newNode);
if (childFileSystemInfo.Attributes == FileAttributes.Directory)
ProcessDirectory(childFileSystemInfo, newNode);
}
}

Posted in .NET, C#, Drag and Drop, Out of Browser, Silverlight, Silverlight 4 Beta, XAML | Leave a comment

>Silverlight 4

>Quite unexpectedly, at least I wasn’t expecting it, today saw the announcement of the first beta of Silverlight 4 at PDC09 in Los Angeles. Barely has version 3 made it out there, but we have a new beta to play with together with another rev of Blend to support it (and .NET 4 in general).
A quick run through the main features in the beta:

  • Printing – Full access to the regular print facilities of the host machine – the print dialog and print preview features – so long to all the writable bitmaps and HTML bridge hacks
  • Webcam/microphone support – Subject to UAC/user confirmation, you can now programmatically get access to connected devices, capture video, stills and audio input streams
  • A Rich TextBox control – the WPF RTF control makes its appearance in Silverlight – embed other UI Elements, control text Runs and other formatting such as Text Decorations
  • Clipboard Access – Programmatic access to the clipboard – again subject to UAC
  • Right Mouse Context Menus – Full control of a customisable context menu – add whatever you want to it and respond to selection events
  • Drag-Drop Drop-Target support Silverlight apps can now act as drag-drop drop-targets receiving file information for the dropped file object(s)
  • MVVM Commanding – Finally proper implementation of the ICommand interface – no longer a need to kludge it via bindings to helper classes
  • IDataErrorInfo/Validation support – Integration of the support for the IDataErrorInfo interface in Silverlight’s DataBinding giving a validation framework for input controls to leverage in consistently surfacing data input errors with asynchronous data validation
  • String Formatting in Binding Extensions – As with WPF, Silverlight has been retrofitted with improved formatting support in the binding extension to give better control over stuff like date, time and other formatting for locales and long and short formatting
  • Out of Browser improvements: HTML content – Hosting of HTML content within the Silverlight app; Notifications (aka toast) support; Elevated Trust (again via UAC) access to the My Documents, My Videos, My Pictures special folders; Cross domain access and Full keyboard access in Full Screen mode
  • Better tooling and integration of RIA/ADO Services/WCF/MEF

Lots to check out and try out – exciting stuff!

Posted in .NET, C#, Clipboard, Drag and Drop, MVVM, Out of Browser, Printing, RichTextArea, Silverlight, Silverlight 4 Beta, WebCam | Leave a comment