Scope

The solution in this article will implement the MVVM pattern and will use the MVVMLight Toolkit.

Introduction

Bluetooth is an industry-standard protocol that enables wireless connectivity for computers, handheld devices, mobile phones and other devices.

Bluetooth is designed for use by C/C++ programmers. Some Bluetooth features are available with Windows Sockets. Familiarity with Microsoft Windows networking and Windows Sockets programming is required. The article Bluetooth Programming with Windows Sockets describes how to use Windows Sockets functions and structures to program a Bluetooth application and provide the Bluetooth connection sample. But in a first attempt it does not look so nice when our goal is to create a WPF application.

In Codeplex, there is a project called 32feet.NET. This project is a shared-source project to make personal area networking technologies such as Bluetooth, Infrared (IrDA) and more, easily accessible from .NET code. It supports desktop, mobile or embedded systems.

32feet.NET is available on Nuget and for desktop apps the reference is 32feet.NET 3.5.0 Nuget Package. That is the version we will use in the sample we will create.

Description

The WPF application will have two "modes": Sender and Receiver. Where the "Sender" has the responsibility to send messages and the "Receiver" will get the messages sent by "Sender".

Let's start!

Creating the project

First create the WPF application in Visual Studio.

wpf application

Then install the nugget packages: MVVMLight and 32feet.Net, as in the following:

manage NuGet Packages

Installing the MVVM Light Toolkit:

MVVM Light

Installing 32feet.Net:

32feet net

At the end our solution will have the base structure to implement MVVM, that MVVM Light installed using Nuget.

Now we need to define the model for the device that is required when we get the list of the devices around us with Bluetooth on.

The Model

The model is defined by the Device class that represents the structure for the device around us. The implementation is:

  1. public sealed class Device
  2. {
  3. /// <summary>
  4. /// Gets or sets the device name.
  5. /// </summary>
  6. /// <value>
  7. /// The device name.
  8. /// </value>
  9. public string DeviceName { get; set; }
  10. /// <summary>
  11. /// Gets or sets a value indicating whether authenticated.
  12. /// </summary>
  13. /// <value>
  14. /// The authenticated.
  15. /// </value>
  16. public bool IsAuthenticated { get; set; }
  17. /// <summary>
  18. /// Gets or sets a value indicating whether is connected.
  19. /// </summary>
  20. /// <value>
  21. /// The is connected.
  22. /// </value>
  23. public bool IsConnected { get; set; }
  24. /// <summary>
  25. /// Gets or sets the nap.
  26. /// </summary>
  27. /// <value>
  28. /// The nap.
  29. /// </value>
  30. public ushort Nap { get; set; }
  31. /// <summary>
  32. /// Gets or sets the sap.
  33. /// </summary>
  34. /// <value>
  35. /// The sap.
  36. /// </value>
  37. public uint Sap { get; set; }
  38. /// <summary>
  39. /// Gets or sets the last seen.
  40. /// </summary>
  41. /// <value>
  42. /// The last seen.
  43. /// </value>
  44. public DateTime LastSeen { get; set; }
  45. /// <summary>
  46. /// Gets or sets the last used.
  47. /// </summary>
  48. /// <value>
  49. /// The last used.
  50. /// </value>
  51. public DateTime LastUsed { get; set; }
  52. /// <summary>
  53. /// Gets or sets a value indicating whether remembered.
  54. /// </summary>
  55. /// <value>
  56. /// The remembered.
  57. /// </value>
  58. public bool Remembered { get; set; }
  59. /// <summary>
  60. /// Gets or sets the device info.
  61. /// </summary>
  62. /// <value>
  63. /// The device info.
  64. /// </value>
  65. public BluetoothDeviceInfo DeviceInfo { get; set; }
  66. /// <summary>
  67. /// Initializes a new instance of the <see cref="Device"/> class.
  68. /// </summary>
  69. /// <param name="device_info">
  70. /// The device_info.
  71. /// </param>
  72. public Device(BluetoothDeviceInfo device_info)
  73. {
  74. if (device_info != null)
  75. {
  76. DeviceInfo = device_info;
  77. IsAuthenticated = device_info.Authenticated;
  78. IsConnected = device_info.Connected;
  79. DeviceName = device_info.DeviceName;
  80. LastSeen = device_info.LastSeen;
  81. LastUsed = device_info.LastUsed;
  82. Nap = device_info.DeviceAddress.Nap;
  83. Sap = device_info.DeviceAddress.Sap;
  84. Remembered = device_info.Remembered;
  85. }
  86. }
  87. /// <summary>
  88. /// The to string.
  89. /// </summary>
  90. /// <returns>
  91. /// The <see cref="string"/>.
  92. /// </returns>
  93. public override string ToString()
  94. {
  95. return DeviceName;
  96. }
  97. }
