2

I have two WPF applications (Application1 and Application2) and Application1 adds a user to a Users.xml file and Application2 displays all the Names from Users.xml to a Label. To refresh the Label in Application2, I have to press the Refresh button in my current implementation. I want to make a mechanism so that whenever I add a user with Application1, the Application2 automatically updates the Label. One way I could achieve this is whenever Application1 adds a user, it sets some flag in XML file (for example IsSync=false) and Application2 constantly monitor the flag and whenever it sees IsSync=false, it updates the Label and sets IsSync=true. But I would like to know if there are any other best ways (maybe publisher/subscriber way by handling the Refresh button of Application2 from Application1) to achieve this. Could you please help me to achieve this? I have attached both XAML/code below:

enter image description here

Application1

XAML

<Window x:Class="Application1.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:Application1"
        mc:Ignorable="d"
        Title="Application1" Height="500" Width="500">
    <Grid>
        <Label Name="lblUserName" Content="Name" HorizontalAlignment="Left" Margin="42,60,0,0" VerticalAlignment="Top"/>
        <TextBox Name="txtUserName" HorizontalAlignment="Left" Height="23" Margin="91,60,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="300"/>
        <Button Name="btnAdd" Content="Add" HorizontalAlignment="Left" Margin="310,110,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
    </Grid>
</Window>

Codebehind

using System.IO;
using System.Windows;
using System.Xml;
using System.Xml.Linq;

namespace Application1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private static string fullPath = "C:\\Files\\Users.xml";

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (!string.IsNullOrWhiteSpace(txtUserName.Text))
            {
                if (!File.Exists(fullPath))
                {
                    // If Users.xml is not exists, create a file and add Textbox Name to the file
                    CreateUsersXMLAndAddUser();
                    txtUserName.Text = "";
                }
                else
                {
                    // Add Textbox name to the Users.xml
                    AddUser();
                    txtUserName.Text = "";
                }
            }
            else
            {
                MessageBox.Show(this, "Name can not be empty", "Required", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void AddUser()
        {
            XElement xEle = XElement.Load(fullPath);
            xEle.Add(new XElement("User", new XAttribute("Name", txtUserName.Text.Trim())));
            xEle.Save(fullPath);
        }

        private void CreateUsersXMLAndAddUser()
        {
            XDocument xDoc = new XDocument(
                                      new XDeclaration("1.0", "UTF-8", null),
                                      new XElement("Users",
                                              new XElement("User",
                                              new XAttribute("Name", txtUserName.Text.Trim())
                                              )));

            StringWriter sw = new StringWriter();
            XmlWriter xWrite = XmlWriter.Create(sw);
            xDoc.Save(xWrite);
            xWrite.Close();
            xDoc.Save(fullPath);
        }
    }
}

Application2

XAML

<Window x:Class="Application2.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:Application2"
        mc:Ignorable="d"
        Title="Application2" Height="500" Width="600">
    <Grid>
        <Label Name="lblUsers" FontSize="20" FontWeight="UltraBold" HorizontalAlignment="Left" Margin="20,20,0,0" VerticalAlignment="Top"/>
        <Button Name="btnRefreshUsers" Content="Refresh" FontSize="20" FontWeight="UltraBold" HorizontalAlignment="Left" Margin="450,39,0,0" VerticalAlignment="Top" Height="100" Width="100" Click="btnRefreshUsers_Click"/>
    </Grid>
</Window>

Codebehind

using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows;
using System.Xml.Linq;

