Using BackgroundWorker in a WPF Application

Just sharing a small app that demonstrates the use of BackgroundWorker in WPF.

The app has two list boxes, one act as source of all the Process that needs to be processed and the other list box for the running and completed Processes.

The click of Start button starts the processing of Process.

Clicking on Cancel aborts the thread.

The XAML for the app.


<Window x:Class="BackgroundWorkerSampleApp.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="MainWindow" Height="324" Width="525" Loaded="Window_Loaded">
 <Grid Height="289">
 <ListBox Height="187" HorizontalAlignment="Left" Margin="20,52,0,0" Name="lstSource" VerticalAlignment="Top" Width="171"
 />
 <Button Content="Start" Height="41" HorizontalAlignment="Left" Margin="212,101,0,0" Name="btnStart" VerticalAlignment="Top" Width="75" Click="btnStart_Click" />
 <Button Content="Cancel" Height="42" HorizontalAlignment="Left" Margin="212,148,0,0" Name="btnCancel" VerticalAlignment="Top" Width="75" ContentStringFormat="Cancel" Click="btnCancel_Click" />
 <ListBox Height="187" HorizontalAlignment="Left" Margin="320,52,0,0" Name="lstDestination" VerticalAlignment="Top" Width="171" />
 <TextBlock Height="25" HorizontalAlignment="Left" Margin="195,252,0,0" Name="txtStatus" Text="" VerticalAlignment="Top" Width="232" />
 </Grid>
</Window>

The code behind for the app.


namespace BackgroundWorkerSampleApp
{
 using System.Collections.ObjectModel;
 using System.ComponentModel;
 using System.Threading;
 using System.Windows;

/// <summary>
 /// Interaction logic for MainWindow.xaml
 /// </summary>
 public partial class MainWindow : Window
 {
 #region Constants and Fields

private readonly BackgroundWorker backgroundWorker;

private readonly ObservableCollection<Process> obcProcess;

private readonly ObservableCollection<Process> obcRunningProcess;

private Thread workerThread;

#endregion

#region Constructors and Destructors

public MainWindow()
 {
 this.InitializeComponent();
 this.obcProcess = new ObservableCollection<Process>();
 this.obcRunningProcess = new ObservableCollection<Process>();
 this.lstSource.ItemsSource = this.obcProcess;
 this.lstDestination.ItemsSource = this.obcRunningProcess;
 this.lstSource.DisplayMemberPath = "ProcessName";
 this.lstDestination.DisplayMemberPath = "ProcessName";

// intialize BackgroundWorker class properties
 this.backgroundWorker = new BackgroundWorker();
 this.backgroundWorker.DoWork += this.backgroundWorker_DoWork;
 this.backgroundWorker.RunWorkerCompleted += this.backgroundWorker_RunWorkerCompleted;
 }

#endregion

#region Methods

/// <summary>
 /// Executes the process.
 /// </summary>
 /// <param name="backgroundWorker">The background worker.</param>
 private void ExecuteProcess(BackgroundWorker backgroundWorker)
 {
 if (this.obcProcess.Count > 0)
 {
 this.obcRunningProcess.Add(this.obcProcess[0]);
 backgroundWorker.RunWorkerAsync(this.obcProcess[0]);
 this.obcProcess.RemoveAt(0);
 }
 }

/// <summary>
 /// Handles the Loaded event of the Window control.
 /// </summary>
 /// <param name="sender">The source of the event.</param>
 /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
 private void Window_Loaded(object sender, RoutedEventArgs e)
 {
 var proc1 = new Process();
 proc1.ProcessName = "Process 1";

var proc2 = new Process();
 proc2.ProcessName = "Process 2";

var proc3 = new Process();
 proc3.ProcessName = "Process 3";

this.obcProcess.Add(proc1);
 this.obcProcess.Add(proc2);
 this.obcProcess.Add(proc3);
 }

/// <summary>
 /// Handles the DoWork event of the backgroundWorker control.
 /// </summary>
 /// <param name="sender">The source of the event.</param>
 /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
 private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
 {
 this.workerThread = Thread.CurrentThread;
 var process = e.Argument as Process;
 Thread.Sleep(5000);
 process.ProcessName = process.ProcessName + " Processed";
 }

/// <summary>
 /// Handles the RunWorkerCompleted event of the backgroundWorker control.
 /// </summary>
 /// <param name="sender">The source of the event.</param>
 /// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
 private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
 {
 if (e.Error != null)
 {
 MessageBox.Show(e.Error.Message);
 }
 else if (e.Cancelled)
 {
 MessageBox.Show("Cancelled");
 }
 else
 {
 if (this.obcProcess.Count > 0)
 {
 this.obcRunningProcess.Add(this.obcProcess[0]);
 this.backgroundWorker.RunWorkerAsync(this.obcProcess[0]);
 this.obcProcess.RemoveAt(0);
 }
 else
 {
 this.txtStatus.Text = "Processing Completed";
 }
 }
 }

/// <summary>
 /// Handles the Click event of the btnCancel control.
 /// </summary>
 /// <param name="sender">The source of the event.</param>
 /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
 private void btnCancel_Click(object sender, RoutedEventArgs e)
 {
 this.workerThread.Abort();
 this.txtStatus.Text = "Processing Aborted";
 }

/// <summary>
 /// Handles the Click event of the btnStart control.
 /// </summary>
 /// <param name="sender">The source of the event.</param>
 /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
 private void btnStart_Click(object sender, RoutedEventArgs e)
 {
 this.ExecuteProcess(this.backgroundWorker);
 }

#endregion
 }
}

