Showing posts with label WPF. Show all posts
Showing posts with label WPF. Show all posts

February 18, 2014

Routing Events to Commands in WPF using MVVM Light

First reference some assemblies:
  • System.Windows.Interactivity
  • GalaSoft.MvvmLight.Extras.WPF4
In the XAML file add references to the namespaces in the window definition:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
Within you window component, add the event to command handler's
<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <cmd:EventToCommand Command="{Binding Mode=OneWay, Path=LoadedCommand}"
                        PassEventArgsToCommand="True" />
    </i:EventTrigger>
    <i:EventTrigger EventName="Closing">
        <cmd:EventToCommand Command="{Binding Mode=OneWay, Path=ClosingCommand}"
                        PassEventArgsToCommand="True" />
    </i:EventTrigger>
</i:Interaction.Triggers>
Then some code to process the command:
private RelayCommand<RoutedEventArgs> loadedCmd;

public RelayCommand<RoutedEventArgs> LoadedCommand
{
  get
  {
    return this.loadedCmd ?? (this.loadedCmd = new RelayCommand<RoutedEventArgs>
    ((rea) => 
    {
      // Use event argument 'rea' if you need it 
      // (but PassEventArgsToCommand="True" is needed in the Xaml, see above)
      this.ActivityText = "Copy files from \'" +
         m_AsyncPhotoBackup.TargetDirectory +
         "\' to \'" +
         m_AsyncPhotoBackup.DestinationDirectory +
         "\'.";
      m_AsyncPhotoBackup.RunWorkerAsync();
    }));
  }
}

Closing a Dialog Window in WPF using MVVM

Alot of ideas are mentioned on this website:
http://stackoverflow.com/questions/4376475/wpf-mvvm-how-to-close-a-window

Simplest way is to simply define the close button as normal using the click event
<Button Content="OK" IsDefault="True" Click="okButton_Click" />
Here is the code behind:
private void okButton_Click(object sender, RoutedEventArgs e)
{
 this.Close();
}
This does not affect the ViewModel in any way, the ViewModel knows nothing about the close functionality. and there is nothing wrong with code behind if it does not affect the ViewModel.
However I wanted to be able to initiate the Close from the ViewModel. To do this I defined a property that takes a simple Action delegate
public Action CloseAction { get; set; }
In the constructor I give it a default value
this.CloseAction = () => 
    { Debug.WriteLine("ActivePhotoBackupViewModel - Close Action Undefined"); )};
Now I can use a command to perform the CloseAction
#region Done Command
private RelayCommand doneCommand;

public RelayCommand DoneCommand
{
 get
 {
  return this.doneCommand ?? 
    (this.doneCommand = new RelayCommand(() => 
     {
      DoSomeOtherStuffHere();
      this.CloseAction();
     }
    ));
 }
}
#endregion Done Command
When creating the view model the close action is defined:
...
ActivePhotoBackupViewModel vm = new ActivePhotoBackupViewModel(srcDir, destDir, photoBackupOptions);
PhotoBackupDlg photoBackupDlg = new PhotoBackupDlg();
vm.CloseAction = new Action(() => photoBackupDlg.Close());
...
Could have a used an interface here on a class to perform the same work but then the interface would have had one method so it is simpler just to use a delegate instead ('Action' is a system defined delegate). Also there were no future extra requirements envisaged on this interface (otherwise the interface route would have been worth it). This simple solution does not impede any unit testing of the ViewModel either.

September 23, 2013

Using the Windows 7 shell Taskbar Item to show that a WPF application is busy/idle

Add the following to the Main window XAML:
    <Window.TaskbarItemInfo>
        <TaskbarItemInfo />
    </Window.TaskbarItemInfo>
In code: To make the task bar icon pulse green to show that it is busy but when the application cannot determine how far through the proceesing the app is:
this.TaskbarItemInfo.ProgressState = TaskbarItemProgressState.Indeterminate;
To stop the task bar icon pulsating green/return it to normal:
this.TaskbarItemInfo.ProgressState = TaskbarItemProgressState.None;
To show progress through the task bar icon:
this.TaskbarItemInfo.ProgressState = TaskbarItemProgressState.Normal;
// ProgressValue must be a value between 0.0 and 1.0
this.TaskbarItemInfo.ProgressValue = progressPercentage/100.0d; 

August 16, 2013

An Animated WPF Gif

