2017-05-28 6 views
0

私は、バックスラッシュ(\)文字を使用してタグを階層構造にすることができるアプリケーションがあります。DynamicData:TransformToTree

例えば、

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を拡張し、ではなく、国の下に新しいノードとしてツリーにこの挿入を見ての、ベルギー [追加]をクリックした場合、私が期待するようツリーを生成します - それは、全国のノードを縮小します。

新しいタグを追加すると、新しいTagLeafが2つ追加され、TramsformToTreeにストリーミングされます。 1つは国、もう1つはベルギーのため、なぜ国ノードを更新しているのか理解していますが、どのように克服できるかはわかりません。

+0

[mcve]を提供する必要があります。 – Enigmativity

+0

あなたは正しいですが、少し曖昧でした。私はこれがより良いと思う。 – Dan

答えて

0

私は突破口を作ったと信じていますが、提案はまだ歓迎されています。

これまでの試みではTransformManyが問題であったことを認識して、後で何を達成するために2つの別々のキャッシュを維持する必要があると判断しました。

私は今、両方のキャッシュを公開するTagServiceを持っています。基本的なTagDtoキャッシュでアイテムが変更されると、変更されたキャッシュが手動で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); 
    } 
} 

タグビューモデルのコンストラクタは、次のようになりました。

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