Hope it helps.

Refresh ObservableCollection in WPF

ObservableCollection only provided notifications when we make any change in the collection itself like Adding or Removing an Item.

If we update any properties of a contained class, it doesn’t raise any notifications.

For e.g.

If I have an ObservableCollection of Person object and it is bind to a TreeView. If I add or remove person object from the ObservableCollection I will be able to see the changes in my TreeView also.

Here Person object has only one string property FullName

Now if I click on Change Name button, which simply renames the selected Person, I will not be able to see the change in my TreeView.

Here in this case we need to call the Refresh method of CollectionViewSource.


CollectionViewSource.GetDefaultView(this.observableCollPerson).Refresh();

https://skydrive.live.com/redir.aspx?cid=2312e1103cbe5ecd&resid=2312E1103CBE5ECD!339&parid=root

Have a look at this solution as well

http://blog.falafel.com/blogs/11-01-30/Using_CollectionViewSource_without_Refresh_for_Faster_Filtering_in_Silverlight

Hope it helps.

Adding a Close Context Menu to TabItem in WPF

Continuing the previous post

https://nishantrana.wordpress.com/2012/04/08/creating-dynamic-tabitem-in-wpf/

let us add the Close Context menu to our TabItem.

To do this we need to add the Context Menu tag to the DataTemplate defined for the Tab.

</pre>
<TabControl Height="271" HorizontalAlignment="Left" Margin="123,9,0,0" Name="mainTabControl" VerticalAlignment="Top" Width="441"
 IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding ObservableCollectionTabItems}" Background="White">
 <TabControl.ItemTemplate>
 <DataTemplate>
 <TextBlock Text="{Binding Header}" >
 <TextBlock.ContextMenu>
 <ContextMenu>
 <MenuItem Header="Close" Click="MenuItem_Click">
 </MenuItem>
 </ContextMenu>
 </TextBlock.ContextMenu>
 </TextBlock>
 </DataTemplate>
 </TabControl.ItemTemplate>
 </TabControl>
<pre>

Add the following code to the ViewModel class for the MainWindow.


public void CloseTabItem(Object sender)
 {
 VMParentForViews vmParentForViews = (sender as MenuItem).DataContext as VMParentForViews;
 this.ObservableCollectionTabItems.Remove(vmParentForViews);
 }

Call the above method from the code behind of the MainWindow’s MenuItem_Click event

This is how the Close menu looks like, clicking on it closes the tab item. (i.e. removes it from the ObservableCollection to which tab control is bind)

The sample application link

https://skydrive.live.com/redir.aspx?cid=2312e1103cbe5ecd&resid=2312E1103CBE5ECD!338&parid=root

Hope it helps

Creating dynamic TabItem in WPF.

Hi,

We recently had a requirement in our project to create dynamic TabItems

something similar to below screen shot.

Here every time when we click on Add View One and Add View Two buttons a new TabItem gets added to the tab control on the right side showing its respective views.

The project structure is like this

Two views, ViewTypeOne and ViewTypeTwo with a simple textblock in them.

Two ViewModel for those views, one for the main window and the fourth one VMParentForView which is the parent of our other two view models.

VMParentForView class

The TabControl in the main window is bind to the ObservableCollection of VMParentForViews viewmodel and the Textblock to the Header property of Views.


