[英]Recursively Add Items to a TreeView
我有一個從 WinRT.XamlToolKit 導入的 TreeView,我正嘗試在通用應用程序中使用它。 重要的是要注意,我知道有類似的問題,但有些問題不起作用,因為它們早於通用應用程序,或者它們不適應 MVVM 的使用。 按照教程了解如何填充樹,我遇到了這種遞歸方法:
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 綁定到 TreeItems 並附加到 TreeViewItemModel 的 Text 屬性。
這適用於預先確定的集合,然后如方法中所見,使用隨機數來計算樹將如何顯示。 (在什么深度,有多少分支等)
我不確定的是如何實現一個類似的遞歸函數來用不同的 ItemModel 填充樹,並具有不確定數量的子項。 在我的設置中,我有一個空間,可以有子空間、設備和/或傳感器。 一個空間可以沒有設備也沒有傳感器,只有一個或多個子空間,或者它可以沒有子空間,但可以有設備和/或傳感器。 在我的 Sensor 類中,它有一個與它綁定到的空間的 ID 和它報告給的設備相關的父 ID。
所以,簡而言之,我需要從空間列表中遞歸地填充我的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 擴展名。
由於控件不支持虛擬化 - 它假設項目數量有限(它最適合遠低於 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)))
);
}
我不太確定為什么我無法得到 Filip Skakun 提供的答案,但他仍然值得稱贊,因為他的例子確實幫助我思考了如何解決問題。 我只使用了一個類模型 -> TreeViewItemModel 並像這樣構建它來構建我的 Observable 集合。
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 的回答遇到的最大問題是,它不喜歡我嘗試聯合多個不同類類型的列表。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.