Tag: WPF

  • How to wrap text around another control in WPF

    First things first, credit where it’s due: the StackOverflow link I eventually found that enabled me to solve this problem:

    https://stackoverflow.com/questions/3339051/wrapping-text-around-an-image-or-linking-two-textblocks-in-c-sharp-wpf

    The trick is not use use a TextBlock as conventional wisdom would indicate, but to use a FlowDocument to achieve this kind of layout.

    For future reference (I am going to need something like this fairly soon), try the following XAML snippet and the resulting WPF app screenshot to demonstrate how to display a button in the top right hand corner with text wrapped around it. In my real-world apps I am using the MVVM pattern with all sorts of bindings and events etc, but this should hopefully enable me to get there:

    MainWindow.xaml

    <Window x:Class="WpfApp1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"       
            mc:Ignorable="d"
            Title="MainWindow" Height="300" Width="400">
    
        <Grid>
            <FlowDocumentScrollViewer>
                <FlowDocument>
                    <Paragraph>
                        <Floater Width="125"
                                 HorizontalAlignment="right" Margin="0,0,0,0" >
                            <BlockUIContainer>
                                <Button 
                                    Content="Go!"
                                    Height="90" Width="100" />
                            </BlockUIContainer>
                        </Floater>
    
                        Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
                        Suspendisse et diam felis. Vestibulum ac nisl mi. 
                        Etiam varius velit lobortis nibh vestibulum nec consequat velit pellentesque. 
                        Cras commodo libero placerat nulla dapibus eget porttitor ligula tempor. 
                        Donec nisl massa, congue et pretium sit amet, feugiat vel est. 
                        Nulla dapibus metus in justo pulvinar sit amet viverra lorem rhoncus. 
                        Integer placerat interdum massa et mattis.
                    </Paragraph>
                </FlowDocument>
            </FlowDocumentScrollViewer>
        </Grid>
    </Window>
    

    Which when run gives the following output:

    Another example, this time using RichTextBox:

    MainWindow.xaml

    <Window x:Class="WrapText.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WrapText"
            mc:Ignorable="d"
            Title="MainWindow" Height="200" Width="320">
    
        <Grid>
            <RichTextBox Margin="-3,-3,-3,-3" >
                <FlowDocument>
                    <Paragraph>
                        <Floater 
                            Margin="0,0,0,0" 
                            Padding="3"
                            Width="60"               
                            HorizontalAlignment="Right">
                            <BlockUIContainer 
                                Margin="0,0,0,0">
                                <Button 
                                    Width="50"
                                    Height="50"
                                    Content="Hello" />
                            </BlockUIContainer>
                        </Floater>
                        Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
                        Vivamus eu urna ipsum. Nullam vulputate quam dolor, eget porttitor 
                        ex sagittis vitae. Nunc auctor purus tellus, ut ultricies dolor 
                        imperdiet a. In scelerisque lacus et ex tempus, quis elementum lacus 
                        bibendum. Etiam lobortis dictum mauris. Sed elementum ornare libero, 
                        et consectetur sapien posuere vitae. Interdum et malesuada fames ac 
                        ante ipsum primis in faucibus. Praesent nec tortor sit amet dui 
                        varius consectetur.
                    </Paragraph>
                </FlowDocument>
            </RichTextBox>
        </Grid>
    </Window>
    

    Giving the following output:

  • Binding the visibility of DataGridColumn in WPF / MVVM

    Binding the visibility of DataGridColumn in WPF / MVVM

    See this StackOverflow answer for the original inspiration.

    Notable quote: “First of all DataGridTextColumn or any other supported dataGrid columns doesn’t lie in Visual tree of DataGrid. Hence, by default it doesn’t inherit DataContext of DataGrid. But, it works for Binding DP only and for not other DP’s on DataGridColumn.”

    Here is an example of how to achieve this in a Visual Studio WPF application.

    Step 1: Create a Visual Studio WPF application:

    Step 2: Create a Freezable class

    “Freezable objects can inherit the DataContext even when they’re not in the visual or logical tree. So, we can take advantage of that to our use. First create class inheriting from Freezable and Data DP which we can use to bind in XAML”.

    BindingProxy.cs

    using System.Windows;
    
    namespace DataGridColumnVisibility
    {
       public class BindingProxy : Freezable
       {
          public static readonly DependencyProperty DataProperty =
             DependencyProperty.Register(&amp;amp;amp;amp;quot;Data&amp;amp;amp;amp;quot;, typeof(object),
                typeof(BindingProxy));
    
          public object Data
          {
             get { return GetValue(DataProperty); }
             set { SetValue(DataProperty, value); }
          }
    
          #region Overrides of Freezable
    
          protected override Freezable CreateInstanceCore()
          {
             return new BindingProxy();
          }
    
          #endregion
       }
    }
    

    Step 3: Set the styles and converters needed in the App.xaml file

    App.xaml

    <Application x:Class="DataGridColumnVisibility.App"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml">
      <Application.Resources>
        <Style x:Key="ColumnHeaderTextStyle" TargetType="DataGridColumnHeader">
          <Setter Property="VerticalContentAlignment" Value="Center" />
          <Setter Property="VerticalAlignment" Value="Center" />
          <Setter Property="Background" Value="Transparent" />
          <Setter Property="Foreground" Value="Black" />
        </Style>
        <BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
      </Application.Resources>
    </Application>
    

    Step 4: Create the Main Window ViewModel class

    This class we use to create and store the items that will be displayed in the DataGrid control.

    MainWindowViewModel.cs

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    
    namespace DataGridColumnVisibility
    {
       public class Item
       {
          public Item(string value1, string value2, string value3)
          {
             Value1 = value1;
             Value2 = value2;
             Value3 = value3;
          }
    
          public string Value1 { get; set; }
          public string Value2 { get; set; }
          public string Value3 { get; set; }
       }
    
       public class MainWindowViewModel : INotifyPropertyChanged
       {
          private bool _firstColumnChecked;
          private List&amp;amp;amp;amp;lt;Item&amp;amp;amp;amp;gt; _items;
    
          public MainWindowViewModel()
          {
             _items = new List<Item>
             {
                 new Item("A", "B", "C"),
                 new Item("X", "Y", "Z"),
                 new Item("1", "2", "3"),
                 new Item("000", "001", "010")
             };
          }
    
          public List<Item> Items
          {
             get { return _items; }
             set
             {
                _items = value;
                OnPropertyChanged(nameof(Items));
             }
          }
    
          public bool FirstColumnChecked
          {
             get { return _firstColumnChecked; }
             set
             {
                _firstColumnChecked = value;
                OnPropertyChanged(nameof(FirstColumnChecked));
             }
          }
    
          public event PropertyChangedEventHandler PropertyChanged;
    
          private void OnPropertyChanged(string propertyName)
          {
             VerifyPropertyName(propertyName);
             var handler = PropertyChanged;
             handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
          }
    
          [Conditional(&amp;amp;amp;amp;quot;DEBUG&amp;amp;amp;amp;quot;)]
          private void VerifyPropertyName(string propertyName)
          {
             if (TypeDescriptor.GetProperties(this)[propertyName] == null)
                throw new ArgumentNullException(GetType().Name + " does not contain property: " + propertyName);
          }
       }
    }
    

    Step 5: Set the view in MainWindow.xaml

    MainWindow.xaml

    <Window x:Class="DataGridColumnVisibility.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:DataGridColumnVisibility"
            mc:Ignorable="d"
            Title="MainWindow"
            Height="350"
            Width="525">
        <Window.DataContext>
            <local:MainWindowViewModel />
        </Window.DataContext>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="50" />
                <RowDefinition Height="*" />
                <RowDefinition Height="10" />
            </Grid.RowDefinitions>
            <StackPanel Margin="10,0,10,0" VerticalAlignment="Center" Orientation="Horizontal">
                <CheckBox Margin="0,0,20,0" FontWeight="SemiBold"
                          IsChecked="{Binding Path=FirstColumnChecked, Mode=TwoWay}"
                          Content="View Column 1" />
            </StackPanel>
            <DataGrid Grid.Row="1" FontSize="13" Margin="10,0,10,0"
                      AutoGenerateColumns="False" CanUserAddRows="False"
                      RowHeaderWidth="30" ColumnHeaderHeight="45"
                      ColumnHeaderStyle="{StaticResource ColumnHeaderTextStyle}"
                      ItemsSource="{Binding Items}">
                <DataGrid.Resources>
                    <local:BindingProxy x:Key="FirstColumnCheckedProxy"
                                        Data="{Binding FirstColumnChecked}" />
                </DataGrid.Resources>
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Column 1" Width="70"
                                        Binding="{Binding Value1}"
                                        Visibility="{Binding Data, Converter={StaticResource BoolToVisibilityConverter}, Source={StaticResource FirstColumnCheckedProxy}}" />
                    <DataGridTextColumn Header="Column 2" Width="70"
                                        Binding="{Binding Value2}" />
                    <DataGridTextColumn Header="Column 3" Width="70"
                                        Binding="{Binding Value3}" />
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
    </Window>
    
         
    

    Observe that when the program is first run the FirstColumnChecked is initially defaulted to false, thereby causing the first column not to be shown:

    And when the Checkbox is set, the first column is displayed:

     

  • Using Expander controls in C# / WPF

    A short post demonstrating how to use expander controls in WPF, to create a list of collapsible / expandable menu items in your C# WPF application

    Create a new Visual Studio application

    Create a View Model class for your main window

    MainWindowViewModel.cs

    using System.Collections.Generic;
    
    namespace ExpanderExample
    {
       public class ApplicationListItem
       {
          public ApplicationListItem(string name, string openButtonText, string updateButtonText,
             bool updateButtonVisibility = false, bool progressVisibility = true)
          {
             Name = name;
             OpenButtonText = openButtonText;
             UpdateButtonText = updateButtonText;
             UpdateButtonVisibility = updateButtonVisibility;
             ProgressVisibility = progressVisibility;
          }
    
          public string Name { get; set; }
          public string OpenButtonText { get; set; }
          public string UpdateButtonText { get; set; }
          public bool UpdateButtonVisibility { get; set; }
          public bool ProgressVisibility { get; set; }
       }
    
       public class ListItem
       {
          public ListItem(string title)
          {
             Title = title;
          }
    
          public string Title { get; set; }
          public List<ApplicationListItem> ListItems { get; set; } = new List<ApplicationListItem>();
    
          public void AddListItem(ApplicationListItem listItem)
          {
             ListItems.Add(listItem);
          }
       }
    
       public class MainWindowViewModel
       {
          public MainWindowViewModel()
          {
             var listItem1 = new ListItem("CMM");
             var appListItem1 = new ApplicationListItem("CMM - Local (English)", "Open", "Update", false, false);
             var appListItem2 = new ApplicationListItem("CMM - Local (German)", "Open", "Update", false, false);
             var appListItem3 = new ApplicationListItem("CMM - Local (Japanese)", "Open", "Update", true);
             listItem1.AddListItem(appListItem1);
             listItem1.AddListItem(appListItem2);
             listItem1.AddListItem(appListItem3);
             Items.Add(listItem1);
    
             var listItem2 = new ListItem("Sales Aid");
             var appListItem4 = new ApplicationListItem("Sales Aid - Local (English)", "Open", "Update", true, false);
             listItem2.AddListItem(appListItem4);
             Items.Add(listItem2);
          }
    
          public List<ListItem> Items { get; } = new List<ListItem>();
       }
    }
    

    So that we may hide/collapse/make visible individual controls in our expandable items list, set the boolean to visibility conversion in app.xaml

    App.xaml

    <Application x:Class="ExpanderExample.App"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:local="clr-namespace:ExpanderExample"
                 StartupUri="MainWindow.xaml">
        <Application.Resources>
            <BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
        </Application.Resources>
    </Application>
    

    Update the main window xaml to contain our expander control:

    MainWindow.xaml

    <Window x:Class="ExpanderExample.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:ExpanderExample"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
    
        <Window.DataContext>
            <local:MainWindowViewModel />
        </Window.DataContext>
    
        <ItemsControl
            Name="ItemsList"
            ItemsSource="{Binding Items}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Expander Header="{Binding Title}">
                            <ItemsControl
                                Name="icTodoList"
                                ItemsSource="{Binding ListItems}">
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                        <Grid>
                                            <Grid.RowDefinitions>
                                                <RowDefinition Height="40" />
                                                <RowDefinition Height="*" />
                                            </Grid.RowDefinitions>
    
                                            <Grid>
                                                <Grid.ColumnDefinitions>
                                                    <ColumnDefinition Width="300" />
                                                    <ColumnDefinition Width="*" />
                                                </Grid.ColumnDefinitions>
    
                                                <TextBlock
                                                    VerticalAlignment="Center" Text="{Binding Name}" />
    
                                                <Grid Column="1">
                                                    <Grid.ColumnDefinitions>
                                                        <ColumnDefinition Width="*" />
                                                        <ColumnDefinition Width="*" />
                                                    </Grid.ColumnDefinitions>
    
                                                    <Button
                                                        Visibility="{Binding UpdateButtonVisibility, Converter={StaticResource BoolToVisibilityConverter}}"
                                                        Name="Update"
                                                        Width="80"
                                                        Height="30"
                                                        HorizontalAlignment="Right"
                                                        Content="{Binding UpdateButtonText}" />
    
                                                    <Button
                                                        Name="OpenInstallPauseResume"
                                                        Grid.Column="1"
                                                        Width="80"
                                                        Height="30"
                                                        HorizontalAlignment="Right"
                                                        Content="{Binding OpenButtonText}" />
                                                </Grid>
                                            </Grid>
    
                                            <Grid Grid.Row="1">
                                                <ProgressBar
                                                    Visibility="{Binding ProgressVisibility, Converter={StaticResource BoolToVisibilityConverter}}"
                                                    Height="20" />
                                            </Grid>
                                        </Grid>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                            </ItemsControl>
                        </Expander>
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Window>
    

    On building and running the application see that the window contains the two expander controls as shown:

    On expanding the expander controls see that thse now contain the ItemsControl elements (buttons, title strings, progress bars etc), which are visible according to the values set during the initialisation of the main window View Model class.

  • Applying the 2-opt algorithm to travelling salesman problems in C# / WPF

    For the Java equivalent see this link:

    https://www.technical-recipes.com/2017/applying-the-2-opt-algorithm-to-traveling-salesman-problems-in-java/

    For the C++ equivalent see this link:

    https://www.technical-recipes.com/2012/applying-c-implementations-of-2-opt-to-travelling-salesman-problems/

    This post demonstrates how to apply the 2-opt algorithm to a number of standard test problems in C# while displaying the
    results in a WPF style window, while using the MVVM design pattern.

    See this link for an overview of the two opt algorithm.

    http://en.wikipedia.org/wiki/2-opt

    But essentially the 2-opt link swapping heuristic can be summarised by the following steps:

    The actual 2-opt heuristic can be summarised by the following pseudocode steps, repeating for all feasible combinations of I and k:

    1. take route[1] to route[i-1] and add them in order to new_route
    2. take route[i] to route[k] and add them in reverse order to new_route
    3. take route[k+1] to end and add them in order to new_route       
    4. return the new_route;  
    

    Architecture

    This application is designed around the Model View ViewModel architecture (MVVM). I find that MVVM allows for a nice clean separation between the graphical
    display, the business logic and data handling.

    https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel

    As with the Java and C++ versions, the user interface is a simple window with buttons for opening the test problem (*.tsp file) and a run button to execute the algorithm(which causes the
    display to get updated and re-draw the tour links each time a better tour is found)

    Building the User Interface in XAML

    I use standard WPF / XAML for the user interface components and the canvas in which to draw the city nodes, tour links etc:

    MainWindow.xaml

    <Window x:Class="TwoOpt.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:twoOpt="clr-namespace:TwoOpt"
            mc:Ignorable="d"        
            Title="{Binding Title}" 
            Height="620" Width="600">
        
        <Window.DataContext>
            <twoOpt:MainWindowViewModel />
        </Window.DataContext>
            
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="70" />
                <RowDefinition Height="5" />
                <RowDefinition Height="*" />
                <RowDefinition Height="5" />
            </Grid.RowDefinitions>
            
            <StackPanel 
                Orientation="Horizontal">
                <Button 
                    Command="{Binding OpenCommand}"
                    Width="110" 
                    Content="Open" 
                    Margin="10" />
                
                <Button 
                    Width="110" 
                    Command="{Binding RunCommand}"
                    Content="Run" 
                    Margin="10"/>
                
                <Grid VerticalAlignment="Center" Width="200" Margin="10">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="25" />
                        <RowDefinition Height="25" />
                    </Grid.RowDefinitions>
                    
                    <Label 
                        VerticalContentAlignment="Center"
                        Name="BestLabel"
                        VerticalAlignment="Center"/>
                    
                    <Label 
                        VerticalContentAlignment="Center"
                        Name="IterationLabel"                    
                        Grid.Row="1" />
                </Grid>
            </StackPanel>
    
            <Grid               
                Grid.Row="2">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="10" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="10" />
                </Grid.ColumnDefinitions>
    
                <Grid.RowDefinitions>
                    <RowDefinition Height="5" />
                    <RowDefinition Height="*" />
                    <RowDefinition Height="5" />
                </Grid.RowDefinitions>
    
                <Grid Name="DisplayGrid" Grid.Column="1" Grid.Row="1">
                    <Canvas Name="CanvasGrid">
                    </Canvas>
                </Grid>
            </Grid>
        </Grid>
    </Window>
    

    The ViewModel class

    This class I use for handling the business logic and the responding to the button press events from the main user interface.

    MainWindowViewModel.cs

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    using System.Windows.Forms;
    using System.Windows.Input;
    
    namespace TwoOpt
    {
       public class MainWindowViewModel : BaseViewModel
       {
          private readonly Model _model;
          private ICommand _openCommand;
          private ICommand _runCommand;    
    
          public MainWindowViewModel()
          {
             _model = new Model();
             _model.TourUpdated += ModelOnTourUpdated;
          }
    
          public IMainWindow MainWindow { get; set; }
    
          public string Title
          {
             get { return _model.Title; }
             set
             {
                _model.Title = value;
                OnPropertyChanged("Title");
             }
          }
    
          public ICommand OpenCommand
          {
             get
             {
                return _openCommand ?? (_openCommand = new RelayCommand(
                          x =>
                          {
                             var openFileDialog1 = new OpenFileDialog
                             {
                                InitialDirectory = "c:\\",
                                Filter = @"txt files (*.tsp)|*.tsp|All files (*.*)|*.*",
                                FilterIndex = 2,
                                RestoreDirectory = true
                             };
    
                             if (openFileDialog1.ShowDialog() != DialogResult.OK) return;
    
                             try
                             {
                                Stream stream;
                                // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                                if ((stream = openFileDialog1.OpenFile()) == null) return;
    
                                var height = MainWindow.GridHeight();
                                var width = MainWindow.GridWidth();
    
                                _model.CancelJobs();
                                _model.InitMatrix(stream, height, width);
                                Title = _model.Title;
                                DisplayNodes();
                                MainWindow.UpdateIteration(0, 0, null);
                             }
                             catch (Exception ex)
                             {
                                MessageBox.Show(@"Error: Could not read file from disk. Original error: " + ex.Message);
                             }
                          }));
             }
          }
    
          public ICommand RunCommand
          {
             get
             {
                return _runCommand ?? (_runCommand = new RelayCommand(
                          x => { _model.Run(); }));
             }
          }
    
          public List<Pair> Lines => _model.TourCoords;
    
          private void ModelOnTourUpdated(object sender, EventArgs<Tuple<double, int>> eventArgs)
          {
             var iter = eventArgs.Value.Item2;
             var best = eventArgs.Value.Item1;
    
             Update(best, iter);
          }
    
          public IEnumerable<string> ReadLines(Func<Stream> streamProvider,
             Encoding encoding)
          {
             using (var stream = streamProvider())
             {
                using (var reader = new StreamReader(stream, encoding))
                {
                   string line;
                   while ((line = reader.ReadLine()) != null)
                      yield return line;
                }
             }
          }
    
          private void DisplayNodes()
          {
             MainWindow?.PlotNodes(_model.DisplayCoords);
          }
    
          private void Update(double best, int iter)
          {
             MainWindow.UpdateIteration(best, iter, _model.TourCoords);
          }
       }
    }
    

    Within this code I also make use of the Supervisor Controlling Pattern as means of accessing the View controls from within the ViewModel class.

    It does this by allowing MainWindow.xaml.cs to inherits from the IMainWindow interface, which provide the functions to plot the nodes and draw the links:

    IMainWindow.cs

    using System.Collections.Generic;
    
    namespace TwoOpt
    {
       public interface IMainWindow
       {
          void UpdateIteration(double best, int iter, List<Pair> tourCoords);
          void PlotNodes(List<Pair> displayCoords);
      
          double GridHeight();
          double GridWidth();
       }
    }
    

    MainWindow.xaml.cs

    using System.Collections.Generic;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Shapes;
    
    namespace TwoOpt
    {
       /// <summary>
       ///    Interaction logic for MainWindow.xaml
       /// </summary>
       public partial class MainWindow : IMainWindow
       {
          public MainWindow()
          {
             InitializeComponent();
    
             var mainWindowViewModel = DataContext as MainWindowViewModel;
    
             if (mainWindowViewModel != null)
                mainWindowViewModel.MainWindow = this;
          }
    
          public void PlotNodes(List<Pair> displayCoords)
          {
             CanvasGrid.Children.Clear();
    
             InvalidateVisual();
    
             foreach (var coord in displayCoords)
             {
                var ellipse = new Ellipse
                {
                   Width = 4,
                   Height = 4,
                   Fill = Brushes.Blue
                };
    
                Canvas.SetLeft(ellipse, coord.X());
                Canvas.SetTop(ellipse, coord.Y());
    
                CanvasGrid.Children.Add(ellipse);
             }
          }    
    
          public double GridHeight()
          {
             return DisplayGrid.ActualHeight;
          }
    
          public double GridWidth()
          {
             return DisplayGrid.ActualWidth;
          }
    
          public void UpdateIteration(double best, int iter, List<Pair> tourCoords)
          {
             Dispatcher.Invoke(() =>
             {
                IterationLabel.Content = "Iteration: " + iter;
                BestLabel.Content = "Best distance: " + (int)best;
    
                if (tourCoords == null) return;          
    
                var size = tourCoords.Count;
                var startCoord = tourCoords[0];
                CanvasGrid.Children.Clear();
    
                for (var i = 1; i < size; ++i)
                {
                   var endCoord = tourCoords[i];
    
                   var link = new Line
                   {
                      X1 = startCoord.X() + 2,
                      Y1 = startCoord.Y() + 2,
                      X2 = endCoord.X() + 2,
                      Y2 = endCoord.Y() + 2,
    
                      Stroke = Brushes.Blue
                   };
    
                   CanvasGrid.Children.Add(link);
                   startCoord = tourCoords[i];
                }
    
                var finalLink = new Line
                {
                   X1 = tourCoords[0].X() + 2,
                   Y1 = tourCoords[0].Y() + 2,
                   X2 = tourCoords[size - 1].X() + 2,               
                   Y2 = tourCoords[size - 1].Y() + 2,
                   Stroke = Brushes.Blue
                };
    
                CanvasGrid.Children.Add(finalLink);
             });         
          }
       }
    }
    

    C# implementation of the 2-opt algorithm

    // Do all 2-opt combinations
          private void TwoOpt()
          {
             // Get tour size
             var size = _tour.TourSize();
    
             //CHECK THIS!!		
             for (var i = 0; i < size; i++)
             {
                _newTour.SetCity(i, _tour.GetCity(i));
             }
    
             // repeat until no improvement is made 
             var improve = 0;
             var iteration = 0;
    
             while (improve < 500)
             {
                var bestDistance = _tour.TourDistance();
    
                for (var i = 1; i < size - 1; i++)
                {
                   for (var k = i + 1; k < size; k++)
                   {
                      TwoOptSwap(i, k);
                      iteration++;
    
                      var newDistance = _newTour.TourDistance();
    
                      if (!(newDistance < bestDistance)) continue;
    
                      // Improvement found so reset
                      improve = 0;
    
                      for (var j = 0; j < size; j++)
                      {
                         _tour.SetCity(j, _newTour.GetCity(j));
                      }
    
                      bestDistance = newDistance;
    
                      // Update the display
                      SetTourCoords();
    
                      TourUpdated.Raise(this, new Tuple<double, int>( bestDistance, iteration));
                   }
                }
    
                improve++;
             }         
          }
    

    And this is how the swap is done:

    private void TwoOptSwap(int i, int k)
          {
             var size = _tour.TourSize();
    
             // 1. take route[0] to route[i-1] and add them in order to new_route
             for (var c = 0; c <= i - 1; ++c)
             {
                _newTour.SetCity(c, _tour.GetCity(c));
             }
    
             // 2. take route[i] to route[k] and add them in reverse order to new_route
             var dec = 0;
             for (var c = i; c <= k; ++c)
             {
                _newTour.SetCity(c, _tour.GetCity(k - dec));
                dec++;
             }
    
             // 3. take route[k+1] to end and add them in order to new_route
             for (var c = k + 1; c < size; ++c)
             {
                _newTour.SetCity(c, _tour.GetCity(c));
             }
          }
    

    Trying out the program

    Att48.tsp

    I tend to always start out on a reasonably small test problem such as att48.tsp, available from here:

    http://elib.zib.de/pub/mp-testdata/tsp/tsplib/tsp/att48.tsp

    Click the Open button and navigate to the Att48.tsp test problem when prompted, which plots the city locations as a series of nodes:

    Upon pressing Run, we obtain the solution of length 10959:

    kroA200.tsp

    I then try out the program on a larger problem with 200 nodes, “kroA200.tsp”, available from here:

    http://elib.zib.de/pub/mp-testdata/tsp/tsplib/tsp/kroA200.tsp

    Giving the following result:

    lin318.tsp

    Finally I try one more test problem, “lin318.tsp”, available from here:

    http://elib.zib.de/pub/mp-testdata/tsp/tsplib/tsp/lin318.tsp

    Results as shown:

    Full code sample in the form of a Visual Studio 2015 project is available from here:


  • Using HierarchicalDataTemplate with TreeView in WPF / MVVM

    Using HierarchicalDataTemplate with TreeView in WPF / MVVM

    Some instructions on how to create and display a hierarchical data structure in WPF by using HierarchicalDataTemplate with TreeView

    Visual Studio 2019 project downloadable from here.

    Stack Overflow link

    Step 1: Create a new WPF application


    Step 2: Create the model class

    The Model class we use to define our Directory ‘Item’ structure.

    In this instance I’m keeping things really simple, just a ‘Name’ and a List of other ‘DirectoryItem’ objects.

    Also contained in this class is a recursive function to traverse the ‘DirectoryItem’ node elements:

    Model.cs

    using System.Collections.Generic;
    
    namespace WpfTreeViewBinding.Model
    {
       public class Item
       {
          public string Name { get; set; }
       }
    
       public class DirectoryItem : Item
       {
          public DirectoryItem()
          {
             Items = new List<DirectoryItem>();
          }
    
          public List<DirectoryItem> Items { get; set; }
    
          public void AddDirItem(DirectoryItem directoryItem)
          {
             Items.Add(directoryItem);
          }
    
          public List<Item> Traverse(DirectoryItem it)
          {
             var items = new List<Item>();
    
             foreach (var itm in it.Items)
             {
                Traverse(itm);
                items.Add(itm);
             }
    
             return items;
          }
       }
    }
    

    Step 3: Create the ‘Item Provider’ class

    I use this class to knock up an example hierarchical structure of ‘DirectoryItem’ elements and a public property to return this:

    ItemProvider.cs

    using System.Collections.Generic;
    using WpfTreeViewBinding.Model;
    
    namespace WpfTreeViewBinding
    {
       public class ItemProvider
       {
          private readonly DirectoryItem _rootDirectoryItem;
    
          public ItemProvider()
          {
             _rootDirectoryItem = new DirectoryItem {Name = "X"};
    
             var childItem1 = new DirectoryItem {Name = "A"};
    
             var grandChildItem11 = new DirectoryItem {Name = "A1"};
             var grandChildItem12 = new DirectoryItem {Name = "A2"};
    
             var greatgrandChildItem2 = new DirectoryItem {Name = "A2_1"};
             grandChildItem11.AddDirItem(greatgrandChildItem2);
    
             childItem1.AddDirItem(grandChildItem11);
             childItem1.AddDirItem(grandChildItem12);
    
             var childItem2 = new DirectoryItem {Name = "B"};
             var childItem3 = new DirectoryItem {Name = "C"};
             var childItem4 = new DirectoryItem {Name = "D"};
    
             var grandChildItem121 = new DirectoryItem {Name = "B1"};
             childItem2.AddDirItem(grandChildItem121);
    
             var childList1 = new List<DirectoryItem>
             {
                childItem1,
                childItem2,
                childItem3,
                childItem4
             };
    
             _rootDirectoryItem.Items = childList1;
          }
    
          public List<Item> DirItems => _rootDirectoryItem.Traverse(_rootDirectoryItem);
       }
    }
    

    Step 4: Create a ViewModel class

    This class we use to bind to the XAML elements that we will define shortly.

    It contains the binding property ‘DirItems’ which we create using the ‘ItemProvider’ class defined previously.

    ViewModel.cs

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using WpfTreeViewBinding.Model;
    
    namespace WpfTreeViewBinding
    {
        public class ViewModel : INotifyPropertyChanged
        {
            private List<Item> _dirItems;
    
            public ViewModel()
            {
                var itemProvider = new ItemProvider();
                DirItems = itemProvider.DirItems;
            }
    
            public List<Item> DirItems
            {
                get { return _dirItems; }
                set
                {
                    _dirItems = value;
                    OnPropertyChanged(nameof(DirItems));
                }
            }
    
            #region INotifyPropertyChanged Members
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            #endregion
    
            protected void OnPropertyChanged(string propertyName)
            {
                VerifyPropertyName(propertyName);
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
    
            protected void OnPropertyChanged(int propertyValue)
            {
                VerifyPropertyName(propertyValue);
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyValue.ToString()));
            }
    
            [Conditional("DEBUG")]
            private void VerifyPropertyName(string propertyName)
            {
                if (TypeDescriptor.GetProperties(this)[propertyName] == null)
                    throw new ArgumentNullException(GetType().Name + " does not contain property: " + propertyName);
            }
    
            [Conditional("DEBUG")]
            private void VerifyPropertyName(int propertyValue)
            {
                if (TypeDescriptor.GetProperties(this)[propertyValue] == null)
                    throw new ArgumentNullException(GetType().Name + " does not contain property: " + propertyValue.ToString());
            }
        }
    }
    

    Step 5: Define the Main Window xaml.

    This xaml we will update to implement the TreeView and HierarchicalDataTemplate elements needed to display the hierarchical tree structure:

    MainWindow.xaml

    <Window x:Class="WpfTreeViewBinding.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:vm="clr-namespace:WpfTreeViewBinding"
            xmlns:model="clr-namespace:WpfTreeViewBinding.Model"
            mc:Ignorable="d"
            Title="MainWindow" Height="200" Width="325">
    
        <Window.DataContext>
            <vm:ViewModel />
        </Window.DataContext>
    
        <TreeView ItemsSource="{Binding DirItems}">
            <TreeView.Resources>
                <HierarchicalDataTemplate DataType="{x:Type model:DirectoryItem}" ItemsSource="{Binding Path=Items}">
                    <TextBlock Text="{Binding Path=Name}" />
                </HierarchicalDataTemplate>
            </TreeView.Resources>
        </TreeView>
    
    </Window>
    

    Running the program we can see our ‘DirectoryItem’ elements are displayed hierarchically as shown:

  • How to use resource files in your C# WPF project

    Some practical instructions on how to include resource files to your Visual Studio C# WPF project that are not source code.

    Such non-code files can include binary data, text files, media files, string values, icons, XML, or any other type of data.

    Project resource data is stored in XML format in the .resx file (named Resources.resx by default).

    In this example we demonstrate how resource files can be defined in and accessed from a separate class library, thereby helping to make your project cleaner and less cluttered.

    Step 1: Create a new Visual Studio WPF project.

    Create a new WPF application for this example:

    Step 2: Add a new class library project

    Right-click on your solution folder and select Add > New Project. Create a new Class Library application. We’ll call it FileStore:

    This project we will use to contain our resource files.

    Step 3: Create a folder to store the resource files

    In the ‘FileStore’ Class Library project you created, right-click the project folder and select Add > New Folder. Let’s call it ‘Resources’:

    Resource files folder
    Resource files folder

    And then add the resource file(s) you would like the executable to be able to access. In this example, I am interested in using an icon resource file. The example icon resource file you can obtain from here:

    https://www.technical-recipes.com/Downloads/icon.ico

    Copy this file to a Resources folder that would have been created in Step 3.

    And in Visual Studio, right-click the Resources folder and select Add > Existing Item. Add the ‘icon.ico’ resource contained in the Resources folder:

    (In this Class Library project you can also delete the ‘Class1.cs’ file that gets automatically generated).

    Step 4: Create a new resx file

    In the ‘FileStore’ Class Library project, right-click the project folder and select Add > New Item.

    Select a new Resource file. I prefer to rename this to ‘Resource.resx’:

    Step 5: Add the file resource to the resx file.

    Double click (select) Resource.resx.

    Select Add Resource > Add Existing File.

    Select the ‘icon.ico’ that you placed in the Resources folder.

    Make sure that the Access Modify is set to ‘Public’ nor ‘Internal’

    Resource.resx now looks like this:

    Icon resource file

    Step 6: Add the ‘FileStore’ dll reference to the main startup project

    Right-click on the ‘FileStore’ Class Library project and select rebuild.

    Right-click the ‘ResourceFiles’ main startup project and select Add > Reference. In the reference Manager dialog that appears, select the ‘Project’ section and select the ‘FileStore’ project:

    Step 7: Add other necessary references to the main startup project

    Firstly in order to use the ‘NotifyIcon’ class we need to import the ‘System.Windows.Forms’ using directive.

    In the ResourceFiles project, right-click on References and select Add Reference. Search for and select ‘System.Windows.Forms’:

    In the same project, we also need to add a reference to System.Drawing:

    Step 8: Access the resource in your code

    In the MainWindow.xaml.cs code, update so that we can access the icon resource created in the ‘FileStore’ project.

    This small code modification will enable the icon resource file to be displayed in the notification area of your Windows screen. Access to the icon file resource is made via the line:

    FileStore.Resource.icon;
    

    Full code listing:

    using System.Windows;
    using System.Windows.Forms;
    
    namespace ResourceFiles
    {
       public partial class MainWindow : Window
       {
          NotifyIcon notifyIcon = new NotifyIcon();
    
          public MainWindow()
          {
             InitializeComponent();
    
             notifyIcon.Icon = FileStore.Resource.icon;
             notifyIcon.Visible = true;
             notifyIcon.Text = "Check for updates";
          }
       }
    }
    

    Rebuild the whole solution and run.

    Notice that the icon resource is displayed in the notification area of the Windows toolbar as shown:

  • How to use Interaction Triggers to handle user-initiated events in WPF / MVVM

    Example scenario: User clicks the mouse in a WPF application – how do we ‘listen’ to that event in order to trigger and handle an event in the main code? A possible solution is to use in WPF MVVM.

    See this post to learn how to do this using MvvmLight:

    https://www.technical-recipes.com/2017/handling-mouse-events-in-wpf-mvvm-using-mvvmlight-event-triggers/

    Here is an example WPF implementation created in Visual Studio 2015.

    Step 1: Create a WPF application

    interactivity1

    Step 2: Add the System.Windows.Interactivity reference

    UPDATE

    For more recent versions of .NET see this StackOverflow post on how to upgrade from older versions of Interactivity to the latest NuGet package:

    https://stackoverflow.com/questions/8360209/how-to-add-system-windows-interactivity-to-project/56240223#56240223

    If you need to migrate from an older version, then do these steps:

    1. Remove reference to “Microsoft.Expression.Interactions” and “System.Windows.Interactivity”
    2. Install the Microsoft.Xaml.Behaviors.Wpf NuGet package.
    3. XAML files – replace the xmlns namespaces http://schemas.microsoft.com/expression/2010/interactivity and http://schemas.microsoft.com/expression/2010/interactions with http://schemas.microsoft.com/xaml/behaviors
    4. C# files – replace the usings in c# files “Microsoft.Xaml.Interactivity” and “Microsoft.Xaml.Interactions” with “Microsoft.Xaml.Behaviors”

    [Older version]

    Right click the project folder and select ‘Add Reference’. Select System.Windows.Interactivity reference:

    interactivity2

    And add this reference to the MainWindow.xaml:

    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    

    So that the MainWindow.xaml looks something like this:

    <Window x:Class="Interactivity.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
            xmlns:local="clr-namespace:Interactivity"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            
        </Grid>
    </Window>
    

    As mentioned in the update, replace usages of

    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    

    with

    xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
    

    Step 3: Add the event handling infrastructure

    Add the following classes to you project: EventArgs.cs, EventRaiser.cs, RelayCommand.cs

    EventArgs.cs

    using System;
    
    namespace Interactivity
    {
       public class EventArgs<T> : EventArgs
       {
          public EventArgs(T value)
          {
             Value = value;
          }
    
          public T Value { get; private set; }
       }
    }
    

    EventRaiser.cs

    using System;
    
    namespace Interactivity
    {
       public static class EventRaiser
       {
          public static void Raise(this EventHandler handler, object sender)
          {
             if (handler != null)
             {
                handler(sender, EventArgs.Empty);
             }
          }
    
          public static void Raise<T>(this EventHandler<EventArgs<T>> handler, object sender, T value)
          {
             if (handler != null)
             {
                handler(sender, new EventArgs<T>(value));
             }
          }
    
          public static void Raise<T>(this EventHandler<T> handler, object sender, T value) where T : EventArgs
          {
             if (handler != null)
             {
                handler(sender, value);
             }
          }
    
          public static void Raise<T>(this EventHandler<EventArgs<T>> handler, object sender, EventArgs<T> value)
          {
             if (handler != null)
             {
                handler(sender, value);
             }
          }
       }
    }
    

    RelayCommand.cs

    using System;
    using System.Windows.Input;
    
    namespace Interactivity
    {
       public class RelayCommand<T> : ICommand
       {
          private readonly Predicate<T> _canExecute;
          private readonly Action<T> _execute;
    
          public RelayCommand(Action<T> execute)
             : this(execute, null)
          {
             _execute = execute;
          }
    
          public RelayCommand(Action<T> execute, Predicate<T> canExecute)
          {
             if (execute == null)
             {
                throw new ArgumentNullException("execute");
             }
             _execute = execute;
             _canExecute = canExecute;
          }
    
          public bool CanExecute(object parameter)
          {
             return _canExecute == null || _canExecute((T)parameter);
          }
    
          public void Execute(object parameter)
          {
             _execute((T)parameter);
          }
    
          public event EventHandler CanExecuteChanged
          {
             add { CommandManager.RequerySuggested += value; }
             remove { CommandManager.RequerySuggested -= value; }
          }
       }
    
       public class RelayCommand : ICommand
       {
          private readonly Predicate<object> _canExecute;
          private readonly Action<object> _execute;
    
          public RelayCommand(Action<object> execute)
             : this(execute, null)
          {
             _execute = execute;
          }
    
          public RelayCommand(Action<object> execute, Predicate<object> canExecute)
          {
             if (execute == null)
             {
                throw new ArgumentNullException("execute");
             }
             _execute = execute;
             _canExecute = canExecute;
          }
    
          public bool CanExecute(object parameter)
          {
             return _canExecute == null || _canExecute(parameter);
          }
    
          public void Execute(object parameter)
          {
             _execute(parameter);
          }
    
          // Ensures WPF commanding infrastructure asks all RelayCommand objects whether their
          // associated views should be enabled whenever a command is invoked 
          public event EventHandler CanExecuteChanged
          {
             add
             {
                CommandManager.RequerySuggested += value;
                CanExecuteChangedInternal += value;
             }
             remove
             {
                CommandManager.RequerySuggested -= value;
                CanExecuteChangedInternal -= value;
             }
          }
    
          private event EventHandler CanExecuteChangedInternal;
    
          public void RaiseCanExecuteChanged()
          {
             CanExecuteChangedInternal.Raise(this);
          }
       }
    }
    

    Step 4: Set the MainWindow.xaml DataContext and Event Triggers

    MainWindow.xaml

    <Window x:Class="Interactivity.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
            xmlns:local="clr-namespace:Interactivity"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
    
        <Window.DataContext>
            <local:MainWindowViewModel />
        </Window.DataContext>
    
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="MouseLeftButtonDown" >
                <i:InvokeCommandAction Command="{Binding LeftMouseButtonDown}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
        
        <Grid>
           
        </Grid>
    </Window>
    

    Step 5: Create a ViewModel class for the Main Window

    Add the class MainWindowViewModel.cs to your project.

    Our MainWindowViewModel class will be used to bind the ‘left mouse button down’ EventName command defined in the MainWindow.xaml.

    MainWindowViewModel.cs

    using System;
    using System.Windows;
    using System.Windows.Input;
    
    namespace Interactivity
    {
       public class MainWindowViewModel
       {
          private ICommand _leftButtonDownCommand;
    
          public ICommand LeftMouseButtonDown
          {
             get
             {
                return _leftButtonDownCommand ?? (_leftButtonDownCommand = new RelayCommand(
                   x =>
                   {
                      DoStuff();
                   }));
             }
          }
    
          private static void DoStuff()
          {
             MessageBox.Show("Responding to left mouse button click event...");
          }
       }
    }
    

    So that when we run the empty WPF application and left click anywhere on the window, the event gets handled, as evidenced by the message box that gets invoked:

    interactivity3

  • Embedding PowerPoint documents in WPF

    Some instruction on how to embed a PowerPoint presentation within a WPF application by converting the file to a XPS format and displaying this using DocumentViewer.

    Step 1: Create a WPF Application

    embedpowerpoint
    Step 2. Include the necessary references

    Microsoft.Office.Interop.PowerPoint

    embedpowerpoint2

    Office

    powerpoint1

    ReachFramework

    powerpoint2

    Step 3: Insert DocumentViewer control into MainWindow.xaml

    <Window x:Class="DocumentViewer.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
    
        <Grid>
            <DocumentViewer 
                Name="DocumentviewPowerPoint" 
                VerticalAlignment="Top" 
                HorizontalAlignment="Left" />
        </Grid>
    </Window>
    

    Step 4: Convert the PowerPoint file to XPS format and point the DocumentView.Document to it

    As demonstrated in the following code snippet.

    For MVVM style applications it would be very similar, except we would need to bind the ‘Document’ to some object property in the ViewModel.

    using System;
    using System.IO;
    using System.Windows;
    using System.Windows.Xps.Packaging;
    using Microsoft.Office.Core;
    using Microsoft.Office.Interop.PowerPoint;
    using Application = Microsoft.Office.Interop.PowerPoint.Application;
    
    namespace DocumentViewer
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
    
                const string powerPointFile = @"E:\temp\powerpoint.pptx";
                var xpsFile = Path.GetTempPath() + Guid.NewGuid() + ".xps";
                var xpsDocument = ConvertPowerPointToXps(powerPointFile, xpsFile);
    
                DocumentviewPowerPoint.Document = xpsDocument.GetFixedDocumentSequence();
            }
    
            private static XpsDocument ConvertPowerPointToXps(string pptFilename, string xpsFilename)
            {
                var pptApp = new Application();
    
                var presentation = pptApp.Presentations.Open(pptFilename, MsoTriState.msoTrue, MsoTriState.msoFalse,
                    MsoTriState.msoFalse);
    
                try
                {
                    presentation.ExportAsFixedFormat(xpsFilename, PpFixedFormatType.ppFixedFormatTypeXPS);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Failed to export to XPS format: " + ex);
                }
                finally
                {
                    presentation.Close();
                    pptApp.Quit();
                }
    
                return new XpsDocument(xpsFilename, FileAccess.Read);
            }
        }
    }
    

    Running the sample program using a sample PowerPoint file shows that the PowerPoint is indeed embedded in the WPF application as shown:

    embedpowerpoint3

  • Using the Mediator pattern in MVVM / WPF

    Some instructions on how to use the Mediator design pattern as a means of allowing communication between ViewModel classes in your MVVM / WPF application.

    In this example I use the Mediator as a means of communicating to the main window which view to display when the user clicks a button on either of the child views, View1 and View2.

    Step 1: Create a new WPF application

    mediator1

    Step 2: Add event handling classes

    EventArgs.cs

    using System;
    
    namespace MvvmSwitchViews
    {
        public class EventArgs<T> : EventArgs
        {
            public EventArgs(T value)
            {
                Value = value;
            }
    
            public T Value { get; private set; }
        }
    }
    

    EventRaiser.cs

    using System;
    
    namespace MvvmSwitchViews
    {
        public static class EventRaiser
        {
            public static void Raise(this EventHandler handler, object sender)
            {
                if (handler != null)
                {
                    handler(sender, EventArgs.Empty);
                }
            }
    
            public static void Raise<T>(this EventHandler<EventArgs<T>> handler, object sender, T value)
            {
                if (handler != null)
                {
                    handler(sender, new EventArgs<T>(value));
                }
            }
    
            public static void Raise<T>(this EventHandler<T> handler, object sender, T value) where T : EventArgs
            {
                if (handler != null)
                {
                    handler(sender, value);
                }
            }
    
            public static void Raise<T>(this EventHandler<EventArgs<T>> handler, object sender, EventArgs<T> value)
            {
                if (handler != null)
                {
                    handler(sender, value);
                }
            }
        }
    }
    

    RelayCommand.cs

    using System;
    using System.Windows.Input;
    
    namespace MvvmSwitchViews
    {
        public class RelayCommand<T> : ICommand
        {
            private readonly Predicate<T> _canExecute;
            private readonly Action<T> _execute;
    
            public RelayCommand(Action<T> execute)
                : this(execute, null)
            {
                _execute = execute;
            }
    
            public RelayCommand(Action<T> execute, Predicate<T> canExecute)
            {
                if (execute == null)
                {
                    throw new ArgumentNullException("execute");
                }
                _execute = execute;
                _canExecute = canExecute;
            }
    
            public bool CanExecute(object parameter)
            {
                return _canExecute == null || _canExecute((T)parameter);
            }
    
            public void Execute(object parameter)
            {
                _execute((T)parameter);
            }
    
            public event EventHandler CanExecuteChanged
            {
                add { CommandManager.RequerySuggested += value; }
                remove { CommandManager.RequerySuggested -= value; }
            }
        }
    
        public class RelayCommand : ICommand
        {
            private readonly Predicate<object> _canExecute;
            private readonly Action<object> _execute;
    
            public RelayCommand(Action<object> execute)
                : this(execute, null)
            {
                _execute = execute;
            }
    
            public RelayCommand(Action<object> execute, Predicate<object> canExecute)
            {
                if (execute == null)
                {
                    throw new ArgumentNullException("execute");
                }
                _execute = execute;
                _canExecute = canExecute;
            }
    
            public bool CanExecute(object parameter)
            {
                return _canExecute == null || _canExecute(parameter);
            }
    
            public void Execute(object parameter)
            {
                _execute(parameter);
            }
    
            // Ensures WPF commanding infrastructure asks all RelayCommand objects whether their
            // associated views should be enabled whenever a command is invoked 
            public event EventHandler CanExecuteChanged
            {
                add
                {
                    CommandManager.RequerySuggested += value;
                    CanExecuteChangedInternal += value;
                }
                remove
                {
                    CommandManager.RequerySuggested -= value;
                    CanExecuteChangedInternal -= value;
                }
            }
    
            private event EventHandler CanExecuteChangedInternal;
    
            public void RaiseCanExecuteChanged()
            {
                CanExecuteChangedInternal.Raise(this);
            }
        }
    }
    

    Step 3: Create the views

    For demonstration purposes we add 2 x new views, each with a button that directs the user to another screen:

    View1.xaml

    <UserControl x:Class="MvvmSwitchViews.View1"
                 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:MvvmSwitchViews"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
    
        <UserControl.DataContext>
            <local:View1ViewModel />
        </UserControl.DataContext>
        
        <Grid>
            <Button 
                Content="Goto View 2"
                Command="{Binding GotoView2Command}"           
    		    HorizontalAlignment="Center"               
    		    Margin="10,10,0,0"
    		    VerticalAlignment="Center"
    		    Width="75">
            </Button>
        </Grid>
    </UserControl>
    
    

    View2.xaml

    <UserControl x:Class="MvvmSwitchViews.View2"
                 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:MvvmSwitchViews"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
    
        <UserControl.DataContext>
            <local:View2ViewModel />
        </UserControl.DataContext>
        
        <Grid>
            <Button 
                Content="Goto View 1"
                Command="{Binding GotoView1Command}"           
    		    HorizontalAlignment="Center"               
    		    Margin="10,10,0,0"
    		    VerticalAlignment="Center"
    		    Width="75">
            </Button>
        </Grid>
    </UserControl>
    

    MainWindow.xaml

    The MainWindow.xaml is updated as follows:

    We use a DataTemplate and ContentControl to enable us to switch between views:

    <Window x:Class="MvvmSwitchViews.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:MvvmSwitchViews"  
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <DataTemplate DataType="{x:Type local:View1ViewModel}">
                <local:View1/>
            </DataTemplate>
            <DataTemplate DataType="{x:Type local:View2ViewModel}">
                <local:View2/>
            </DataTemplate>
        </Window.Resources>
    
        <Window.DataContext>
            <local:MainWindowViewModel />
        </Window.DataContext>
    
        <Grid>
            <ContentControl Content="{Binding CurrentView}" />
        </Grid>
    </Window>
    
    

    Step 4: Create the ViewModel classes

    ViewModelBase.cs

    using System.ComponentModel;
    
    namespace MvvmSwitchViews
    {
       public class ViewModelBase : INotifyPropertyChanged
       {
          public event PropertyChangedEventHandler PropertyChanged;
          protected void OnPropertyChanged(string propertyName)
          {
    
             var handler = PropertyChanged;
             if (handler != null)
             {
                handler(this, new PropertyChangedEventArgs(propertyName));
             }
          }
       }
    }
    

    View1ViewModel1.cs

    using System;
    using System.Windows.Input;
    
    namespace MvvmSwitchViews
    {
        public class View1ViewModel : ViewModelBase
        {
            private ICommand _gotoView2Command;
    
            public ICommand GotoView2Command
            {
                get
                {
                    return _gotoView2Command ?? (_gotoView2Command = new RelayCommand(
                       x =>
                       {
                           Mediator.NotifyColleagues("ChangeView", false);
                       }));
                }
            }
        }
    }
    

    View2ViewModel.cs

    using System;
    using System.Windows.Input;
    
    namespace MvvmSwitchViews
    {
        public class View2ViewModel : ViewModelBase
        {
            private ICommand _gotoView1Command;
    
            public ICommand GotoView1Command
            {
                get
                {
                    return _gotoView1Command ?? (_gotoView1Command = new RelayCommand(
                       x =>
                       {
                           Mediator.NotifyColleagues("ChangeView", true);
                       }));
                }
            }
        }
    }
    

    Update the ViewModel class for the Main Window ViewModel as well:

    MainWindowViewModel.cs

    using System.Windows.Input;
    
    namespace MvvmSwitchViews
    {
        public class MainWindowViewModel : ViewModelBase
        {
            private object _view1 = new View1();
            private object _view2 = new View2();
            private object _currentView;     
    
            public MainWindowViewModel()
            {
                _currentView = _view1;
                Mediator.Register("ChangeView", OnChangeView);
            }
    
            public object CurrentView
            {
                get
                {
                    return _currentView;
                }
                set
                {
                    _currentView = value;
                    OnPropertyChanged("CurrentView");
                } 
            }
    
            public void OnChangeView(object show)
            {
                bool showView1 = (bool)show;
                CurrentView = showView1 ? _view1 : _view2;
            }
        }
    }
    

    Step 5: Create the Mediator static class

    Mediator.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace MvvmSwitchViews
    {
        static public class Mediator
        {
            static IDictionary<string, List<Action<object>>> pl_dict = new Dictionary<string, List<Action<object>>>();
    
            static public void Register(string token, Action<object> callback)
            {
                if (!pl_dict.ContainsKey(token))
                {
                    var list = new List<Action<object>>();
                    list.Add(callback);
                    pl_dict.Add(token, list);
                }
                else
                {
                    bool found = false;
                    foreach (var item in pl_dict[token])
                        if (item.Method.ToString() == callback.Method.ToString())
                            found = true;
                    if (!found)
                        pl_dict[token].Add(callback);
                } 
            }
    
            static public void Unregister(string token, Action<object> callback)
            {
                if (pl_dict.ContainsKey(token))
                    pl_dict[token].Remove(callback);
            }
    
            static public void NotifyColleagues(string token, object args)
            {
                if (pl_dict.ContainsKey(token))
                    foreach (var callback in pl_dict[token])
                        callback(args);
            }
        }
    }
    

    Step 6: Try the code

    So in this example, the MainWindowViewModel class subscribes to the “ChangeView” events that are initiated in the View1ViewModel and View2ViewModel classes that are bound to View1 and View2 respectively.

    mediator2

    So when the user clicks the button in the first view, the Mediator class notifies it’s listeners that this event has happened, telling it not to switch to View1 but to View2 instead:

    mediator3


  • Using the CefSharp Chromium Web Browser in WPF / XAML

    Some brief instructions on getting started with using the Chromium Web Browser in WPF.

    Step 1: Create a new WPF project

    cefsharpbrowser

    Step 2: Obtain the CefSharp packages using NuGet

    Link for the NuGet packages is here:

    https://www.nuget.org/packages/CefSharp.WPF/

    Select Tool > NuGet Package manager > Package Manager Console.

    Use the

    PM> install-package CefSharp.Wpf

    Step 3: add the CefSharp dll references

    Right-click on References, select ‘Add reference’

    When the dialog appears, select the Browse button. Navigate to the ‘packages’ folder that NuGet has installed to your Visual Studio project. For this project I’m choosing the x86 versions…

    Add CefSharp.Wpf.dll:

    cefsharpbrowser1

    Add the CefSharp.dll, CefSharp.Core.dll, CefSharp.BrowserSubprocessCore.dll:

    cefsharpbrowser2

    If you get the error similar to the following:

    Error	1	CefSharp.Common will work out of the box if you specify platform (x86 / x64). For AnyCPU Support see https://github.com/cefsharp/CefSharp/issues/1714	CefSharpBrowser
    

    … make sure you have set the Configuration Manager is set to either x86 or x64 – NOT ‘Any CPU’ otherwise it will never work.

    cefsharpbrowser3

    Step 4: Update the MainWindow.xaml to embed the ChromiumWebBrowser control:

    Namely, a reference to the CefSharp.Wpf namespace and the ChromiumBrowserControl itself.

    MainWindow.xaml

    <Window x:Class="CefSharpBrowser.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:wpf="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
            Title="MainWindow" 
            Height="350" Width="525">
        
        <Grid>
            <wpf:ChromiumWebBrowser 
                x:Name="Browser"
                Address="http://www.google.co.uk" />
        </Grid>
    </Window>
    

    The re-build your project. You may have to close and re-open your MainWindow.xaml file to get it to update and display correctly. When run the browser control appears embedded in the WPF application and navigates to the web address pointed to by the ChromiumWebBrowser ‘Address’ property, as shown:

    cefsharpbrowser4

    Known issues

    The most recent version of CefSharp (version 53.0.0) does not seem to browse PDF documents so well. If this is an issue for you, uninstall this version (by using the NuGet package manager) and install an earlier version instead:

    First un-install the newer packages:

    PM> uninstall-package CefSharp.Wpf
    PM> uninstall-package CefSharp.Common
    PM> uninstall-package cef.redist.x64
    PM> uninstall-package cef.redist.x64
    

    Then remove the existing references: right click the References folder, and select to remove CefSharp.Wpf, CefSharp.Core etc.

    And then install the older version:. Just the following command is sufficient to install everything:

    PM> install-package CefSharp.Wpf -version 39.0.0
    

    So that the list of packages now becomes:

    <?xml version="1.0" encoding="utf-8"?>
    <packages>
      <package id="cef.redist.x64" version="3.2171.2069" targetFramework="net452" />
      <package id="cef.redist.x86" version="3.2171.2069" targetFramework="net452" />
      <package id="CefSharp.Common" version="39.0.0" targetFramework="net452" />
      <package id="CefSharp.Wpf" version="39.0.0" targetFramework="net452" />
    </packages>
    

    As before, add the updates dll references to the project, but this time from the 39.0.0 folder:

    cefsharpbrowser5

    cefsharpbrowser6

    Try wiring the ‘Address’ property to an example PDF location, such as

    <Grid>
            <wpf:ChromiumWebBrowser 
                x:Name="Browser"
                Address="http://www.pdf995.com/samples/pdf.pdf" />
        </Grid>
    

    The application will then successfully browse PDF files as shown:

    cefsharpbrowser7