In a class diagram we will have:

divice

The Services

The services in the application will define the features for the "Sender" and for the "Receiver". These classes will be injected into the view model using the ServiceLocator and the setup is defined in the ViewModelLocator constructor.

To connect the "Sender" and the "Receiver", we need to define a Guid that is set to the ServiceClassId and it is the key for the communication. When the "Sender" sends a message using the ServiceClassID X only the "Receiver" that knows the ServiceClassID X will get the data, any other "Receiver" that only knows the ServiceClassID Y, for example, will not receive the data.

The ReceiverBluetoothService

The ReceiverBluetoothService defines how the "Receiver" will receive the data from the "Sender". Since it is used for a thread that will run and will be listening for data.

The implementation of this class is something like:
  1. public class ReceiverBluetoothService : ObservableObject, IDisposable, IReceiverBluetoothService
  2. {
  3. private readonly Guid _serviceClassId;
  4. private Action<string> _responseAction;
  5. private BluetoothListener _listener;
  6. private CancellationTokenSource _cancelSource;
  7. private bool _wasStarted;
  8. private string _status;
  9. /// <summary>
  10. /// Initializes a new instance of the <see cref="ReceiverBluetoothService" /> class.
  11. /// </summary>
  12. public ReceiverBluetoothService()
  13. {
  14. _serviceClassId = new Guid("0e6114d0-8a2e-477a-8502-298d1ff4b4ba");
  15. }
  16. /// <summary>
  17. /// Gets or sets a value indicating whether was started.
  18. /// </summary>
  19. /// <value>
  20. /// The was started.
  21. /// </value>
  22. public bool WasStarted
  23. {
  24. get { return _wasStarted; }
  25. set { Set(() => WasStarted, ref _wasStarted, value); }
  26. }
  27. /// <summary>
  28. /// Starts the listening from Senders.
  29. /// </summary>
  30. /// <param name="reportAction">
  31. /// The report Action.
  32. /// </param>
  33. public void Start(Action<string> reportAction)
  34. {
  35. WasStarted = true;
  36. _responseAction = reportAction;
  37. if (_cancelSource != null && _listener != null)
  38. {
  39. Dispose(true);
  40. }
  41. _listener = new BluetoothListener(_serviceClassId)
  42. {
  43. ServiceName = "MyService"
  44. };
  45. _listener.Start();
  46. _cancelSource = new CancellationTokenSource();
  47. Task.Run(() => Listener(_cancelSource));
  48. }
  49. /// <summary>
  50. /// Stops the listening from Senders.
  51. /// </summary>
  52. public void Stop()
  53. {
  54. WasStarted = false;
  55. _cancelSource.Cancel();
  56. }
  57. /// <summary>
  58. /// Listeners the accept bluetooth client.
  59. /// </summary>
  60. /// <param name="token">
  61. /// The token.
  62. /// </param>
  63. private void Listener(CancellationTokenSource token)
  64. {
  65. try
  66. {
  67. while (true)
  68. {
  69. using (var client = _listener.AcceptBluetoothClient())
  70. {
  71. if (token.IsCancellationRequested)
  72. {
  73. return;
  74. }
  75. using (var streamReader = new StreamReader(client.GetStream()))
  76. {
  77. try
  78. {
  79. var content = streamReader.ReadToEnd();
  80. if (!string.IsNullOrEmpty(content))
  81. {
  82. _responseAction(content);
  83. }
  84. }
  85. catch (IOException)
  86. {
  87. client.Close();
  88. break;
  89. }
  90. }
  91. }
  92. }
  93. }
  94. catch (Exception exception)
  95. {
  96. // todo handle the exception
  97. // for the sample it will be ignored
  98. }
  99. }
  100. /// <summary>
  101. /// The dispose.
  102. /// </summary>
  103. public void Dispose()
  104. {
  105. Dispose(true);
  106. GC.SuppressFinalize(this);
  107. }
  108. /// <summary>
  109. /// The dispose.
  110. /// </summary>
  111. /// <param name="disposing">
  112. /// The disposing.
  113. /// </param>
  114. protected virtual void Dispose(bool disposing)
  115. {
  116. if (disposing)
  117. {
  118. if (_cancelSource != null)
  119. {
  120. _listener.Stop();
  121. _listener = null;
  122. _cancelSource.Dispose();
  123. _cancelSource = null;
  124. }
  125. }
  126. }
  127. }