The Image control in WPF does not support animated GIF files by default. However, there is a library on codeplex, here, that can be used to do this.
Here is my usage of this library for an animated busy indicator.
In the XAML, first of all create an XML namespace for the library:
xmlns:gif="http://wpfanimatedgif.codeplex.com"
then use it with an image control:
<Image Name="imgBusy" 
       Stretch="UniformToFill" VerticalAlignment="Top" Width="20"  
    gif:ImageBehavior.AnimatedSource="/ScriptRunnerWPF;component/Resources/busyIndicator.gif" 
    gif:ImageBehavior.AutoStart="False" />
In code the animation of the GIF can be controlled:
Start the animation
   
this.imgBusy.Visibility = System.Windows.Visibility.Visible;
ImageAnimationController iac = ImageBehavior.GetAnimationController(this.imgBusy);
if (iac != null)
    iac.Play();
Resume the animation
// Resume the animation (or restart it if it was completed)
ImageAnimationController iac = ImageBehavior.GetAnimationController(this.imgBusy);
if (iac != null)
 iac.Pause();
this.imgBusy.Visibility = System.Windows.Visibility.Hidden;

July 8, 2013

WPF Hyperlink Sample

Here are a couple of examples
WPF Application Hyperlink (in this case handle the RequestNavigate)
<TextBlock>Files found using the given <Hyperlink 
Name="hlRules" 
NavigateUri="Rules" 
RequestNavigate="Hyperlink_RequestNavigate">Rules</Hyperlink>:
</TextBlock>
private void HyperlinkRequestNavigate(object sender, RequestNavigateEventArgs e)
{
   // What to do with a hyperlink navigation request?
   // If it is an internet URI this will invoke the default browser
   // Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));

   // Otherwise show a dialog or whatever you want
   e.Handled = true;
}
Also check this out this link for a reusable hyperlink navigation routine using dependent properties.

How about using a WPF hyperlink in MVVM. Here is how it is done.
In the XAML add the textblock with the hyperlink and bind it to a command ('AboutCommand' in this case). Note in this sample the text of the hyperlink is also bound to some property 'Version'
 <TextBlock>
    <Hyperlink Command="{Binding AboutCommand}"><TextBlock Text="{Binding Version}" /></Hyperlink>
 </TextBlock>
The command is implemented in the ViewModel
private RelayCommand aboutCommand;

public RelayCommand AboutCommand
{
 get
 {
  return aboutCommand ?? (aboutCommand = new RelayCommand(ExecuteAboutCommand));
 }
}

private void ExecuteAboutCommand()
{
 IAboutDialogService aboutDialogService = ServiceLocator.Current.GetInstance<IAboutDialogService>();
 aboutDialogService.ShowAboutDialog(this.Title);
}

April 19, 2012

File Drag and Drop in WPF

Watch drag and drop to text boxes as they need special handling see Textbox Drag/Drop in WPF

Add to your control (or main window) declaration
AllowDrop="True" DragEnter="Window_DragEnter" Drop="Window_Drop"

In the code do something like this (this is a file drag/drop sample):
#region DragDrop

bool IsValidDropData(IDataObject draggedObj)
{
  bool res = false;
  IEnumerable<string> lst = draggedObj.GetData(DataFormats.FileDrop)
     as IEnumerable<string>;
  if (lst != null)
  { 
    // In this sample I am only taking the first file
 // the rest are ignored
    string first = lst.FirstOrDefault();
    if (!string.IsNullOrWhiteSpace(first))
    {
      FileInfo fi = new FileInfo(first);
      res = fi.Exists && HasValidFileExtension(fi);
    }
  }
  return res;
}

private bool HasValidFileExtension(FileInfo file)
{
  string[] validExtensions = new[] { ".xml" };

  bool res = false;
  res = (file != null) && validExtensions.Contains(file.Extension);
  return res;
}

private void Window_DragEnter(object sender, DragEventArgs e)
{
  if (IsValidDropData(e.Data))
  {
    e.Effects = DragDropEffects.Copy;
  }
  else
  {
    e.Effects = DragDropEffects.None;
  }
}

private void Window_Drop(object sender, DragEventArgs e)
{
  if (IsValidDropData(e.Data))
  {
    IEnumerable<string> files = e.Data.GetData(DataFormats.FileDrop)
        as IEnumerable<string>;
    string fileName = files.FirstOrDefault();
    if (!string.IsNullOrEmpty(fileName) && 
     File.Exists(fileName))
    {
       DoSomethingWith(fileName);
    }
  }
}