<TabControl Height=”271″ HorizontalAlignment=”Left” Margin=”123,9,0,0″ Name=”mainTabControl” VerticalAlignment=”Top” Width=”441″ 
IsSynchronizedWithCurrentItem=”True” ItemsSource=”{Binding ObservableCollectionTabItems}” Background=”White”>

<TabControl.ItemTemplate>

<DataTemplate>

<TextBlock Text=”{Binding Header}” ></TextBlock>

</DataTemplate>

</TabControl.ItemTemplate>

</TabControl>

Here is the sample code of the demo application.

https://skydrive.live.com/redir.aspx?cid=2312e1103cbe5ecd&resid=2312E1103CBE5ECD!337&parid=root

Hope it helps.

Creating dynamic ContextMenu for TreeView in WPF

Hi,

Sharing a simple example wherein we are generating dynamic menuitems for the context menu based on the node or treeviewitem selected in the treeview.

If root Packages is selected –

If Package is selected –

If Batch is selected –

The xml file

<?xml version="1.0" encoding="utf-8" ?>
<Packages>
 <Package Name="Package 1" Type="Package">
 <Batch Name="Batch 1" Type="Batch"/>
 <Batch Name="Batch 2" Type="Batch"/>
 </Package>
 <Package Name="Package 2" Type="Package">
 <Batch Name="Batch 1" Type="Batch"/>
 <Batch Name="Batch 2" Type="Batch"/>
 </Package>
</Packages>

XAML

<Window x:Class="SampleSyncServerAdminTool.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="MainWindow" Height="350" Width="525">
 <Window.Resources>
 <!--Binding TreeView-->
 <XmlDataProvider x:Key="MyList" Source="Packages.xml" XPath="Packages"/>

<HierarchicalDataTemplate DataType="Packages" ItemsSource="{Binding XPath=*}">
 <TextBlock Text="Packages"></TextBlock>
 </HierarchicalDataTemplate>

<HierarchicalDataTemplate DataType="Package" ItemsSource="{Binding XPath=*}">
 <TextBlock Text="{Binding XPath=@Name}"></TextBlock>
 </HierarchicalDataTemplate>

<HierarchicalDataTemplate DataType="Batch" ItemsSource="{Binding XPath=*}">
 <TextBlock Text="{Binding XPath=@Name}"></TextBlock>
 </HierarchicalDataTemplate>

<!-- Resources for Right Hand Side detail grid-->

<DataTemplate x:Key="ClosableTabItemTemplate">
 <DockPanel Width="120">
 <Button
 Command="{Binding Path=CloseCommand}"
 Content="X"
 Cursor="Hand"
 DockPanel.Dock="Right"
 Focusable="False"
 FontFamily="Courier"
 FontSize="9"
 FontWeight="Bold"
 Margin="0,1,0,0"
 Padding="0"
 VerticalContentAlignment="Bottom"
 Width="16" Height="16"
 />
 <ContentPresenter
 Content="{Binding Path=DisplayName}"
 VerticalAlignment="Center"
 />
 </DockPanel>
 </DataTemplate>

<DataTemplate x:Key="WorkspacesTemplate">
 <TabControl
 IsSynchronizedWithCurrentItem="True"
 ItemsSource="{Binding}"
 ItemTemplate="{StaticResource ClosableTabItemTemplate}"
 Margin="4"
 />
 </DataTemplate>

</Window.Resources>
 <Grid>
 <DockPanel>
 <TreeView DockPanel.Dock="Left" Width="150" Name="treeViewPackages"

 ItemsSource="{Binding Source={StaticResource MyList}}">
 <TreeView.Resources>
 <ContextMenu x:Key="TestMenu">
 </ContextMenu>
 </TreeView.Resources>
 <TreeView.ItemContainerStyle>
 <Style TargetType="{x:Type TreeViewItem}">
 <Setter Property="IsExpanded" Value="True"/>
 <EventSetter Event="PreviewMouseRightButtonDown"
 Handler="OnPreviewMouseRightButtonDown" />
 </Style> </TreeView.ItemContainerStyle>
 </TreeView>
 <Grid DockPanel.Dock="Right">
 <HeaderedContentControl
 Content="{Binding Path=Workspaces}"
 ContentTemplate="{StaticResource WorkspacesTemplate}"
 Header="Workspaces"
 />
 </Grid>
 </DockPanel>
 </Grid>
</Window>

XAML.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml;