In the Start method we need to define an action that will be used to report the data received in ViewModel. We could use an event or the Message feature from MVVMLight.

In the Listener method that is running in another thread, we defined a CancellationTokenSource that will be used to stop the process listening for data.

Note: The "Receiver" allows the starting or stopping of the process listening for data. If a "Sender" sends data but the "Receiver" does not allow for listening, the "Sender" will send the data but the "Receiver" will not get it.

The SenderBluetoothService

The SenderBluetoothService defines how the "Sender" will send the data, but it is required to select a device that is available. It is not possible to filter for devices that know the ServiceClassId and the name of the device for where the "Sender" wants to send the data should be known.

The implementation of this class is something like:
  1. public sealed class SenderBluetoothService : ISenderBluetoothService
  2. {
  3. private readonly Guid _serviceClassId;
  4. /// <summary>
  5. /// Initializes a new instance of the <see cref="SenderBluetoothService"/> class.
  6. /// </summary>
  7. public SenderBluetoothService()
  8. {
  9. // this guid is random, only need to match in Sender & Receiver
  10. // this is like a "key" for the connection!
  11. _serviceClassId = new Guid("0e6114d0-8a2e-477a-8502-298d1ff4b4ba");
  12. }
  13. /// <summary>
  14. /// Gets the devices.
  15. /// </summary>
  16. /// <returns>The list of the devices.</returns>
  17. public async Task<IList<Device>> GetDevices()
  18. {
  19. // for not block the UI it will run in a different threat
  20. var task = Task.Run(() =>
  21. {
  22. var devices = new List<Device>();
  23. using (var bluetoothClient = new BluetoothClient())
  24. {
  25. var array = bluetoothClient.DiscoverDevices();
  26. var count = array.Length;
  27. for (var i = 0; i < count; i++)
  28. {
  29. devices.Add(new Device(array[i]));
  30. }
  31. }
  32. return devices;
  33. });
  34. return await task;
  35. }
  36. /// <summary>
  37. /// Sends the data to the Receiver.
  38. /// </summary>
  39. /// <param name="device">The device.</param>
  40. /// <param name="content">The content.</param>
  41. /// <returns>If was sent or not.</returns>
  42. public async Task<bool> Send(Device device, string content)
  43. {
  44. if (device == null)
  45. {
  46. throw new ArgumentNullException("device");
  47. }
  48. if (string.IsNullOrEmpty(content))
  49. {
  50. throw new ArgumentNullException("content");
  51. }
  52. // for not block the UI it will run in a different threat
  53. var task = Task.Run(() =>
  54. {
  55. using (var bluetoothClient = new BluetoothClient())
  56. {
  57. try
  58. {
  59. var ep = new BluetoothEndPoint(device.DeviceInfo.DeviceAddress, _serviceClassId);
  60. // connecting
  61. bluetoothClient.Connect(ep);
  62. // get stream for send the data
  63. var bluetoothStream = bluetoothClient.GetStream();
  64. // if all is ok to send
  65. if (bluetoothClient.Connected && bluetoothStream != null)
  66. {
  67. // write the data in the stream
  68. var buffer = System.Text.Encoding.UTF8.GetBytes(content);
  69. bluetoothStream.Write(buffer, 0, buffer.Length);
  70. bluetoothStream.Flush();
  71. bluetoothStream.Close();
  72. return true;
  73. }
  74. return false;
  75. }
  76. catch
  77. {
  78. // the error will be ignored and the send data will report as not sent
  79. // for understood the type of the error, handle the exception
  80. }
  81. }
  82. return false;
  83. });
  84. return await task;
  85. }
  86. }