#endregion DragDrop

March 28, 2012

WPF/Silverlight MVVM

Quick Intro to MVVM A small introduction

Getting Started with the MVVM Pattern in Silverlight Applications

MVVM Light Toolkit
Download it here - There is a couple of videos here as well!

Chapter 5: Implementing the MVVM Pattern
Chapter 6: Advanced MVVM Scenarios - Most introductions only explain the topic in a basic way which does not occur in reality. Here, they show how to deal with the tricksey areas

Simplifying commands in MVVM and WPF - I like the adapted approach proffered by David N. in the comments

Using the BackgroundWorker in a Silverlight MVVM Application - Most of my apps have background worker threads or threading in some manner.

WPF MVVM Commands - new reduced-boilerplate recipe - Not so keen on this approach

March 26, 2012

Custom commands with standard menus

Define the command:
public static class CustomCommands
{
    private static RoutedCommand exitCommand;

    static CustomCommands()
    {
        exitCommand = new RoutedCommand("ExitApplication", typeof(CustomCommands));
    }

    public static RoutedCommand Exit
    {
        get
        {
            return (exitCommand);
        }
    }

}
Define the command in the window bindings:
<Window.CommandBindings>
        <CommandBinding
          Command="{x:Static local:CustomCommands.Exit}"
    Executed="Exit_Execute" 
          CanExecute="Exit_CanExecute" />
    </Window.CommandBindings>
Of course you'll have to define the namespace in the window/page class node:
<Window ...
xmlns:local="clr-namespace:MyNamespace"
... >
Alternatively, define the command in the window constructor:
InitializeComponent();
...
   CommandBindings.Add(
    new CommandBinding(
   CustomCommands.Exit, // this is the command object
   Exit_Execute,      // execute
   Exit_CanExecute));// can it execute?
Define the menu item that will call the command
<MenuItem Header="E_xit" Command="local:CustomCommands.Exit" />
Now define the event handlers that will execute the command
private void Exit_Execute(object sender, ExecutedRoutedEventArgs e)
{
    this.Close();
}

private void Exit_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = CanExitApplication();
    e.Handled = true;
}

Using Application Commands

Define the command binding
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Close"
 Executed="CloseCommandHandler"
 CanExecute="Close_CanExecute"
 /> 
 ...
 </Window.CommandBindings> 
and then define the menu item:
<MenuItem Header="E_xit" Command="ApplicationCommands.Close" />
Again ensure that within the code behind window class that the event handlers are defined:
private void Close_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
    e.Handled = true;
}

private void Close_Execute(object sender, ExecutedRoutedEventArgs e)
{
    DoClose();
}
Beware of using commands with Context Menus. There is a 'gotcha' here. This is explained here: How to Solve Execution Problems of RoutedCommands in a WPF ContextMenu The simplest solution is to call 'Focus()' on the parent window.

February 17, 2012

WPF/Silverlight Data Binding

WPF Basic Data Binding FAQ - Better explanation than the one below.

WPF Data binding cheat sheet

Data Binding Overview - Basically, the target is the control and the source is the code property. Typically, each binding has these four components: a binding target object, a target property, a binding source, and a path to the value in the binding source to use. For example, if you want to bind the content of a TextBox to the Name property of an Employee object, your target object is the TextBox, the target property is the Text property, the value to use is Name, and the source object is the Employee object.

http://msdn.microsoft.com/en-gb/magazine/cc163299.aspx - Data Binding in WPF. Good introduction. Good short description of Xml data binding (though not 2 way I notice)

http://msdn.microsoft.com/en-us/magazine/cc700358.aspx - Customize Data Display with Data Binding and WPF. Some good stuff on hierarchical data templates and how to use them to fill a tree view using object data binding.

http://www.codeproject.com/Articles/26270/Understanding-WPF-via-ASP-NET - Nice article comparing WPF and ASP.NET data binding etc.

Also look at these related blogs
DisplayMemberpath
Data Templates

Bind "IsEnabled" of one control "cbXXX" to "IsChecked" property of another with name "cbYYY" in Xaml:
... Name="cbXXX" IsEnabled="{Binding ElementName=cbYYY, Path=IsChecked}" ...

February 16, 2012

Data Templates

Taken from Top Ten UI Development Breakthroughs In Windows Presentation Foundation

