Quintic
WPF Notes

ViewModels

Must implement INotifyPropertyChange

   public class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string _configurationFile;

        void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            // Eliminates Race condition
            var localPC = PropertyChanged;
            if (localPC != null)
            {
                localPC(this, new PropertyChangedEventArgs(propertyName));
            }
        }
     }

Label Alignment

Use HorizontalContentAlignment  & VerticalContentAlignment to align the text in a label

Prism, Commands & ListBox Selected Items

With ListBoxes you often need to perform an action on the list of selected items. This usually involves a Button being click to indicate – Execute Action.

Question is with MVVM how do you access the Selected Items? The ViewModel knows about the Collection that the list box is displaying, but that Collection object does not have a ‘SelectedItems’ property.

The answer is to add a CommandParameter to the Button pointing to the SelectedItems property of the ListBox. eg

<ListBox x:Name="MyCollectionLbx" ItemsSource="{Binding MyCollection}" 
         SelectionMode="Multiple"></ListBox>

<Button x:Name="DeleteItemsBtn"
    Command="{Binding DeleteItemsCommand}"
    CommandParameter="{Binding ElementName=MyCollectionLbx,
                        Path=SelectedItems}">Delete</Button>

This will pass the SelectedItems of ListBox MyCollectionLbx as an ICollection<object> to the CanExecute and Execute methods of the ICommand DeleteItemsCommand

However, should you be using Prism’s DelegateCommand then the question is how do you construct the delegate commands to accept CommandParameters? Answer:

 DeleteItemsCommand = new DelegateCommand<ICollection<object>>
                    (ExecuteDeleteItems, CanExecuteDeleteItems);
 

        public bool CanExecuteDeleteItems(ICollection<object> selectedItems)
        {
            return true;
        }

        public void ExecuteDeleteItems(ICollection<object> selectedItems)
        {
           // Action code here
            IList<object> copy = selectedItems.ToList<object>();
            foreach (var item in copy)
            {
                FrequencyList.Remove((double)item);
            }
        }

SelectedItems are returned as an ICollection<object>

Note: The SelectedItems are returned as an ICollection<object>, so you do have to cast the items to their Type, which is a shame.

Modifying the Original Collection, modifies the SelectedItems Collection

Any modification to the original collection in the Command Execute function (i.e. the ExecuteDeleteItems function above) will also change the selectedItems collection.
This is why, in the ExecuteDeleteItems function above, we first take a copy of the SelectedItems collection, and then iterate over the copy to delete the selected entries from the original collection.

Creating New Controls

WPF provicdes three mechanisms for creating New Controls

  1. Modify presentation and behaviour of standard control
  2. User Controls
  3. Custom Controls

Modify presentation and behaviour of standard control

User Controls

Example of a File Selection User Control.

File Selection Control

The control provides for:

  • A Label to indicate the purpose of the control
  • A TextBox into which the user can enter a File path
  • A Button to trigger a File Selection Dialog as a Model Dialogue window. Results of which are written to the TextBox

The behaviour we have is:

  • The Label is designated the Content Proptery of the Control. Hence it is populated by <fsc>Content<fsc>
  • The Textbox is bound to the FileName property and can be pre-populated via that Property and the its value will be returned to whatever is bound to that Property.
<UserControl x:Class="C_V_App.UserControls.FileSelectionControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:C_V_App.UserControls"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800"
             x:Name="FileSelectionUserControl">
  <Grid Margin="0,2,0,2">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <Label x:Name="FileDialogLbl" Grid.Column="0" MinWidth="190" HorizontalAlignment="Right" HorizontalContentAlignment="Right"/>
    <TextBox x:Name="FileDialogTbx" Grid.Column="1" Margin="2,0,2,0" MinWidth="375" 
             VerticalContentAlignment="Center" HorizontalAlignment="Left"
             Text="{Binding FileName, ElementName=FileSelectionUserControl, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></TextBox>
    <Button x:Name="FileDialogBtn" Grid.Column="2" Click="FileDialogBtn_OnClick" >Browse...</Button>
  </Grid>
</UserControl>

Code-Behind. I realise that having code behind is deemed blasphomous by some. I agree for the most part, but for me it is not a matter of rigourously following dogma. My opinion is that the FileSelectionDialog displayed by the Browse… button is a UI Instrument for populating the TextBox. Its display and control is the responsibility of the View. It is most definitely not the responsibility of the ViewModel.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using Microsoft.Win32;

namespace C_V_App.UserControls
{
    /// <summary>
    /// Interaction logic for FileSelectionControl.xaml
    /// </summary>
    /// 
    [ContentProperty ("Label")]
    public partial class FileSelectionControl : UserControl
    {
        public static readonly DependencyProperty FileNameProperty =
            DependencyProperty.Register("FileName", typeof(string), typeof(FileSelectionControl));

        public event EventHandler<EventArgs> FileNameChanged;

        public FileSelectionControl()
        {
            FileDialogue = new OpenFileDialog();
            InitializeComponent();
            FileDialogTbx.TextChanged += new TextChangedEventHandler(OnFileNameChanged);
        }

        private OpenFileDialog FileDialogue { get; set; }

        public void FileDialogBtn_OnClick (object sender, RoutedEventArgs args)
        {
            if (FileDialogue.ShowDialog() == true)
            {
                FileName = FileDialogue.FileName;
            }
        }

        public string Label
        {
            get { return this.FileDialogLbl.Content.ToString(); }
            set { this.FileDialogLbl.Content = value; }
        }

        public string FileName
        {
            get { return (string)GetValue(FileNameProperty); }
            set { SetValue(FileNameProperty, value); }
        }

        public void OnFileNameChanged (object sender, TextChangedEventArgs args)
        {
            var local = FileNameChanged;
            if (local != null)
            {
                local(this, args);
            }
        }
    }
}

Consuming User Control

<ucs:FileSelectionControl x:Name="KeithleyUsc" 
        FileName ="{Binding Path=KeithleyEmulationFile, Mode=TwoWay}">
        Keithley2400 Emulation File:
</ucs:FileSelectionControl>

FileName is a TwoWay binding

Notice that FileName binding is two-way.
Notice the Label is derived from the controls content

Custom Controls

Status Bar

A Status bar is basically like a menu, (ie a container that accepts Separators), except its items are displayed horizontally.

Data Binding

Data Binding to a Property of an Object that is a Property of Data Source

The data binding syntax is one of those things where you just have to learn and remember it. You cannot (IMHO) decern what it should be from a logoical interptretation of lexical rules.

As an example (as the heading suggests), I have a Data Source, lets call it College. College has a Property call Principle and Principle has (at least) two Properties FirstName & LastName.

If I have a view whose DataContext is College, what is the syntax to Bind to the Principle’s First and Last Name. I could, simply expose these Properties as Properties of the College, and that would work, up to the point where the Principle, and hence her/his name changes.

What is the correct syntax to cover this:

<TextBox Text="{Binding Principle.FirstName}" />