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.
Then install the nugget packages: MVVMLight and 32feet.Net, as in the following:
Installing the MVVM Light Toolkit:
Installing 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:
In a class diagram we will have:
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:
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:
- public sealed class SenderBluetoothService : ISenderBluetoothService
- {
- private readonly Guid _serviceClassId;
-
-
-
-
- public SenderBluetoothService()
- {
-
-
- _serviceClassId = new Guid("0e6114d0-8a2e-477a-8502-298d1ff4b4ba");
- }
-
-
-
-
-
- public async Task<IList<Device>> GetDevices()
- {
-
- var task = Task.Run(() =>
- {
- var devices = new List<Device>();
- using (var bluetoothClient = new BluetoothClient())
- {
- var array = bluetoothClient.DiscoverDevices();
- var count = array.Length;
- for (var i = 0; i < count; i++)
- {
- devices.Add(new Device(array[i]));
- }
- }
- return devices;
- });
- return await task;
- }
-
-
-
-
-
-
-
- public async Task<bool> Send(Device device, string content)
- {
- if (device == null)
- {
- throw new ArgumentNullException("device");
- }
-
- if (string.IsNullOrEmpty(content))
- {
- throw new ArgumentNullException("content");
- }
-
-
- var task = Task.Run(() =>
- {
- using (var bluetoothClient = new BluetoothClient())
- {
- try
- {
- var ep = new BluetoothEndPoint(device.DeviceInfo.DeviceAddress, _serviceClassId);
-
-
- bluetoothClient.Connect(ep);
-
-
- var bluetoothStream = bluetoothClient.GetStream();
-
-
- if (bluetoothClient.Connected && bluetoothStream != null)
- {
-
- var buffer = System.Text.Encoding.UTF8.GetBytes(content);
- bluetoothStream.Write(buffer, 0, buffer.Length);
- bluetoothStream.Flush();
- bluetoothStream.Close();
- return true;
- }
- return false;
- }
- catch
- {
-
-
- }
- }
- return false;
- });
- return await task;
- }
- }
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
- public sealed class ReceiverViewModel : ViewModelBase
- {
- private readonly IReceiverBluetoothService _receiverBluetoothService;
- private string _data;
- private bool _isStarEnabled;
- private string _status;
-
-
-
-
-
- public ReceiverViewModel(IReceiverBluetoothService receiverBluetoothService)
- {
- _receiverBluetoothService = receiverBluetoothService;
- _receiverBluetoothService.PropertyChanged += ReceiverBluetoothService_PropertyChanged;
- IsStarEnabled = true;
- Data = "N/D";
- Status = "N/D";
- StartCommand = new RelayCommand(() =>
- {
- _receiverBluetoothService.Start(SetData);
- IsStarEnabled = false;
- Data = "Can receive data.";
- });
-
- StopCommand = new RelayCommand(() =>
- {
- _receiverBluetoothService.Stop();
- IsStarEnabled = true;
- Data = "Cannot receive data.";
- });
-
- Messenger.Default.Register<Message>(this, ResetAll);
- }
-
-
-
-
-
- private void ResetAll(Message message)
- {
- if (!message.IsToShowDevices)
- {
- if (_receiverBluetoothService.WasStarted)
- {
- _receiverBluetoothService.Stop();
- }
- IsStarEnabled = true;
- Data = "N/D";
- Status = "N/D";
- }
- }
-
-
-
-
-
-
-
- public void SetData(string data)
- {
- Data = data;
- }
-
-
-
-
-
-
-
- public string Data
- {
- get { return _data; }
- set { Set(() => Data, ref _data, value); }
- }
-
-
-
-
-
-
-
- public ICommand StartCommand { get; private set; }
-
-
-
-
-
-
-
- public ICommand StopCommand { get; private set; }
-
-
-
-
-
-
-
- public bool IsStarEnabled
- {
- get
- {
- return _isStarEnabled;
- }
- set
- {
- Set(() => IsStarEnabled, ref _isStarEnabled, value);
- RaisePropertyChanged(() => IsStopEnabled);
- }
- }
-
-
-
-
-
-
-
- public bool IsStopEnabled
- {
- get
- {
- return !_isStarEnabled;
- }
- set
- {
- Set(() => IsStopEnabled, ref _isStarEnabled, !value);
- RaisePropertyChanged(() => IsStarEnabled);
- }
- }
-
-
-
-
-
- public string Status
- {
- get { return _status; }
- set { Set(() => Status, ref _status, value); }
- }
-
-
-
-
-
-
- private void ReceiverBluetoothService_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
- {
- if (e.PropertyName == "WasStarted")
- {
- IsStarEnabled = true;
- }
- }
- }
The SenderViewModel
The MainViewModel
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:
- <UserControl x:Class="BluetoothSample.Views.ReceiverView"
- 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"
- DataContext="{Binding ReceiverViewModel, Source={StaticResource Locator}}"
- mc:Ignorable="d"
- d:DesignHeight="300" d:DesignWidth="400">
- <StackPanel Margin="20" Orientation="Vertical">
- <TextBlock>I am the Receiver</TextBlock>
- <StackPanel Orientation="Horizontal">
- <Button Margin="0,10,0,0" Width="80" Command="{Binding StartCommand}" IsEnabled="{Binding IsStarEnabled}" Content="Start"/>
- <Button Margin="20,10,0,0" Width="80" Command="{Binding StopCommand}" IsEnabled="{Binding IsStopEnabled}" Content="Stop"/>
- </StackPanel>
- <TextBlock Margin="00,20,0,0" Text="Data:"/>
- <TextBlock Margin="00,20,0,0" Text="{Binding Data}"/>
- </StackPanel>
- </UserControl>
The SenderView.xamlThe implementation will be something like:
- <UserControl x:Class="BluetoothSample.Views.SenderView"
- 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"
- DataContext="{Binding SenderViewModel,
- Source={StaticResource Locator}}"
- d:DesignHeight="400"
- d:DesignWidth="400"
- mc:Ignorable="d">
- <StackPanel Margin="20" Orientation="Vertical">
- <TextBlock>I am the Sender.</TextBlock>
- <TextBlock Margin="0,20,0,0">Select one device:</TextBlock>
- <ListBox Width="200"
- Height="100"
- MaxWidth="200"
- MaxHeight="100"
- Margin="0,20,0,0"
- HorizontalAlignment="Left"
- ItemsSource="{Binding Devices}"
- SelectedItem="{Binding SelectDevice}" />
- <TextBlock Margin="0,20,0,0" Text="Write the data to send:" />
- <TextBox Margin="0,20,20,0" Text="{Binding Data, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
- <Button Width="80"
- Margin="0,20,20,0"
- HorizontalAlignment="Right"
- Command="{Binding SendCommand}"
- Content="Send" />
- <TextBlock Margin="0,20,0,0" TextWrapping="Wrap">
- Result:<Run Text="{Binding ResultValue}" />
- </TextBlock>
- </StackPanel>
- </UserControl>
The MainWindow.xaml
The MainWindow will show/hide the user controls defined. The implementation is defined by:
- <Window x:Class="BluetoothSample.MainWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:views="clr-namespace:BluetoothSample.Views"
- DataContext="{Binding Main, Source={StaticResource Locator}}"
- Title="Bluetooth Sample"
- MinWidth="600" MinHeight="560"
- MaxWidth="600" MaxHeight="560">
- <StackPanel Orientation="Vertical">
- <GroupBox Margin="10,10,10,0" Header="Choose the type:">
- <StackPanel Orientation="Horizontal">
- <RadioButton Margin="20" IsChecked="{Binding IsReceiver, Mode=TwoWay}">Receiver - will receive data from Sender</RadioButton>
- <RadioButton Margin="20" IsChecked="{Binding IsSender, Mode=TwoWay}">Sender - will send data for the Receiver</RadioButton>
- </StackPanel>
- </GroupBox>
- <GroupBox Margin="10,10,10,0" Header="Dashboard">
- <StackPanel Orientation="Vertical">
- <!-- visibility binding not worked in user control and
- for this reason was added the stackpanel for each usercontrol-->
- <StackPanel Visibility="{Binding ReceiverVisibility}">
- <views:ReceiverView Height="390" x:Name="ReceiverView"/>
- </StackPanel>
- <StackPanel Visibility="{Binding SenderVisibility}">
- <views:SenderView Height="390" x:Name="SenderView" />
- </StackPanel>
- </StackPanel>
- </GroupBox>
- </StackPanel>
- </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;
- <vm:ViewModelLocator xmlns:vm="clr-namespace:BluetoothSample.ViewModel"
- x:Key="Locator"
- d:IsDataSource="True" />
This class is where the setup for the view model and service are made and the implementation is something like:
- public class ViewModelLocator
- {
-
-
-
- public ViewModelLocator()
- {
- ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
-
- SimpleIoc.Default.Register<IReceiverBluetoothService, ReceiverBluetoothService>();
- SimpleIoc.Default.Register<ISenderBluetoothService, SenderBluetoothService>();
- SimpleIoc.Default.Register<MainViewModel>();
- SimpleIoc.Default.Register<ReceiverViewModel>();
- SimpleIoc.Default.Register<SenderViewModel>();
- }
-
-
-
-
-
- public MainViewModel Main
- {
- get
- {
- return ServiceLocator.Current.GetInstance<MainViewModel>();
- }
- }
-
-
-
-
-
- public ReceiverViewModel ReceiverViewModel
- {
- get
- {
- return ServiceLocator.Current.GetInstance<ReceiverViewModel>();
- }
- }
-
-
-
-
-
- public SenderViewModel SenderViewModel
- {
- get
- {
- return ServiceLocator.Current.GetInstance<SenderViewModel>();
- }
- }
-
-
-
-
- public static void Cleanup()
- {
-
- }
- }
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:
The "Sender" is searching for available devices as in the following:
The "Receiver" begins to listen as in the following:
The "Sender" can select a device for sending the message as in the following:
The "Sender" will send a message for the selected device as in the following:
The "Receiver" received the data sent by "Sender" as in the following:
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!