You don't have to set the data template on a particular control. If you prefer, you can associate a data template with a particular type, indicating that WPF should use the specified template whenever it encounters that particular type as content.
eg
<Window.Resources> 
<DataTemplate DataType="{x:Type local:Person}"> <StackPanel Orientation="Horizontal"> <TextBlock FontWeight="Bold">Name:</TextBlock>
<TextBlock Text="{Binding Name}" Margin="2,0,10,0" /> <TextBlock FontWeight="Bold">
Date of birth:</TextBlock> <TextBlock Text="{Binding DateOfBirth}"Margin="2,0,10,0" /> </StackPanel> </DataTemplate> 
</Window.Resources>
Anything in the window that uses a Person object as its content will pick up this data template by default.

June 28, 2011

Silverlight and WPF relationship

Here is a neat image that succinctly describes the relationship between Silverlight and WPF.
Silverlight versus WPF

I took it from this site but I think it is the best indicator of the relationship between the 2 of them.

Here is another that relates them Windows Phone 7 as well
Silverlight, WPF and Windows Phone 7

January 12, 2011

Simple Pie Chart using WPF Toolkit

Here is more detail
Also look at WPF Toolkit Tutorial – Part 1

A simple pie chart example in that there only 2 wedges in the pie chart!
Add a reference to the WPFToolkit data visualisation assembly ("...\Program Files\WPF Toolkit\v3.5.50211.1\System.Windows.Controls.DataVisualization.Toolkit.dll")

Add following xaml to the window where the pie chart will be placed:
<Window ...
  <!-- First define the namespace for charting -->
  xmlns:charting="clr-namespace:
  System.Windows.Controls.DataVisualization.Charting;
  assembly=System.Windows.Controls.DataVisualization.Toolkit"

  <charting:Chart Name="pieChart">
    <charting:PieSeries ItemsSource="{Binding}" 
      IndependentValueBinding="{Binding Path=Description}"
   DependentValueBinding="{Binding Path=Percentage}"
    />
  </charting:Chart>
Set the Pie chart wedges
void AssignPieChartWedges()
{
    System.IO.DriveInfo cdrive = new System.IO.DriveInfo("C");
    double availPercentage = Math.Round(100.0d * 
        (double)cdrive.TotalFreeSpace / (double)cdrive.TotalSize);

    List<DrivePercentage> dpList = new List<DrivePercentage>();
    dpList.Add(new DrivePercentage() 
    { Percentage=availPercentage, Description="Free" });
    dpList.Add(new DrivePercentage() 
    { Percentage=100.0d-availPercentage, Description="Used" });
    pieChart.DataContext = dpList;
}

October 6, 2010

More Complex Progress Dialog

This sample does a file search asynchronously. At the end of the search the progress dialog pauses and closes itself. Internally a timer is used to initiate this automatic close of the progress dialog. AsynchFileFinder is the BackgroundWorker derived object. Here is th Xaml for the dialog:
<Window x:Class="FindingFilesDlg"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Finding modified files" Height="167" Width="514" Closing="Window_Closing" Loaded="Window_Loaded">
    <Grid>
        <Button Height="23" HorizontalAlignment="Right" Margin="0,0,12,12" Name="butCancel" VerticalAlignment="Bottom" Width="91" Click="butCancelDone_Click">_Cancel</Button>
        <TextBlock Height="21" Margin="12,15,0,0" Name="tbActivity" VerticalAlignment="Top"  Text="Searching for modified files:" HorizontalAlignment="Left" Width="Auto" />
        <ProgressBar Margin="12,61,0,52" Name="progressBar" HorizontalAlignment="Left" Width="427" />
        <TextBlock Margin="0,61,16,52" Name="tbPercentage" Text="0%" HorizontalAlignment="Right" Width="31" TextAlignment="Right" />
    </Grid>
</Window>
Here is the code for the dialog:
public partial class FindingFilesDlg : Window
{
    AsyncFileFinder m_AsyncFileFinder = null;
    DispatcherTimer m_Timer = new DispatcherTimer();
    IEnumerable<FileSystemInfo> m_FilesFound = 
               new FileSystemInfo[0];
    int countDown = 1;
    const int SECOND = 10000000;
    bool m_Completed = false;

    public FindingFilesDlg(string rootDir, FileSearchRules fsr)
    {
        InitializeComponent();
        tbActivity.Text = "Preparing to search \'" +
            rootDir + "\' for modified files:";

        InitialiseTimer();
        InitialiseAsyncFileFinder(rootDir, fsr);
    }