In the Send method, when we are connected to the selected device, we will be able to write in a stream that is received by "Receiver".

The ViewModel

We will define the following view models: ReceiverViewModel, SenderViewModel and MainViewModel that will be binding to the DataContext in ReceiverView, SenderView and MainWindow respectively.

The ReceiverViewModel
  1. public sealed class ReceiverViewModel : ViewModelBase
  2. {
  3. private readonly IReceiverBluetoothService _receiverBluetoothService;
  4. private string _data;
  5. private bool _isStarEnabled;
  6. private string _status;
  7. /// <summary>
  8. /// Initializes a new instance of the <see cref="ReceiverViewModel" /> class.
  9. /// </summary>
  10. /// <param name="receiverBluetoothService">The Receiver bluetooth service.</param>
  11. public ReceiverViewModel(IReceiverBluetoothService receiverBluetoothService)
  12. {
  13. _receiverBluetoothService = receiverBluetoothService;
  14. _receiverBluetoothService.PropertyChanged += ReceiverBluetoothService_PropertyChanged;
  15. IsStarEnabled = true;
  16. Data = "N/D";
  17. Status = "N/D";
  18. StartCommand = new RelayCommand(() =>
  19. {
  20. _receiverBluetoothService.Start(SetData);
  21. IsStarEnabled = false;
  22. Data = "Can receive data.";
  23. });
  24. StopCommand = new RelayCommand(() =>
  25. {
  26. _receiverBluetoothService.Stop();
  27. IsStarEnabled = true;
  28. Data = "Cannot receive data.";
  29. });
  30. Messenger.Default.Register<Message>(this, ResetAll);
  31. }
  32. /// <summary>
  33. /// Resets all.
  34. /// </summary>
  35. /// <param name="message">The message.</param>
  36. private void ResetAll(Message message)
  37. {
  38. if (!message.IsToShowDevices)
  39. {
  40. if (_receiverBluetoothService.WasStarted)
  41. {
  42. _receiverBluetoothService.Stop();
  43. }
  44. IsStarEnabled = true;
  45. Data = "N/D";
  46. Status = "N/D";
  47. }
  48. }
  49. /// <summary>
  50. /// The set data received.
  51. /// </summary>
  52. /// <param name="data">
  53. /// The data.
  54. /// </param>
  55. public void SetData(string data)
  56. {
  57. Data = data;
  58. }
  59. /// <summary>
  60. /// Gets or sets the data.
  61. /// </summary>
  62. /// <value>
  63. /// The data received.
  64. /// </value>
  65. public string Data
  66. {
  67. get { return _data; }
  68. set { Set(() => Data, ref _data, value); }
  69. }
  70. /// <summary>
  71. /// Gets the start command.
  72. /// </summary>
  73. /// <value>
  74. /// The start command.
  75. /// </value>
  76. public ICommand StartCommand { get; private set; }
  77. /// <summary>
  78. /// Gets the stop command.
  79. /// </summary>
  80. /// <value>
  81. /// The stop command.
  82. /// </value>
  83. public ICommand StopCommand { get; private set; }
  84. /// <summary>
  85. /// Gets or sets a value indicating whether is star enabled.
  86. /// </summary>
  87. /// <value>
  88. /// The is star enabled.
  89. /// </value>
  90. public bool IsStarEnabled
  91. {
  92. get
  93. {
  94. return _isStarEnabled;
  95. }
  96. set
  97. {
  98. Set(() => IsStarEnabled, ref _isStarEnabled, value);
  99. RaisePropertyChanged(() => IsStopEnabled);
  100. }
  101. }
  102. /// <summary>
  103. /// Gets or sets a value indicating whether is stop enabled.
  104. /// </summary>
  105. /// <value>
  106. /// The is stop enabled.
  107. /// </value>
  108. public bool IsStopEnabled
  109. {
  110. get
  111. {
  112. return !_isStarEnabled;
  113. }
  114. set
  115. {
  116. Set(() => IsStopEnabled, ref _isStarEnabled, !value);
  117. RaisePropertyChanged(() => IsStarEnabled);
  118. }
  119. }
  120. /// <summary>
  121. /// Gets or sets the status.
  122. /// </summary>
  123. /// <value>The status.</value>
  124. public string Status
  125. {
  126. get { return _status; }
  127. set { Set(() => Status, ref _status, value); }
  128. }
  129. /// <summary>
  130. /// Handles the PropertyChanged event of the ReceiverBluetoothService control.
  131. /// </summary>
  132. /// <param name="sender">The source of the event.</param>
  133. /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
  134. private void ReceiverBluetoothService_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
  135. {
  136. if (e.PropertyName == "WasStarted")
  137. {
  138. IsStarEnabled = true;
  139. }
  140. }
  141. }
