>我有一个应用程序,其中可以使用反斜杠 (\( 字符在层次结构中构建标签。
例如;
CountryCanadaAlberta
CountryCanadaBritish Columbia
CountryUSACalifornia
CountryUSATexas
将出现在用户界面中;
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 = @"CountryBelgium"});
// this comes from an web service normally.
Tags.AddOrUpdate(new[] {
new TagDto {Id = 1, Name = @"CountryCanadaAlberta"},
new TagDto {Id = 2, Name = @"CountryCanadaBritish Columbia"},
new TagDto {Id = 3, Name = @"CountryUSACalifornia"},
new TagDto {Id = 4, Name = @"CountryUSATexas"}
});
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
中。一个用于国家,一个用于比利时,所以我理解它为什么要更新国家/地区节点,但是,我不确定如何克服这一点 - 任何建议将不胜感激。
我已经取得了突破,但是,仍然欢迎提出建议。
意识到TransformMany
是我早期尝试中的问题,我决定有必要维护 2 个单独的缓存来实现我所追求的目标。
我现在有一个公开两个缓存的标签服务。每当在基础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 = @"CountryCanadaAlberta"},
new TagDto {Id = 2, Name = @"CountryCanadaBritish Columbia"},
new TagDto {Id = 3, Name = @"CountryUSACalifornia"},
new TagDto {Id = 4, Name = @"CountryUSATexas"}
});
_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 = @"CountryBelgium"}));
TagsSubscription = tagService.TagLeafs
.Connect()
.TransformToTree(leaf => leaf.ParentId)
.Transform(node => new TagLeafViewModel(node))
.Sort(SortExpressionComparer<TagLeafViewModel>.Ascending(vm => vm.Name))
.Bind(out _tagTree)
.Subscribe();
}