    private void InitialiseAsyncFileFinder(
        string rootDir, 
        FileSearchRules fsr)
    {
        // The BackgroundWorker derived object
        m_AsyncFileFinder = new AsyncFileFinder(rootDir, fsr);
        m_AsyncFileFinder.ProgressChanged +=
            new ProgressChangedEventHandler(
                AsynchFileFinder_ProgressChanged);
        m_AsyncFileFinder.RunWorkerCompleted += new
            RunWorkerCompletedEventHandler(
            AsynchFileFinder_RunWorkerCompleted);
    }

    private void InitialiseTimer()
    {
        // Disable timer
        m_Timer.IsEnabled = false;
        // Set timer event interval
        m_Timer.Interval = new TimeSpan((long)(1 * SECOND));
        // Timer events
        m_Timer.Tick += new EventHandler(timer_Tick);
    }

    public IEnumerable<FileSystemInfo> FilesFound
    {
        get { return m_FilesFound; }
    }

    void AsynchFileFinder_RunWorkerCompleted(
        object sender, 
        RunWorkerCompletedEventArgs e)
    {
        if (!e.Cancelled)
        {
            m_Completed = true;
            m_FilesFound = m_AsyncFileFinder.Files;
            tbActivity.Text = "Finished searching \'" + 
                m_AsyncFileFinder.SearchDirectory + 
                "\', " + m_FilesFound.Count().ToString() + 
                " files found";
            progressBar.Value = progressBar.Maximum;
            tbPercentage.Text = "100%";
            UpdateDoneButton();
            // wait several seconds before closing the dialog
            m_Timer.Start(); 
        }
    }

    void UpdateDoneButton()
    {
        string str = "_Done (" + 
              countDown.ToString() + ")";
        butCancel.Content = str;
    }
    
    bool textUpdated = false;
    void AsynchFileFinder_ProgressChanged(object sender, 
        ProgressChangedEventArgs e)
    {
        Debug.Assert((e.ProgressPercentage >= 0) && 
            (e.ProgressPercentage <= 100));
        progressBar.Value = e.ProgressPercentage;
        tbPercentage.Text = e.ProgressPercentage.ToString() +
                "%";
        if ((e.ProgressPercentage > 1) && !textUpdated)
        {
            textUpdated = true;
            tbActivity.Text = "Searching \'" + 
                m_AsyncFileFinder.SearchDirectory + 
                "\' for modified files (" + 
                m_AsyncFileFinder.NumFilesToSearch.ToString() + 
                " files to search) :";
        }
    }

    private void Window_Closing(object sender, CancelEventArgs e)
    {
        CancelAsyncFileFinder();
        if (m_Timer.IsEnabled)
            StopTimer();
        m_AsyncFileFinder.Dispose();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        m_AsyncFileFinder.RunWorkerAsync();
        tbActivity.Text = "Searching \'" + 
            m_AsyncFileFinder.SearchDirectory + 
            "\' for modified files:";
    }

    // wait several seconds before closing the dialog
    void timer_Tick(object sender, EventArgs e)
    {
        --countDown;
        UpdateDoneButton();
        if (countDown == 0)
        {
            StopTimer();
            DialogResult = m_Completed;
        }
    }

    private void StopTimer()
    {
        m_Timer.Stop();
    }

    private void butCancelDone_Click(object sender, 
          RoutedEventArgs e)
    {
        if (!m_Completed)
        {
            CancelAsyncFileFinder();
            if (m_Timer.IsEnabled)
                StopTimer();
        }
        DialogResult = m_Completed;
    }

    private void CancelAsyncFileFinder()
    {
        if (m_AsyncFileFinder.IsBusy)
            m_AsyncFileFinder.CancelAsync();
    }
}

Here is the code for the background worker object:
internal class AsyncFileFinder : BackgroundWorker
{
    private FileSearchRules FileSearchRules { get; set; }
    string m_RootDir = string.Empty;
    List<System.IO.FileSystemInfo> files = new 
        List<System.IO.FileSystemInfo>();
    int m_NumberFilesToSearch = 0;

    // Pass required info in through constructor, 
    // cant change it whilst a search is underway
    public AsyncFileFinder(string rootDir, 
        FileSearchRules fsr) : base()
    {
        this.WorkerReportsProgress = true;
        this.WorkerSupportsCancellation = true;
        m_RootDir = rootDir;
        FileSearchRules = new FileSearchRules(fsr);
    }

