Patrick Hansen Patrick Hansen - 3 months ago 10
C# Question

Referencing another project in Visual Studio 2015

Here is the scenario
I have 2 C# windows form projects, Project A and Project B. Project A has a textbox and a button. When the button is pressed, whatever value is in the textbox is saved in another class. We will call this

ClassA
and it is saved through
ClassA.myString = textbox.Text;
.

public class ClassA
{
public String myString
{
get;
set;
}
}


Now project B has a button and a label. When the button is pressed, it should set the label to whatever the value that was saved into ClassA in project A. I have already established a reference through right click the project, click Add, Reference, and point to Project A from Project B. I have using ProjectA; inside my project B form, but I am unable to get the value to pull over. Below is one method I have tried that failed.

using ProjectA;

namespace projectBSolution
{
public class ProjectB
{
ClassA myClass;
public ProjectB()
{
InitializeComponent();
myClass = new ClassA();
}
private void btn_click(object sender, EventArgs e)
{
label1.Text = myClass.myString;
}
}
}


The problem with this is it does not return my value because I am initializing a new version of the class. If I do not initialize a new version though, it returns null every time. Any help is appreciated. Thank you.

Answer

If you're okay with using components of one project inside the other, but not run them as two separate executables, then it's a very simple matter. Here I'm assuming that you're using WPF, but you can apply similar technique to WinForms or any other framework you're using:

App A:

<Window x:Class="SampleApp.A.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:SampleApp.A"
        mc:Ignorable="d"
        Title="A" 
        SizeToContent="WidthAndHeight">
    <StackPanel>
        <TextBox x:Name="textBox" VerticalAlignment="Center"
                 Width="250" Margin="10" Height="30"/>
        <Button Content="Save" Width="80" Margin="10" Click="Button_Click"/>
    </StackPanel>
</Window>

using System.Windows;

namespace SampleApp.A
{
    public partial class MainWindow : Window
    {
        public string Text { get; set; }

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Text = textBox.Text;
        }
    }
}

App B:

<Window x:Class="SampleApp.B.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:SampleApp.B"
        mc:Ignorable="d"
        Title="B" 
        SizeToContent="WidthAndHeight">
    <StackPanel>
        <Label x:Name="label" VerticalAlignment="Center" 
               Width="250" Margin="10" Height="30"/>
        <Button Content="Load" Width="80" Margin="10" Click="Button_Click"/>
    </StackPanel>
</Window>
using System.Windows;

namespace SampleApp.B
{
    public partial class MainWindow : Window
    {
        private A.MainWindow A;

        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += MainWindow_Loaded;
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            A = new A.MainWindow();
            A.Show();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            label.Content = A.Text;
        }
    }
}

Note that I'm making this as plain as possible. Ideally, you would make use of INotifyPropertyChanged, MVVM and a whole bunch of other design techniques and patterns, which are clearly missing from this example.

What that example does show is that you can have one project start a window from another project and access its public properties. Of course, it's not two executables interacting. If separate executables is what you're aiming at, then the options get a bit tricky and they vary from Interop window handles to messaging and lots of things in between.

You could, for example, use IO.Pipes to handle your messaging routine. Below is an example of that, but it's incredibly crude, lacks lots of safety checks and will probably crash after a few minutes of use (I don't have time to test, as I'm heading out). It works similarly to the above example -- you type in text in App A, click on Save and then click on Load in App B. You'll notice that App A will be frozen until you read in text in App B. Like I said, this is not meant as a production app and is simply a proof of concept. I guarantee it'll do something wrong -- it's meant purely as an example to build on.

App A:

using System.IO;
using System.IO.Pipes;
using System.Windows;

namespace SampleApp.A
{
    public partial class MainWindow : Window
    {
        private NamedPipeClientStream pipeClient;
        public string Text { get; set; }

        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += MainWindow_Loaded;
            this.Closing += MainWindow_Closing;
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            pipeClient = new NamedPipeClientStream(".", "1234", PipeDirection.Out);
            pipeClient.Connect();
        }

        private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            pipeClient?.Close();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Text = textBox.Text;

            using (var sw = new StreamWriter(pipeClient))
            {
                sw.WriteLine(Text);
                sw.Flush();
            }
        }
    }
}

App B:

using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Threading.Tasks;
using System.Windows;

namespace SampleApp.B
{
    public partial class MainWindow : Window
    {
        private NamedPipeServerStream pipeServer;
        public string Text { get; set; }

        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += MainWindow_Loaded;
            this.Closing += MainWindow_Closing;
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            Process.Start(@"C:\Users\bk\Desktop\SampleApp V2\SampleApp.A\bin\Debug\SampleApp.A.exe");
            pipeServer = new NamedPipeServerStream("1234", PipeDirection.In);
            pipeServer.WaitForConnection();
        }

        private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (pipeServer.IsConnected)
            {
                pipeServer.Disconnect();
            }
            pipeServer.Close();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (pipeServer.IsConnected)
            {
                using (var sr = new StreamReader(pipeServer))
                {
                    Text = sr.ReadLine();
                }
            }

            label.Content = Text;
        }
    }
}

Ideally, you would have a loop or an event that keeps track of incoming stream content, rather than having to press the button to get it.

On top of this option, you have lots of other great options such as WCF and MSMQ. They're a lot more robust and worth learning.