WPF привязка двухуровневых структурированных данных без прямых ссылок

Мне нужно представить двухуровневые структурированные данные MVVM. Если бы данные были:

public class ParentEntity
{
    public ObservableCollection<ChildEntity> ChildsEntities { get; set; } = new ObservableCollection<ChildEntity>();
    public string Name { get; set; }
}

public class ChildEntity
{
    public string Name { get; set; }
    public int Value { get; set; }
}

тогда я бы создал привязку с TreeView:

<TreeView x:Name="treeView">
    <TreeView.Resources>

        <!--Template for ParentEntity-->
        <HierarchicalDataTemplate DataType="{x:Type ParentEntity}" ItemsSource="{Binding ChildsEntities}">
            <TextBox Text="{Binding Name}"/>
        </HierarchicalDataTemplate>

        <!--Template for ChildEntity-->
        <DataTemplate DataType="{x:Type ChildEntity}">
            <StackPanel Orientation="Horizontal">
                <TextBox Text="{Binding Name}"/>
                <TextBox Text="{Binding Value}"/>
            </StackPanel>
        </DataTemplate>

    </TreeView.Resources>
</TreeView>

ObservableCollection<ParentEntity> data = new ObservableCollection<ParentEntity>();
treeView.ItemsSource = data ;

Но у меня есть такая структура (из библиотеки):

public class ParentEntity
{
    public int UID { get; set; }
    public string Name { get; set; }
}

public class ChildEntity
{
    public int ParentUID { get; set; }
    public string Name { get; set; }
    public int Value { get; set; }
}

Как это должно быть обработано? Благодарю.

1 ответ

Я не очень рекомендую это, но я решу проблему, у вас есть XAML

 <StackPanel x:Name="root">
        <StackPanel.DataContext>
            <vm:MainWindowViewModel></vm:MainWindowViewModel>
        </StackPanel.DataContext>
        <Button Command="{Binding AddRoot}">Add</Button>
        <TreeView Height="400">
            <TreeView.Resources >
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="IsExpanded" Value="True">
                    </Setter>
                </Style>
                <vm:HierarchicalDataConverter x:Key="HierarchicalDataConverter"/>
                <HierarchicalDataTemplate x:Key="ParentEntityTemplate" DataType="{x:Type vm:ParentEntity}" >
                    <HierarchicalDataTemplate.ItemsSource>
                        <MultiBinding Converter="{StaticResource HierarchicalDataConverter}" Mode="TwoWay">
                            <Binding Mode="OneTime"></Binding>
                            <Binding Path="DataContext.Items" ElementName="root" ></Binding>
                            <Binding Path="DataContext.Items.Count" ElementName="root" ></Binding>
                        </MultiBinding>
                    </HierarchicalDataTemplate.ItemsSource>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{ Binding Name }"></TextBlock>
                        <Button Command="{Binding DataContext.AddLeafe, ElementName=root}" CommandParameter="{Binding}">Add item</Button>
                    </StackPanel>
                </HierarchicalDataTemplate>
                <DataTemplate  x:Key="ChildEntityTemplate" DataType="{x:Type vm:ChildEntity}">
                    <TextBlock Text="{Binding Name}"></TextBlock>
                </DataTemplate>
            </TreeView.Resources>
            <TreeView.ItemsSource>
                <Binding Path="RootItems"/>
            </TreeView.ItemsSource>
            <TreeView.ItemTemplateSelector>
                <vm:ItemTemplateSelector>
                </vm:ItemTemplateSelector>
            </TreeView.ItemTemplateSelector>
        </TreeView>
    </StackPanel> 

Пространство имен VM

public class MainWindowViewModel : BindableBase
    {
        public ObservableCollection<object> Items { get; } =
            new ObservableCollection<object>(new List<object>(new List<object>
        {
            new ParentEntity { UID = 1, Name = "Groop1" },
            new ChildEntity { ParentUID = 1, Name = "Item1" },
            new ParentEntity { UID = 2, Name = "Groop2" },
            new ChildEntity { ParentUID = 2, Name = "Item1" },
            new ChildEntity { ParentUID = 2, Name = "Item2" },
            new ChildEntity { ParentUID = 2, Name = "Item3" }
        }));

        public CollectionView RootItems { get; }


        int count = 2;
        public DelegateCommand AddRoot { get; }

        public DelegateCommand<ParentEntity> AddLeafe { get; }
        public MainWindowViewModel()
        {
            RootItems = new ListCollectionView(Items) { Filter = (item) => item is ParentEntity }; 

            AddRoot = new DelegateCommand(() =>
            {
                Items.Add(new ParentEntity { UID = ++count ,Name= "Group"+count});
            });

            AddLeafe = new DelegateCommand<ParentEntity>((pe) =>
            {
                Items.Add(new ChildEntity { ParentUID = pe.UID, Name = "Item" + count });
            });

        }

    }

    public class ParentEntity
    {
        public int UID { get; set; }
        public string Name { get; set; }
    }

    public class ChildEntity
    {
        public int ParentUID { get; set; }
        public string Name { get; set; }
        public int Value { get; set; }
    }

    public class ItemTemplateSelector : DataTemplateSelector
    {
        public override DataTemplate
            SelectTemplate(object item, DependencyObject container)
        {
            FrameworkElement element = container as FrameworkElement;

            if (item is ParentEntity)
            {
                return element.FindResource("ParentEntityTemplate") as DataTemplate;
            }
            if (item is ChildEntity)
            {
                return element.FindResource("ChildEntityTemplate") as DataTemplate;
            }
            return null;
        }
    }

    public class HierarchicalDataConverter : IMultiValueConverter
    {
        public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value[0] != null)
            {
                ParentEntity pe = value[0] as ParentEntity;
                var items = value[1] as IEnumerable<object>;

                return items.Where((item) => item is ChildEntity && (item as ChildEntity).ParentUID == pe.UID);
            }
            return null;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

Проблема в том, что он воссоздает все дочерние узлы.

Другие вопросы по тегам