namespace Application2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private static string fullPath = "C:\\Files\\Users.xml";
        StringBuilder userList;

        public MainWindow()
        {
            InitializeComponent();
            userList = new StringBuilder();

            lblUsers.Content = string.Empty;
            lblUsers.Content = LoadUsers();
        }

        private StringBuilder LoadUsers()
        {
            userList.AppendLine("  Users  ");
            userList.AppendLine("---------");

            if (File.Exists(fullPath))
            {
                XElement xelement = XElement.Load(fullPath);
                IEnumerable<XElement> users = xelement.Elements();

                foreach (var user in users)
                {
                    userList.AppendLine(user.Attribute("Name").Value);
                }              
            }
            else
            {
                userList.AppendLine("Nothing to show ...");
            }
            return userList;
        }

        private void btnRefreshUsers_Click(object sender, RoutedEventArgs e)
        {
            userList.Clear();
            lblUsers.Content = string.Empty;
            lblUsers.Content = LoadUsers();
        }
    }
}
Simant
  • 3,142
  • 4
  • 32
  • 61
  • Is this just a practice/experimentation project? Because, as it looks like you probably would be better off combining those two applications into one single application... –  Nov 25 '18 at 12:02
  • Possible duplicate of [Notification when a file changes?](https://stackoverflow.com/questions/721714/notification-when-a-file-changes) –  Nov 25 '18 at 12:06
  • @elgonzo I can't combine both apps because there are separate applications in the real world and this code is just experimental code to achieve this in a real scenario. – Simant Nov 25 '18 at 12:07
  • By the way, a related issue you will have to address is that both applications need to gracefully handle situations where the file they want to read from or write to is locked. It is no good if one application crashes (or does other silly things) just because the other application is doing something with the file just at the same time. –  Nov 25 '18 at 12:11
  • Is using a proper database an option? Combined with [Signalr](https://github.com/Expecho/Self-Hosted-SignalR) for notifications – Peter Bons Nov 25 '18 at 12:26

2 Answers2

1

There are several ways to communicate between processes:

  • MSMQ (Microsoft MessageQueue)
  • Socket Programming
  • Named Pipeline
  • Web Services (WCF , ...)

Theoretically at the low level of communication most of this technologies are using sockets as the main part. so Socket Programming is the low level communication, you have more control and you need to do more to get this to work.

I have read a good answer on SO:

IPC Mechanisms in C# - Usage and Best Practices

RezaNoei
  • 1,266
  • 1
  • 8
  • 24
0

I have implemented FileSystemWatcher to achieve my target. Here is changed the complete code in Application2 to listen to the changes in Users.xml.

Application2

Code behind

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows;
using System.Xml.Linq;

namespace Application2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private static string fullPath = "C:\\Files\\Users.xml";
        StringBuilder userList;

        public MainWindow()
        {
            InitializeComponent();
            userList = new StringBuilder();

            lblUsers.Content = string.Empty;
            lblUsers.Content = LoadUsers();

            CreateFileWatcher(@"C:\\Files");
        }

        private StringBuilder LoadUsers()
        {
            userList.AppendLine("  Users  ");
            userList.AppendLine("---------");

            if (File.Exists(fullPath))
            {
                XElement xelement = XElement.Load(fullPath);
                IEnumerable<XElement> users = xelement.Elements();

                foreach (var user in users)
                {
                    userList.AppendLine(user.Attribute("Name").Value);
                }              
            }
            else
            {
                userList.AppendLine("Nothing to show ...");
            }
            return userList;
        }

        private void btnRefreshUsers_Click(object sender, RoutedEventArgs e)
        {
            userList.Clear();
            lblUsers.Content = string.Empty;
            lblUsers.Content = LoadUsers();
        }

        private void CreateFileWatcher(string path)
        {
            // Create a new FileSystemWatcher and set its properties.
            FileSystemWatcher watcher = new FileSystemWatcher();
            watcher.Path = path;
            /* Watch for changes in LastAccess and LastWrite times, and the renaming of files or directories. */
            watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
               | NotifyFilters.FileName | NotifyFilters.DirectoryName;
            // Only watch text files.
            watcher.Filter = "Users.xml";

            // Add event handlers.
            watcher.Changed += new FileSystemEventHandler(OnChanged);
            watcher.Created += new FileSystemEventHandler(OnChanged);
            watcher.Deleted += new FileSystemEventHandler(OnChanged);

            // Begin watching.
            watcher.EnableRaisingEvents = true;
        }

        // Define the event handlers.
        private  void OnChanged(object source, FileSystemEventArgs e)
        {
            // Specify what is done when a file is changed, created, or deleted.
            lblUsers.Dispatcher.Invoke(new Action(delegate ()
            {
                userList.Clear();
                lblUsers.Content = string.Empty;
                lblUsers.Content = LoadUsers();
            }), System.Windows.Threading.DispatcherPriority.Normal);
        }      
    }
}
Simant
  • 3,142
  • 4
  • 32
  • 61