The SenderViewModel
  1. public sealed class SenderViewModel : ViewModelBase
  2. {
  3. private readonly ISenderBluetoothService _senderBluetoothService;
  4. private string _data;
  5. private Device _selectDevice;
  6. private string _resultValue;
  7. /// <summary>
  8. /// Initializes a new instance of the <see cref="SenderViewModel"/> class.
  9. /// </summary>
  10. /// <param name="senderBluetoothService">
  11. /// The Sender bluetooth service.
  12. /// </param>
  13. public SenderViewModel(ISenderBluetoothService senderBluetoothService)
  14. {
  15. _senderBluetoothService = senderBluetoothService;
  16. ResultValue = "N/D";
  17. SendCommand = new RelayCommand(
  18. SendData,
  19. () => !string.IsNullOrEmpty(Data) && SelectDevice != null && SelectDevice.DeviceInfo != null);
  20. Devices = new ObservableCollection<Device>
  21. {
  22. new Device(null) { DeviceName = "Searching..." }
  23. };
  24. Messenger.Default.Register<Message>(this, ShowDevice);
  25. }
  26. /// <summary>
  27. /// Gets or sets the devices.
  28. /// </summary>
  29. /// <value>
  30. /// The devices.
  31. /// </value>
  32. public ObservableCollection<Device> Devices
  33. {
  34. get; set;
  35. }
  36. /// <summary>
  37. /// Gets or sets the select device.
  38. /// </summary>
  39. /// <value>
  40. /// The select device.
  41. /// </value>
  42. public Device SelectDevice
  43. {
  44. get { return _selectDevice; }
  45. set { Set(() => SelectDevice, ref _selectDevice, value); }
  46. }
  47. /// <summary>
  48. /// Gets or sets the data.
  49. /// </summary>
  50. /// <value>
  51. /// The data.
  52. /// </value>
  53. public string Data
  54. {
  55. get { return _data; }
  56. set { Set(() => Data, ref _data, value); }
  57. }
  58. /// <summary>
  59. /// Gets or sets the result value.
  60. /// </summary>
  61. /// <value>
  62. /// The result value.
  63. /// </value>
  64. public string ResultValue
  65. {
  66. get { return _resultValue; }
  67. set { Set(() => ResultValue, ref _resultValue, value); }
  68. }
  69. /// <summary>
  70. /// Gets the send command.
  71. /// </summary>
  72. /// <value>
  73. /// The send command.
  74. /// </value>
  75. public ICommand SendCommand { get; private set; }
  76. private async void SendData()
  77. {
  78. ResultValue = "N/D";
  79. var wasSent = await _senderBluetoothService.Send(SelectDevice, Data);
  80. if (wasSent)
  81. {
  82. ResultValue = "The data was sent.";
  83. }
  84. else
  85. {
  86. ResultValue = "The data was not sent.";
  87. }
  88. }
  89. /// <summary>
  90. /// Shows the device.
  91. /// </summary>
  92. /// <param name="deviceMessage">The device message.</param>
  93. private async void ShowDevice(Message deviceMessage)
  94. {
  95. if (deviceMessage.IsToShowDevices)
  96. {
  97. var items = await _senderBluetoothService.GetDevices();
  98. Devices.Clear();
  99. Devices.Add(items);
  100. Data = string.Empty;
  101. }
  102. }
  103. }