    public string SearchDirectory
    {
        get { return m_RootDir; }
    }

    public IEnumerable<FileSystemInfo> Files
    {
        get
        {
            if (IsBusy) // No access while thread active
                return new List<FileSystemInfo>();
            else
                return files.AsReadOnly();
        }
    }

    // Total number of files that will be checked
    public int NumFilesToSearch
    {
        get
        {
            return m_NumberFilesToSearch;
        }
    }

    protected override void OnDoWork(DoWorkEventArgs e)
    {
        bool cancelled = false;
        double progress = 0;
        double prevProgress = 0;
        files = new List<FileSystemInfo>();
        DirectoryInfo targ = new DirectoryInfo(m_RootDir);
        // Count the number of files to process first
        foreach (System.IO.FileSystemInfo fsi in
            targ.IterateFiles(true))
        {
            if (CancellationPending)
            {
                cancelled = true;
                break;
            }
            
            Interlocked.Increment(ref 
                m_NumberFilesToSearch);
        }
        int count = 0;
        if (!cancelled)
        {
            foreach (FileSystemInfo fsi in 
                targ.IterateFiles(true))
            {
                if (CancellationPending)
                {
                    cancelled = true;
                    break;
                }

                ++count;
                // progress as a percentage
                progress = (100.0 * (double)count /
                    (double)m_NumberFilesToSearch) + 0.5;
                // Report every 1% of progress
                if ((progress - prevProgress) > 1.0)
                {
                    ReportProgress((int)(progress));
                    prevProgress = progress;
                }

                if (FileSearchRules.PassesRules(fsi))
                {
                    files.Add(fsi);
                }
            }
        }
        e.Cancel = cancelled;
    }
 
}

General Purpose Progess Dialog

This is a general purpose progress dialog. It takes a background worker object as a parameter. Here is the Xaml for the dialog:
<Window x:Class="AsyncMessageDlg"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="AsyncMessageDlg" Height="314" Width="750"
         WindowStartupLocation="CenterOwner" 
         Closing="Window_Closing" Loaded="Window_Loaded">
    <Grid>
        <Ellipse Fill="Azure" Height="56" Margin="0,27,120,0" Name="ellipse2" 
        Stroke="Beige" VerticalAlignment="Top" HorizontalAlignment="Right" Width="82" />
        <Ellipse Fill="Azure" Height="56" Margin="120,0,0,20" Name="ellipse1" 
        Stroke="Beige" VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="82" />
        <TextBox Margin="12,35,12,63" Name="textBox" IsReadOnly="True" 
VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" />
        <Button Height="23" Margin="560,0,0,6" VerticalAlignment="Bottom" 
Name="butOK" HorizontalAlignment="Left" Width="75" IsDefault="True" Click="butOK_Click">_Ok</Button>
        <Button Height="23" HorizontalAlignment="Right" Margin="0,0,12,6" 
Name="butCancel" VerticalAlignment="Bottom" Width="75" IsCancel="True" Click="butCancel_Click">_Cancel</Button>
        <Label Height="28" Margin="12,7,12,0" Name="label" 
VerticalAlignment="Top">Label</Label>
        <ProgressBar Height="16" Margin="120,0,181,41" Name="progressBar" 
VerticalAlignment="Bottom" Visibility="Hidden" />
        <TextBlock Height="16" HorizontalAlignment="Right" Margin="0,0,144,41" 
Name="tbProgressPercent" Text="0%" TextAlignment="Right" VerticalAlignment="Bottom" Width="31" Visibility="Hidden" />
    </Grid>
</Window>
The dialog takes a string for the dialog title and a string for the label above the output text window. When the background worker task sends a progress update it tries to convert the UserState object passed in the event to a string which is added to the output window. Here is the code for the dialog:
public partial class AsyncMessageDlg : Window
{
    BackgroundWorker m_WorkerThread = null;
    bool m_Completed = false;

    public AsyncMessageDlg(
        string textBoxTitle,
        string dlgTitle, 
        BackgroundWorker bw)
    {
        Debug.Assert(bw != null);
        InitializeComponent();
        this.Title = dlgTitle;
        this.label.Content = textBoxTitle;
        m_Completed = false;
        InitialiseBackgrounWorker(bw);
    }

