动态数据:转换到树



>我有一个应用程序,其中可以使用反斜杠 (\( 字符在层次结构中构建标签。

例如;

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缓存。在我的示例应用程序中,现在插入新节点而不折叠根节点。

这是不完整的,当父TagLeafTagLeaf缓存中没有子级时,我仍然需要处理删除父,但我相信我可以做到这一点,所以我认为问题解决了。

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

相关内容

  • 没有找到相关文章

最新更新