The MainViewModel
  1. public sealed class MainViewModel : ViewModelBase
  2. {
  3. private bool _isReceiver;
  4. /// <summary>
  5. /// Initializes a new instance of the <see cref="MainViewModel"/> class.
  6. /// </summary>
  7. public MainViewModel()
  8. {
  9. PropertyChanged += MainViewModelPropertyChanged;
  10. IsSender = false;
  11. }
  12. /// <summary>
  13. /// Gets or sets a value indicating whether is Receiver.
  14. /// </summary>
  15. /// <value>
  16. /// The is Receiver.
  17. /// </value>
  18. public bool IsReceiver
  19. {
  20. get
  21. {
  22. return _isReceiver;
  23. }
  24. set
  25. {
  26. Set(() => IsReceiver, ref _isReceiver, value);
  27. RaisePropertyChanged(() => IsSender);
  28. }
  29. }
  30. /// <summary>
  31. /// Gets or sets a value indicating whether is Sender.
  32. /// </summary>
  33. /// <value>
  34. /// The is Sender.
  35. /// </value>
  36. public bool IsSender
  37. {
  38. get
  39. {
  40. return !_isReceiver;
  41. }
  42. set
  43. {
  44. Set(() => IsSender, ref _isReceiver, !value);
  45. RaisePropertyChanged(() => IsReceiver);
  46. }
  47. }
  48. /// <summary>
  49. /// Gets or sets the Receiver visibility.
  50. /// </summary>
  51. /// <value>
  52. /// The Receiver visibility.
  53. /// </value>
  54. public Visibility ReceiverVisibility
  55. {
  56. get
  57. {
  58. return _isReceiver ? Visibility.Visible : Visibility.Collapsed;
  59. }
  60. set
  61. {
  62. _isReceiver = value == Visibility.Visible;
  63. }
  64. }
  65. /// <summary>
  66. /// Gets or sets the Sender visibility.
  67. /// </summary>
  68. /// <value>
  69. /// The Sender visibility.
  70. /// </value>
  71. public Visibility SenderVisibility
  72. {
  73. get
  74. {
  75. return !_isReceiver ? Visibility.Visible : Visibility.Collapsed;
  76. }
  77. set
  78. {
  79. _isReceiver = value != Visibility.Visible;
  80. }
  81. }
  82. /// <summary>
  83. /// Mains the view model property changed.
  84. /// </summary>
  85. /// <param name="sender">The sender.</param>
  86. /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
  87. private void MainViewModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
  88. {
  89. if (e.PropertyName == "IsReceiver" || e.PropertyName == "IsSender")
  90. {
  91. RaisePropertyChanged(() => ReceiverVisibility);
  92. RaisePropertyChanged(() => SenderVisibility);
  93. if (e.PropertyName == "IsReceiver")
  94. {
  95. Messenger.Default.Send(IsSender ? new Message(true) : new Message(false));
  96. }
  97. }
  98. }
  99. }
