1

У меня есть TreeView, импортированный из WinRT.XamlToolKit, который я пытаюсь использовать в универсальном приложении. Важно отметить, что я понимаю, что есть похожие вопросы, но некоторые из них не работают либо потому, что они предшествуют универсальным приложениям, либо не подходят для использования MVVM. После урока, чтобы понять, как заполнить дерево, я наткнулся на этот рекурсивный метод:Рекурсивно добавлять элементы в TreeView

private ObservableCollection<TreeViewItemModel> BuildTree(int depth, int branches) 
    { 
     var tree = new ObservableCollection<TreeViewItemModel>(); 

     if (depth <= 0) return tree; 
     var depthIndices = Enumerable.Range(0, branches).Shuffle(); 

     for (var i = 0; i < branches; i++) 
     { 
      var d = depthIndices[i] % depth; 
      var b = _rand.Next(branches/2, branches); 
      tree.Add(
       new TreeViewItemModel 
       { 
        Branch = b, 
        Depth = d, 
        Text = "Item " + _itemId++, 
        Children = BuildTree(d, b) 
       }); 
     } 
     return tree; 
    } 

Это называется такой TreeItems = BuildTree(5,5); тогда я связать свою TreeView ItemSource к и прикрепить элементов дерева к текстовому свойству TreeViewItemModel.

Это работает для предопределенного набора, а затем, как видно из метода, использует случайные числа для определения того, как дерево будет отображаться. (В каких глубинах, количестве ветвей и т. Д.)

Что я не уверен в том, как реализовать подобную рекурсивную функцию для заполнения дерева с помощью другого ItemModel с неопределенным количеством детей. В моей настройке у меня есть Пробел, который может иметь дочерние пространства, устройства и/или датчики. В пространстве не было бы ни устройств, ни датчиков, кроме одного или нескольких детских пространств или у него не было бы детских пространств, но у него могли быть устройства и/или датчики. В моем классе Sensor у него есть идентификатор родителя, соответствующий идентификатору пространства, к которому он привязан, и устройству, которому он подчиняется.

Итак, одним словом, мне нужно заполнить мой ObservableCollection<TreeViewSpaceModel> рекурсивно из списка пространств.

TreeViewSpaceModel класс:

public class TreeViewSpaceModel : NotifyObject 
{ 
    private string _name; 
    private Guid _iD; 
    private string _parentName; 
    private ObservableCollection<TreeViewSpaceModel> _children = new ObservableCollection<TreeViewSpaceModel>(); 
    private ObservableCollection<TreeViewDeviceModel> _devices = new ObservableCollection<TreeViewDeviceModel>(); 
    private ObservableCollection<TreeViewSensorModel> _sensors = new ObservableCollection<TreeViewSensorModel>(); 

    public string Name 
    { 
     get { return _name; } 
     set { SetProperty(ref _name, value); } 
    } 

    public string ParentName 
    { 
     get { return _parentName; } 
     set { SetProperty(ref _parentName, value); } 
    } 

    public Guid Id 
    { 
     get { return _iD; } 
     set { SetProperty(ref _iD, value); } 
    } 

    public ObservableCollection<TreeViewSpaceModel> Children 
    { 
     get { return _children; } 
     set { SetProperty(ref _children, value); } 
    } 

    public ObservableCollection<TreeViewDeviceModel> Devices 
    { 
     get { return _devices; } 
     set { SetProperty(ref _devices, value); } 
    } 

    public ObservableCollection<TreeViewSensorModel> Sensors 
    { 
     get { return _sensors; } 
     set { SetProperty(ref _sensors, value); } 
    } 
} 

DeviceModel не имеет детей, но у него есть датчики, и SensorModel не имеет детей или устройств или датчиков, как это конец иерархии. Космический класс:

public class Space 
{ 
    public Guid Id { get; set; } 
    public string Name { get; set; } 
    public int SpaceTypeId { get; set; } 
    public virtual Space Parent { get; set; } 
    public virtual ICollection<Sensor> Sensors { get; set; } 
    public virtual ICollection<Device> Devices { get; set; } 
    public virtual ICollection<Space> Children { get; set; } 
} 

Конечный результат будет идеально выглядеть примерно так:

Space1 
---- ChildSpace1 
--------Device1 
-------------Sensor1 
-------------Sensor2 (Sensors report for Spaces, but are bound to Devices) 
--------SubChildSpace1 
-------------Sensor1 
-------------Sensor2 
--------SubChildSpace2 
-------------Sensor1 
------------Sensor2 
--------Device2 
---- ChildSpace2 
Space2 

Если есть более простой способ сделать это вместо рекурсивного метода, я рад, чтобы развлечь его. Изучая TreeViews для универсальных приложений, я столкнулся с кем-то другим, говорящим, что пользователь должен иметь возможность делать что-то вроде items.Node(0).Add(stuffhere), однако я не мог понять, как этого добиться, поскольку ObservableCollection не предлагает расширение .Node.

ответ

0

Я не совсем уверен, почему я не мог «Ответ на вопрос Филипа Скакуна предоставлен на работу, но он по-прежнему заслуживает некоторого признания, так как его пример помог мне подумать о том, как решить проблему. Я просто использовал одну модель класса -> TreeViewItemModel и сконструировал ее так, чтобы собрать мою Observable Collection.

