我有一个应用程序,可以使用反斜杠(\)字符在层次结构中构建标记。
例如;
Country\Canada\Alberta
Country\Canada\British Columbia
Country\USA\California
Country\USA\Texas
将成为用户界面;
Country
Canada
Alberta
British Columbia
USA
California
Texas
在数据库中,它以字符串形式存储,并以TagDto
的形式返回给客户端。我已经尝试了以下方法来实现这一目标;
public class TagDto
{
public int Id { get; set; }
public string Name { get; set; }
}
public class TagLeaf
{
public string Id { get; }
public string ParentId { get; }
public int TagId { get; }
public string Name { get; }
public TagLeaf(string id, string parentId, int tagId, string name)
{
Id = id;
ParentId = parentId;
TagId = tagId;
Name = name;
}
// IEquatable implemented on Id property.
}
public class TagsViewModel : ReactiveObject
{
private IDisposable TagsSubscription { get; }
public SourceCache<TagDto, string> Tags { get } = new SourceCache<TagDto, string>(t => t.Id);
private readonly ReadOnlyObservableCollection<TagLeafViewModel> _tagTree;
public ReadOnlyObservableCollection<TagLeafViewModel> TagTree => _tagTree;
public ReactiveCommand AddBelgium { get; }
public TagsViewModel()
{
AddBelgium = ReactiveCommand.Create(() =>
Tags.AddOrUpdate(new TagDto {Id = 5, Name = @"Country\Belgium"});
// this comes from an web service normally.
Tags.AddOrUpdate(new[] {
new TagDto {Id = 1, Name = @"Country\Canada\Alberta"},
new TagDto {Id = 2, Name = @"Country\Canada\British Columbia"},
new TagDto {Id = 3, Name = @"Country\USA\California"},
new TagDto {Id = 4, Name = @"Country\USA\Texas"}
});
TagsSubscription = Tags
.Connect()
.TransformMany(dto =>
{
var names = dto.Name.Split(new[] {'\\'}, StringSplitOptions.RemoveEmptyEntries);
var results = new TagLeaf[names.Length];
var parentId = "";
for (var i = 0; i < names.Length; i++)
{
var name = names[i];
var id = $"{parentId}{name}\\";
results[i] = new TagLeaf(id, parentId, dto.Id, name);
parentId = id;
}
return results;
}, leaf => leaf.Id)
.TransformToTree(leaf => leaf.ParentId)
.Transform(leaf => new TagLeafViewModel(leaf))
.Sort(SortExpressionComparer<TagLeafViewModel>.Ascending(vm => vm.Name))
.Bind(out _tagTree)
.Subscribe();
}
}
public class TagLeafViewModel : ReactiveObject
{
private readonly ReadOnlyObservableCollection<TagLeafViewModel> _children;
public ReadOnlyObservableCollection<TagLeafViewModel> Children => _children;
private string _name;
public string Name
{
get => _name;
set => this.RaiseAndSetIfChanged(ref _name, value);
}
public TagLeafViewModel(Node<TagLeaf, string> node)
{
Name = node.Item.Name;
ChildrenSubscription = node.Children
.Connect()
.Transform(n => new TagLeafViewModel(n))
.Sort(SortExpressionComparer<TagLeafViewModel>.Ascending(vm => vm.Name))
.Bind(out _children)
.Subscribe();
}
}
// TagsView.xaml
<StackPanel>
<Button x:Name="AddBelgiumButton" Content="Add Belgium"/>
<telerik:RadTreeView x:Name="TagTreeView">
<telerik:RadTreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</telerik:RadTreeView.ItemTemplate>
</telerik:RadtreeView>
</StackPanel>
// TagsView.xaml.cs constructor
public TagsView()
{
...
this.WhenActivated(d =>
{
d(this.AddBelgiumButton.Events().Click.Select(x => Unit.Default).InvokeCommand(ViewModel, vm => vm.AddBelgium));
d(this.OneWayBind(ViewModel, vm => vm.TagTree, v => v.TagTreeView.ItemsSource));
});
}
这会产生一棵树,正如我所期望的那样,但如果我展开Country
并点击添加比利时,而不是将此插入作为国家/地区下方的新节点插入树中 - 它会崩溃整个国家的节点。
添加新标记会导致2个新的TagLeaf
流式传输到TramsformToTree
。一个用于国家,一个用于比利时,所以我理解为什么它更新了国家节点但是,我不确定如何克服这个问题 - 任何建议都会非常感激。
答案 0 :(得分:0)
我相信我已经取得了突破,但建议仍然受到欢迎。
在我之前的尝试中意识到TransformMany
是问题所在,我认为有必要维护2个单独的缓存来实现我的目标。
我现在有一个TagService,它暴露了两个缓存。每当在基础TagDto
缓存中更改项目时,我都会使用更改手动更新TagLeaf
缓存。在我的示例应用程序中,现在插入新节点而不折叠根节点。
这是不完整的,我仍然需要处理在TagLeaf
缓存中没有子项时删除父TagLeaf
,但我相信我可以做到这一点,所以我认为问题已经解决了。
public class TagService : ITagService
{
private readonly SourceCache<TagDto, int> _tagDtos = new SourceCache<TagDto, int>(t => t.Id);
public IObservableCache<TagDto, int> TagDtos => _tagDtos;
private readonly SourceCache<TagLeaf, string> _tagLeafs = new SourceCache<TagLeaf, string>(t => t.Id);
public IObservableCache<TagLeaf, string> TagLeafs => _tagLeafs;
public TagService()
{
_tagDtos.AddOrUpdate(new[]
{
new TagDto {Id = 1, Name = @"Country\Canada\Alberta"},
new TagDto {Id = 2, Name = @"Country\Canada\British Columbia"},
new TagDto {Id = 3, Name = @"Country\USA\California"},
new TagDto {Id = 4, Name = @"Country\USA\Texas"}
});
_tagDtos
.Connect()
.Transform(dto =>
{
var names = dto.Name.Split(new[] {'\\'}, StringSplitOptions.RemoveEmptyEntries);
var results = new TagLeaf[names.Length];
var parentId = "";
for (var i = 0; i < names.Length; i++)
{
var name = names[i];
var id = $"{parentId}{name}\\";
results[i] = new TagLeaf(id, parentId, dto.Id, name);
parentId = id;
}
return new TagBranch(dto.Id, results);
})
.ForEachChange(change =>
{
var branch = change.Current;
switch (change.Reason)
{
case ChangeReason.Remove:
var lastLeaf = branch.Leaves.Last();
_tagLeafs.RemoveKey(lastLeaf.Id);
break;
case ChangeReason.Add:
foreach (var leaf in branch.Leaves)
{
if (_tagLeafs.Keys.Contains(leaf.Id))
continue;
_tagLeafs.AddOrUpdate(leaf);
}
break;
}
})
.Subscribe();
}
public void AddOrUpdate(TagDto dto)
{
_tagDtos.AddOrUpdate(dto);
}
}
TagsViewModel中的构造函数现在看起来像这样;
public TagsViewModel(ITagService tagService)
{
AddBelgium = ReactiveCommand.Create(() =>
tagService.AddOrUpdate(new TagDto {Id = 5, Name = @"Country\Belgium"}));
TagsSubscription = tagService.TagLeafs
.Connect()
.TransformToTree(leaf => leaf.ParentId)
.Transform(node => new TagLeafViewModel(node))
.Sort(SortExpressionComparer<TagLeafViewModel>.Ascending(vm => vm.Name))
.Bind(out _tagTree)
.Subscribe();
}