namespace SampleSyncServerAdminTool
{
 /// <summary>
 /// Interaction logic for MainWindow.xaml
 /// </summary>
 public partial class MainWindow : Window
 {
 public MainWindow()
 {
 InitializeComponent();
 }

void OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
 {

DependencyObject obj = e.OriginalSource as DependencyObject;
 TreeViewItem item = GetDependencyObjectFromVisualTree(obj, typeof(TreeViewItem)) as TreeViewItem;
 XmlElement selectedElement = (XmlElement)item.Header;

string header = selectedElement.Name;
 if (header.ToUpper() == "PACKAGES")
 {
 // Packages root node
 MenuItem mnuItem = new MenuItem();
 mnuItem.Header = "New Package";
 ContextMenu menu = new ContextMenu() { };
 menu.Items.Add(mnuItem);
 (sender as TreeViewItem).ContextMenu = menu;
 }

else
 {
 string attName = selectedElement.Attributes["Name"].Value;
 string type = selectedElement.Attributes["Type"].Value;

string fullNodeInfo = "Header: " + header + " Attribute Name: " + attName + " Type: " + type;

if (type.ToUpper() == "PACKAGE")
 {
 MenuItem mnuItem1 = new MenuItem();
 mnuItem1.Header = "New Package";
 MenuItem mnuItem2 = new MenuItem();
 mnuItem2.Header = "Show Package Details";
 MenuItem mnuItem3 = new MenuItem();
 mnuItem3.Header = "Edit Package";
 MenuItem mnuItem4 = new MenuItem();
 mnuItem4.Header = "Delete Package";
 MenuItem mnuItem5 = new MenuItem();
 mnuItem5.Header = "Add to Queue";

ContextMenu menu = new ContextMenu() { };
 menu.Items.Add(mnuItem1);
 menu.Items.Add(mnuItem2);
 menu.Items.Add(mnuItem3);
 menu.Items.Add(mnuItem4);
 menu.Items.Add(mnuItem5);

(sender as TreeViewItem).ContextMenu = menu;
 }
 else if (type.ToUpper() == "BATCH")
 {
 MenuItem mnuItem1 = new MenuItem();
 mnuItem1.Header = "Show Batch Details";
 MenuItem mnuItem2 = new MenuItem();
 mnuItem2.Header = "Edit Batch";
 MenuItem mnuItem3 = new MenuItem();
 mnuItem3.Header = "Delete Batch";

ContextMenu menu = new ContextMenu() { };
 menu.Items.Add(mnuItem1);
 menu.Items.Add(mnuItem2);
 menu.Items.Add(mnuItem3);

(sender as TreeViewItem).ContextMenu = menu;
 }
 }
 }

private static DependencyObject GetDependencyObjectFromVisualTree(DependencyObject startObject, Type type)
 {
 var parent = startObject;
 while (parent != null)
 {
 if (type.IsInstanceOfType(parent))
 break;
 parent = VisualTreeHelper.GetParent(parent);
 }
 return parent;
 }
 }
}

Bye.

Using HierarchicalDataTemplate to bind TreeView in WPF

Just sharing a simple example that shows how to bind the XML data to TreeView using HierarchicalDataTemplate.

Suppose this is our XML file named Packages.xml

This is the XAML used


<Window x:Class="SampleSyncServerAdminTool.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="MainWindow" Height="350" Width="525">
 <Window.Resources>

 <XmlDataProvider x:Key="MyList" Source="Packages.xml" XPath="Packages"/>

<HierarchicalDataTemplate DataType="Packages" ItemsSource="{Binding XPath=*}">
 <TextBlock Text="Packages"></TextBlock>
 </HierarchicalDataTemplate>

<HierarchicalDataTemplate DataType="Package" ItemsSource="{Binding XPath=*}">
 <TextBlock Text="{Binding XPath=@Name}"></TextBlock>
 </HierarchicalDataTemplate>

<HierarchicalDataTemplate DataType="Batch" ItemsSource="{Binding XPath=*}">
 <TextBlock Text="{Binding XPath=@Name}"></TextBlock>
 </HierarchicalDataTemplate>

</Window.Resources>
 <Grid>
 <DockPanel>
 <TreeView DockPanel.Dock="Left" Width="150" Name="treeViewPackages"
 ItemsSource="{Binding Source={StaticResource MyList}}">
 </TreeView>
 <Grid DockPanel.Dock="Right">
 </Grid>
 </DockPanel>
 </Grid>
</Window>

The TreeView showing package information.

Bye.