The UI

The MainWindow will be the starting point for the application and will contain the two user controls: ReceiverView and SenderView that will be shown if the user wants to be a "Sender" or a "Receiver".

The ReceiverView.xaml

The implementation will be something like:
  1. <UserControl x:Class="BluetoothSample.Views.ReceiverView"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  5. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  6. DataContext="{Binding ReceiverViewModel, Source={StaticResource Locator}}"
  7. mc:Ignorable="d"
  8. d:DesignHeight="300" d:DesignWidth="400">
  9. <StackPanel Margin="20" Orientation="Vertical">
  10. <TextBlock>I am the Receiver</TextBlock>
  11. <StackPanel Orientation="Horizontal">
  12. <Button Margin="0,10,0,0" Width="80" Command="{Binding StartCommand}" IsEnabled="{Binding IsStarEnabled}" Content="Start"/>
  13. <Button Margin="20,10,0,0" Width="80" Command="{Binding StopCommand}" IsEnabled="{Binding IsStopEnabled}" Content="Stop"/>
  14. </StackPanel>
  15. <TextBlock Margin="00,20,0,0" Text="Data:"/>
  16. <TextBlock Margin="00,20,0,0" Text="{Binding Data}"/>
  17. </StackPanel>
  18. </UserControl>
The SenderView.xaml

The implementation will be something like:
  1. <UserControl x:Class="BluetoothSample.Views.SenderView"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6. DataContext="{Binding SenderViewModel,
  7. Source={StaticResource Locator}}"
  8. d:DesignHeight="400"
  9. d:DesignWidth="400"
  10. mc:Ignorable="d">
  11. <StackPanel Margin="20" Orientation="Vertical">
  12. <TextBlock>I am the Sender.</TextBlock>
  13. <TextBlock Margin="0,20,0,0">Select one device:</TextBlock>
  14. <ListBox Width="200"
  15. Height="100"
  16. MaxWidth="200"
  17. MaxHeight="100"
  18. Margin="0,20,0,0"
  19. HorizontalAlignment="Left"
  20. ItemsSource="{Binding Devices}"
  21. SelectedItem="{Binding SelectDevice}" />
  22. <TextBlock Margin="0,20,0,0" Text="Write the data to send:" />
  23. <TextBox Margin="0,20,20,0" Text="{Binding Data, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
  24. <Button Width="80"
  25. Margin="0,20,20,0"
  26. HorizontalAlignment="Right"
  27. Command="{Binding SendCommand}"
  28. Content="Send" />
  29. <TextBlock Margin="0,20,0,0" TextWrapping="Wrap">
  30. Result:<Run Text="{Binding ResultValue}" />
  31. </TextBlock>
  32. </StackPanel>
  33. </UserControl>
The MainWindow.xaml

The MainWindow will show/hide the user controls defined. The implementation is defined by:
  1. <Window x:Class="BluetoothSample.MainWindow"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:views="clr-namespace:BluetoothSample.Views"
  5. DataContext="{Binding Main, Source={StaticResource Locator}}"
  6. Title="Bluetooth Sample"
  7. MinWidth="600" MinHeight="560"
  8. MaxWidth="600" MaxHeight="560">
  9. <StackPanel Orientation="Vertical">
  10. <GroupBox Margin="10,10,10,0" Header="Choose the type:">
  11. <StackPanel Orientation="Horizontal">
  12. <RadioButton Margin="20" IsChecked="{Binding IsReceiver, Mode=TwoWay}">Receiver - will receive data from Sender</RadioButton>
  13. <RadioButton Margin="20" IsChecked="{Binding IsSender, Mode=TwoWay}">Sender - will send data for the Receiver</RadioButton>
  14. </StackPanel>
  15. </GroupBox>
  16. <GroupBox Margin="10,10,10,0" Header="Dashboard">
  17. <StackPanel Orientation="Vertical">
  18. <!-- visibility binding not worked in user control and
  19. for this reason was added the stackpanel for each usercontrol-->
  20. <StackPanel Visibility="{Binding ReceiverVisibility}">
  21. <views:ReceiverView Height="390" x:Name="ReceiverView"/>
  22. </StackPanel>
  23. <StackPanel Visibility="{Binding SenderVisibility}">
  24. <views:SenderView Height="390" x:Name="SenderView" />
  25. </StackPanel>
  26. </StackPanel>
  27. </GroupBox>
  28. </StackPanel>
  29. </Window>