    private void InitialiseBackgrounWorker(BackgroundWorker bw)
    {
        Debug.Assert(bw != null);
        Debug.Assert(bw.WorkerSupportsCancellation);
        this.m_WorkerThread = bw;
        //if (m_WorkerThread.WorkerReportsProgress)
        butCancel.IsEnabled = m_WorkerThread.WorkerSupportsCancellation;
        butOK.IsEnabled = false;
        tbProgressPercent.Visibility = Visibility.Visible;
        progressBar.Visibility = Visibility.Visible;

        m_WorkerThread.ProgressChanged += new 
            ProgressChangedEventHandler(
              m_WorkerThread_ProgressChanged);
        m_WorkerThread.RunWorkerCompleted += new 
            RunWorkerCompletedEventHandler(
              m_WorkerThread_RunWorkerCompleted);
    }

    void m_WorkerThread_RunWorkerCompleted(object sender, 
        RunWorkerCompletedEventArgs e)
    {
        if (e.Cancelled)
            return;
        m_Completed = true;
        string result = e.Result as string;
        if (result != null)
        {
            label.Content = result as string;
        }
        progressBar.Value = progressBar.Maximum;
        tbProgressPercent.Text = "100%";
        butOK.IsEnabled = true;
        butCancel.IsEnabled = false;
    }

    void m_WorkerThread_ProgressChanged(object sender, 
        ProgressChangedEventArgs e)
    {
        Debug.Assert((e.ProgressPercentage >= 0) &&
            (e.ProgressPercentage <= 100));
        progressBar.Value = e.ProgressPercentage;
        tbProgressPercent.Text = e.ProgressPercentage.ToString() +
            "%";
        if (e.UserState != null) 
        {
            if (e.UserState is string)
            {
                textBox.Text += e.UserState as string;
            }
            else
            {
                textBox.Text += e.UserState.ToString();
            }
        }
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        m_WorkerThread.RunWorkerAsync();
    }

    private void Window_Closing(object sender, CancelEventArgs e)
    {
        CancelWorkerThread();
        m_WorkerThread.Dispose();
    }

    private void butOK_Click(object sender, RoutedEventArgs e)
    {
        DialogResult = m_Completed;
        this.Close();
    }

    private void butCancel_Click(object sender, RoutedEventArgs e)
    {
        if (!m_Completed)
        {
            CancelWorkerThread();
        }
        DialogResult = m_Completed;
    }

    private void CancelWorkerThread()
    {
        if (m_WorkerThread.IsBusy && 
            m_WorkerThread.WorkerSupportsCancellation)
            m_WorkerThread.CancelAsync();
    }
}
Here is the code for a sample background worker
// Background worker to check out some files
internal class AsynchAddCheckOutFiles : BackgroundWorker
{
    List<FileSystemInfo> m_fileSystemInfoList;

    // Pass required info in through constructor, cant change it
    // whilst a search is underway
    public AsynchAddCheckOutFiles(ref List<FileSystemInfo> fileSystemInfoList) :
        base()
    {
        this.m_fileSystemInfoList = fileSystemInfoList;
        // Set clients refernce to null, we own the list.
        fileSystemInfoList = null; 
        this.WorkerReportsProgress = true;
        this.WorkerSupportsCancellation = true;
    }

    protected override void OnDoWork(DoWorkEventArgs e)
    {
        bool cancelled = false;
        double progress = 0;
        double prevProgress = 0;

        int m_NumberFiles = m_fileSystemInfoList.Count;
        int count = 0;
        if (!cancelled)
        {
            foreach (FileSystemInfo fsi in m_fileSystemInfoList)
            {
                if (CancellationPending)
                {
                    cancelled = true;
                    break;
                }

                Perforce.P4CommandTransaction res = 
                    Perforce.Instance.AddCheckOutFile(fsi);
                string state = res.ToString();

                ++count;
                // progress as a percentage
                progress = (100.0 * (double)count /
                    (double)m_NumberFiles) + 0.5;
                // Report every 1% of progress
                if ((progress - prevProgress) > 1.0)
                {
                    ReportProgress((int)(progress), state);
                    prevProgress = progress;
                }
                
            }
        }
        e.Cancel = cancelled;
    }

}

July 1, 2010

Using Images in WPF

Using project resources in WPF
  1. Create sub-directory for resources called say "Resources"
  2. Add your resources (images, icons etc.) to it.
  3. Copy the folder structure into the VS project and add the resource files to it.
  4. Check the Properties of your resources in VS project file to ensure they have a Build Action of 'Resource'