TreeViewItemModel класс:

public class TreeViewItemModel : NotifyObject 
{ 

    private string _name; 
    private Guid _iD; 
    private string _parentName; 
    private Guid _parentId; 
    private string _parentDeviceName; 
    private ObservableCollection<TreeViewItemModel> _children = new ObservableCollection<TreeViewItemModel>(); 


    public string Name 
    { 
     get { return _name; } 
     set { SetProperty(ref _name, value); } 
    } 

    public Guid ParentId 
    { 
     get { return _parentId; } 
     set { SetProperty(ref _parentId, value); } 
    } 

    public string ParentDeviceName 
    { 
     get { return _parentDeviceName; } 
     set { SetProperty(ref _parentDeviceName, value); } 
    } 
    public string ParentName 
    { 
     get { return _parentName; } 
     set { SetProperty(ref _parentName, value); } 
    } 

    public Guid Id 
    { 
     get { return _iD; } 
     set { SetProperty(ref _iD, value); } 
    } 

    public ObservableCollection<TreeViewItemModel> Children 
    { 
     get { return _children; } 
     set { SetProperty(ref _children, value); } 
    } 


    public TreeViewItemModel(object thing) 
    { 
     //GetDevices(); 
     if (thing.GetType() == typeof (Space)) 
     { 
      var space = (Space)thing; 
      var parentName = string.Empty; 
      if (space.Parent != null) 
      { 
       parentName = space.Parent.Name; 
      } 
      Name = space.Name; 
      ParentName = parentName; 
      Id = space.Id; 
      Children = new ObservableCollection<TreeViewItemModel>(space.Children.Select(s => new TreeViewItemModel(s)).Union(space.Devices.Select(d => new TreeViewItemModel(d)).Union(space.Sensors.Select(sensor => new TreeViewItemModel(sensor))))); 
     } 

     else if (thing.GetType() == typeof (Device)) 
     { 
      var device = (Device) thing; 
      var parentName = device.Space.Name; 
      Name = device.Name; 
      ParentName = parentName; 
      Id = device.Id; 
      Children = new ObservableCollection<TreeViewItemModel>(device.Sensors.Select(s => new TreeViewItemModel(s))); 
     } 

     else if (thing.GetType() == typeof(Sensor)) 
     { 
      var sensor = (Sensor) thing; 
      var space = sensor.Space.Name ?? string.Empty; 
      //var device = Devices.First(d => d.Id == sensor.DeviceId); 
      var device = sensor.Device; 
      ParentName = device == null ? "No Matching Device" : device.Name; 
      Name = sensor.Id.ToString(); 
      Id = sensor.Id; 
      ParentName = space; 

      Children = null; 
     } 

    } 

Я называю это так: TreeSpaces = BuildSpaceTree(AllSpaces); Метод BuildSpaceTree выглядит следующим образом:

private ObservableCollection<TreeViewItemModel> BuildSpaceTree(IEnumerable<Space> spaces) 
    { 
     return new ObservableCollection<TreeViewItemModel>(spaces.Where(space => space.Parent == null).Select(space => new TreeViewItemModel(space))); 
    } 

Самая большая проблема у меня с ответом Filip, то, что он Ждут» Мне нравится, когда я пытаюсь объединить несколько списков разных типов классов.

1

Поскольку элемент управления не поддерживает виртуализацию - он предполагает ограниченное количество элементов (он лучше всего подходит для менее 1000 элементов), поэтому рекурсия должна быть прекрасной. Небольшая проблема с вашим TreeViewSpaceModel заключается в том, что он имеет несколько списков дочерних моделей. Чтобы он работал с TreeView, вам нужен один список детей, но они могут быть разных типов. Если вы хотите отобразить их по-разному - вам нужно будет использовать селекторы шаблонов. Что касается формирования списка, без компиляции проверки это может быть что-то вроде этого:

static ObservableCollection<TreeViewItemModel> GetSpaceTree(
    IEnumerable<Space> spaces) 
{ 
    return new ObservableCollection<TreeViewItemModel>(
     spaces 
      .Where(space => space.Parent == null) 
      .Select(space => new TreeViewSpaceModel(space)) 
    ); 
} 

Тогда в TreeViewSpaceModel вы бы конструктор как этот

TreeViewSpaceModel(Space space) 
{ 
    // TODO: Set name, ID etc. 
    this.Children = new ObservableCollection<TreeViewItemModel>(
     space.Children.Select(childSpace => new TreeViewSpaceModel(childSpace)) 
     .Union(space.Devices.Select(device => new TreeViewDeviceModel(device))) 
     .Union(space.Sensors.Select(sensor => new TreeViewSensorModel(sensor))) 
    ); 
} 
+0

Почему 'space.Parent == null'? – TekGiant

+0

Чтобы получить корни, я думаю? Но это ваш код, поэтому не стесняйтесь его менять. –

+0

О, ладно. Кроме того, как выглядит класс TreeViewItemModel в этом случае. Причина, по которой я спрашиваю об этом, заключается в том, что в моей текущей итерации я не использую класс TreeViewItemModel, который был классом, который использовался в примере рекурсивного метода, который я показал в начале. – TekGiant