Note: To have a nice look, we will add the Modern UI nugget package. To see more about it, please read the following article: Modern UI for WPF application by example (Blank Window).

The ViewModelLocator

The ViewModelLocator will be a static resource for the application and is defined in App.xaml, as in the following;
  1. <vm:ViewModelLocator xmlns:vm="clr-namespace:BluetoothSample.ViewModel"
  2. x:Key="Locator"
  3. d:IsDataSource="True" />
This class is where the setup for the view model and service are made and the implementation is something like:
  1. public class ViewModelLocator
  2. {
  3. /// <summary>
  4. /// Initializes a new instance of the ViewModelLocator class.
  5. /// </summary>
  6. public ViewModelLocator()
  7. {
  8. ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
  9. SimpleIoc.Default.Register<IReceiverBluetoothService, ReceiverBluetoothService>();
  10. SimpleIoc.Default.Register<ISenderBluetoothService, SenderBluetoothService>();
  11. SimpleIoc.Default.Register<MainViewModel>();
  12. SimpleIoc.Default.Register<ReceiverViewModel>();
  13. SimpleIoc.Default.Register<SenderViewModel>();
  14. }
  15. /// <summary>
  16. /// Gets the main.
  17. /// </summary>
  18. /// <value>The main.</value>
  19. public MainViewModel Main
  20. {
  21. get
  22. {
  23. return ServiceLocator.Current.GetInstance<MainViewModel>();
  24. }
  25. }
  26. /// <summary>
  27. /// Gets the Receiver view model.
  28. /// </summary>
  29. /// <value>The Receiver view model.</value>
  30. public ReceiverViewModel ReceiverViewModel
  31. {
  32. get
  33. {
  34. return ServiceLocator.Current.GetInstance<ReceiverViewModel>();
  35. }
  36. }
  37. /// <summary>
  38. /// Gets the Sender view model.
  39. /// </summary>
  40. /// <value>The Sender view model.</value>
  41. public SenderViewModel SenderViewModel
  42. {
  43. get
  44. {
  45. return ServiceLocator.Current.GetInstance<SenderViewModel>();
  46. }
  47. }
  48. /// <summary>
  49. /// Cleanups this instance.
  50. /// </summary>
  51. public static void Cleanup()
  52. {
  53. // TODO Clear the ViewModels
  54. }
  55. }
Running the application

To test the application we need two devices, where in the first device we will run as "Sender" and in the second device we will run as "Receiver".

The "Receiver" can start listening as in the following:

bluetooth sample

The "Sender" is searching for available devices as in the following:

choose the type

The "Receiver" begins to listen as in the following:

Receiver

The "Sender" can select a device for sending the message as in the following:

select a device for send the message

The "Sender" will send a message for the selected device as in the following:

send a message for the selected device

The "Receiver" received the data sent by "Sender" as in the following:

received data sent by Sender

Conclusion

In conclusion, we can conclude the 32feet.Net is a great library to get all the devices around with Bluetooth on and to send data using Bluetooth. The library has a great documentation but could have more samples that we could run to test the features provided.

Another point that the developer should be aware of is the fact the project hasn´t been updated since 2012 but everyone can use the source code if needed, to fix any issue.

Source Code

The complete source code can be found in: Bluetooth Sample using 32feet.Net.

Credits

Thanks to Pedro Lamas and Peter Foot for helping me to make it work!