Creating an image in WPF: In XAML:
<Image Source="Resources/P4.ico" />
Adding an image to a window's resources is very easy:
<Window.Resources>
        <Image x:Key="P4Icon" Source="Resources/P4.ico" />
</Window.Resources>
In code:
Image myImage = new Image();
myImage.Source = new BitmapImage(new Uri("Icons/MyImage.png", UriKind.Relative));
Inserting an image in a button is very easy in WPF:
<Button HorizontalAlignment="Left" Margin="5,8,0,10" Name="p4But" Width="34" Click="p4But_Click">
   <Image Source="Resources/P4.ico" Width="24" />
</Button>

May 12, 2010

Creating A Single Instance WPF Application

Look here: Initial idea. - Did not seem to work!
How can I provide my own Main() method in my WPF application?
and here

Simplest solution found:
  1. On App.xaml build properties, set "Build Action" to Page.
  2. Add following code to "App.xml.cs" or equivalent, the "App" class source.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;

...

[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);

/// <summary>
/// Application Entry Point.
/// </summary>
[System.STAThreadAttribute()]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static void Main()
{
  // Use mutex to ensure only single instance is running
  bool mutexOwnershipGranted = true;
  using (Mutex mutex = new Mutex(
    true, 
    "$SOMEUNIQUESTRING$", // Use GUID or string id here
    out mutexOwnershipGranted))
  {
    // If this is the only running instance
    if (mutexOwnershipGranted) 
    { // Then run app 
      DevHelperWpf.App app = new DevHelperWpf.App();
      app.InitializeComponent();
      app.Run();
    }
    else 
    { // Bring current running instance to the front
      Process current = Process.GetCurrentProcess();
      foreach (Process process in Process.GetProcessesByName(
          current.ProcessName))
      {
         if (process.Id != current.Id)
         {
            SetForegroundWindow(process.MainWindowHandle);
            break;
         }
      }
    }
  }
}

February 16, 2010

Show and ShowDialog in WPF

Show a WPF window in modal form use ShowDialog()
private void ShowXXXDialogModal()
{
    // Instantiate the dialog box
    XXXDlg dlg = new XXXDlg();

    // Configure the dialog box
    dlg.Owner = this;
    // Open the dialog box modally 
    dlg.ShowDialog();
}
Show a WPF window in modaless form (ie dont wait for the opened window to close before returning) use Show()
private void ShowXXXDialogModaless()
{
    // Instantiate the dialog box
    XXXDlg dlg = new XXXDlg();

    // Configure the dialog box
    dlg.Owner = this;
    // Open the dialog box modalessly 
    dlg.Show();
}

January 22, 2010

Hosting WPF Controls In Windows Forms

Host WPF controls in a windows form control (using an ElementHost)
WPF for those who know Windows Forms (Large document)

You must add references to "WindowsBase", "WindowsFormsIntergration", "PresentationCore" and "PresentationFramework"
private ElementHost wpfCtrlHost;
private TestWpfControl testWpfCtrl;

public WindowsFormHost()
{
    InitializeComponent();
 ...
    HostWpfControl();
}

private void HostWpfControl()
{
    wpfCtrlHost = new ElementHost();
    wpfCtrlHost.Dock = DockStyle.Fill;
    this.Controls.Add(wpfCtrlHost);
    testWpfCtrl = new TestWpfControl();
    testWpfCtrl.InitializeComponent();
    wpfCtrlHost.Child = testWpfCtrl;
}

February 5, 2009

Using Application Commands

These are standard application related commands, see here In Xaml:
  <Window.CommandBindings>
    <CommandBinding Command="ApplicationCommands.Close"
                    Executed="CloseCommandHandler"
                    CanExecute="CanExecuteHandler"
                    />
  </Window.CommandBindings>
...
  <Button Command="ApplicationCommands.Close" 
            Content="Close File" />
and in code:
// Executed event handler.
private void CloseCommandHandler(object sender, ExecutedRoutedEventArgs e)
{
    // Calls a method to close the file and release resources.
    CloseFile();
}

// CanExecute event handler.
private void CanExecuteHandler(object sender, CanExecuteRoutedEventArgs e)
{
    // Call a method to determine if there is a file open.
    // If there is a file open, then set CanExecute to true.
    if (IsFileOpened())
    {
        e.CanExecute = true;
    }
    // if there is not a file open, then set CanExecute to false.
    else
    {
        e.CanExecute = false;
    }
}