来自另一个线程的UWP XAML Combobox ItemsSource管理

时间:2016-06-21 05:50:47

标签: c# xaml uwp-xaml

上下文

在网络上是定期用UDP通告其名称的服务器。

数据报在端口1967上进入并包含如下字符串:

UiProxy SomeServerMachineName

添加了新条目,更新了现有条目,并且陈旧条目在作为XAML组合框的ItemsSource的可观察集合中老化。

这是组合框

<ComboBox x:Name="comboBox" ItemsSource="{Binding Directory}" />

这是支持代码。异常处理程序包装所有危险的东西,但为简洁起见,此处省略。

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);
    }
  });
}

该集合开始为空。

从SelectedHost的setter调用的UpdateWrite方法会破坏(如果需要)并在DatagramSocket周围创建一个DataWriter(如果可能),目标是SelectedHost值所描述的地址。

目标

自动选择何时添加值并且列表不再为空。

该列表也可以变为为空。发生这种情况时,选择必须返回null,并且选择的索引为-1。

根据情况,列表是可管理的,并且可以从列表中以交互方式选择服务器。

目前我正在设置SelectedHost,但我相信它可以通过绑定来完成。

private void comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
  SelectedHost = comboBox.SelectedItem as HostEntry;
}

SelectedHost的setter方法调用CreateWriter,它管理在别处使用的对象以将数据发送到所选主机。我已经从setter中调用了它,因为它必须始终在值更改后立即发生,并且在任何时候都不会发生。它在一个方法中,因此它可以是异步的。

我可以将它移动到SelectionChanged处理程序,但如果我这样做,那么我如何保证执行顺序?

问题

当我尝试以编程方式设置组合框的选择时,我收到错误。我正在调整UI线程,但仍然不是很好。这样做的正确方法是什么?我尝试过设置SelectedIndex和SelectedValue。

1 个答案:

答案 0 :(得分:0)

  

当我尝试以编程方式设置组合框的选择时,我收到错误。

你好吗?在代码隐藏中,只要您绑定的集合在该索引处有一个项目,它就应该起作用:

myComboBox.SelectedIndex = 4;
  

但我相信可以通过绑定来完成

是的,可以,看起来你忘了实施INotifyPropertyChanged。此外,由于您使用的是UWP,因此有一种新的改进的绑定语法Bind而非Binding在此处了解更多信息:https://msdn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth

<ComboBox x:Name="comboBox" ItemsSource="{Binding Directory}" 
     SelectedItem="{Binding SelectedHost}" />

public event PropertyChangedEventHandler PropertyChanged;

HostEntry _selectedHost;
public HostEntry SelectedHost
{
  get { return _selectedHost; }
  set
  {
    _selectedHost = value;
    RaiseNotifyPropertyChanged();
    // What is this? propertys should not do things like this CreateWriter();
  }
}

// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}