Context
On the network are servers that advertise their names with UDP at regular intervals.
The datagrams come in on port 1967 and contain a string like this:
UiProxy SomeServerMachineName
New entries are added, existing entries are updated and stale entries age out of an observable collection that serves as the ItemsSource of a XAML combo box.
This is the combo box
<ComboBox x:Name="comboBox" ItemsSource="{Binding Directory}" />
and this is the supporting code. Exception handlers wrap everything dangerous but are here omitted for brevity.
public class HostEntry
{
public string DisplayName { get; set;}
public HostName HostName { get; set; }
public DateTime LastUpdate { get; set; }
public HostEntry(string displayname, HostName hostname)
{
DisplayName = displayname;
HostName = hostname;
LastUpdate = DateTime.Now;
}
public override string ToString()
{
return DisplayName;
}
}
HostEntry _selectedHost;
public HostEntry SelectedHost
{
get { return _selectedHost; }
set
{
_selectedHost = value;
UpdateWriter();
}
}
async UpdateWriter() {
if (_w != null)
{
_w.Dispose();
_w = null;
Debug.WriteLine("Disposed of old writer");
}
if (SelectedHost != null)
{
_w = new DataWriter(await _ds.GetOutputStreamAsync(SelectedHost.HostName, "1967"));
Debug.WriteLine(string.Format("Created new writer for {0}", SelectedHost));
}
}
ObservableCollection<HostEntry> _directory = new ObservableCollection<HostEntry>();
public ObservableCollection<HostEntry> Directory
{
get { return _directory; }
}
private async void _ds_MessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
{
if (_dispatcher == null) return;
await _dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
var dr = args.GetDataReader();
var raw = dr.ReadString(dr.UnconsumedBufferLength);
var s = raw.Split();
if (s[0] == "UiProxy")
{
if (_directory.Any(x => x.ToString() == s[1]))
{ //update
_directory.Single(x => x.ToString() == s[1]).LastUpdate = DateTime.Now;
}
else
{ //insert
_directory.Add(new HostEntry(s[1], args.RemoteAddress));
}
var cutoff = DateTime.Now.AddSeconds(-10);
var stale = _directory.Where(x => x.LastUpdate < cutoff);
foreach (var item in stale) //delete
_directory.Remove(item);
}
});
}
The collection starts empty.
The UpdateWrite method called from the setter of SelectedHost destroys (if necessary) and creates (if possible) a DataWriter around a DatagramSocket aimed at the address described by the value of SelectedHost.
Goals
Automatically select when a value is added and the list ceases to be empty.
The list can also become empty. When this happens the selection must return to null with a selected index of -1.
As things stand, the list is managed and it is possible to interactively pick a server from the list.
At the moment I am setting SelectedHost like this but I am sure it could be done with binding.
private void comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SelectedHost = comboBox.SelectedItem as HostEntry;
}
The setter method for SelectedHost calls CreateWriter which manages the object used elsewhere to send data to the selected host. I've called this from the setter because it must always happen right after the value changes, and at no other time. It's in a method so it can be async.
I could move it to the SelectionChanged handler but if I do that then how can I guarantee order of execution?
Problem
I get errors when I try to programmatically set the selection of the combo box. I am marshalling onto the UI thread but still things aren't good. What is the right way to do this? I've tried setting SelectedIndex and SelectedValue.