Итак, формулировка задачи:
Есть детали (сборки, компоненты), эти детали собираются из других деталей (правда напоминает дерево?), разные детали могут собираются из одних и тех же деталей. Для наглядности, картинка:
Так вот, необходимо разработать элемент управления, для адекватной работы с такой сетью, т.е. добавление, редактирование связей, просмотр из чего состоит данная деталь, в какие сборки она входит.
Порешаем?
Определимся с дизайном. Компонент предполагаю сделать такого вида:
Сразу оговорюсь, при решении этой задачи, я пренебрегу описанием работы с базой данных. В рамках данной задачи, у меня уже есть объектная модель следующего вида:
Два класса вида: "...Collection" - это классы содержащие коллекцию объектов чье имя и будет вместо точек. Детали (Component) связаны друг с другом отношением многие ко многим, вот для описания этих связей и используется mm_ComponentComponent. Имеющий ссылки на соборку в которую входит деталь, и собственно на саму деталь. Как я уже сказал у нас есть две коллекции одна содержит список всех компонентов, вторая список всех связей между компонентами.
Для удобства биндинга, коллекций к пользовательскому интерфейсу я сделю класс "обертку", который будет содержать информацию по компоненту и список ссылок на "обертки" для компонентов в которые входит данный, плюс ссылку на "обертки" компонентов из которых он собирается.
Обертка будет иметь вид:
class ComponentWrapper : DependencyObject { public Component Component { get { return (Component)GetValue(ComponentProperty); } set { SetValue(ComponentProperty, value); } } // Using a DependencyProperty as the backing store for Component. This enables animation, styling, binding, etc... public static readonly DependencyProperty ComponentProperty = DependencyProperty.Register("Component", typeof(Component), typeof(ComponentWrapper), new UIPropertyMetadata(null)); public IEnumerable<ComponentWrapper> Parents { get { return (IEnumerable<ComponentWrapper>)GetValue(ParentsProperty); } set { SetValue(ParentsProperty, value); } } // Using a DependencyProperty as the backing store for Parents. This enables animation, styling, binding, etc... public static readonly DependencyProperty ParentsProperty = DependencyProperty.Register("Parents", typeof(IEnumerable<ComponentWrapper>), typeof(ComponentWrapper), new UIPropertyMetadata(null)); public IEnumerable<ComponentWrapper> Children { get { return (IEnumerable<ComponentWrapper>)GetValue(ChildrenProperty); } set { SetValue(ChildrenProperty, value); } } // Using a DependencyProperty as the backing store for Children. This enables animation, styling, binding, etc... public static readonly DependencyProperty ChildrenProperty = DependencyProperty.Register("Children", typeof(IEnumerable<ComponentWrapper>), typeof(ComponentWrapper), new UIPropertyMetadata(null)); public ComponentWrapper(Component p_component) { Component = p_component; Parents = null; Children = null; } }
Итак, теперь ставится задача отразить коллекции ComponentCollection и mm_ComponentComponentCollection в коллекцию ComponentWrapper-ов.
Это можно сделать следующим образом:
public List<ComponentWrapper> ComponentWrappers { get { return (List<ComponentWrapper>)GetValue(ComponentWrappersProperty); } set { SetValue(ComponentWrappersProperty, value); } } // Using a DependencyProperty as the backing store for ComponentWrappers. This enables animation, styling, binding, etc... public static readonly DependencyProperty ComponentWrappersProperty = DependencyProperty.Register("ComponentWrappers", typeof(List<ComponentWrapper>), typeof(ComponentsViewModel), new UIPropertyMetadata(null)); private void RefreshData() { // Заполнение данными объектной модели ComponentCollection components = null; ORMService.ClientProxy.u_obj_Component_Sel_All(out components); mm_ComponentComponentCollection componentComponents = null; ORMService.ClientProxy.u_man_mm_ComponentComponent_Sel_All(out componentComponents); // Заполняем коллекцию оберткок объектами ComponentWrappers = (from Component comp in components select new ComponentWrapper(comp) ).ToList(); // Заполняем в каждой обертке связи к "родителям" и "детям" foreach (ComponentWrapper currentCW in ComponentWrappers) { List<Guid> parentIDs = (from mm_ComponentComponent cc in componentComponents where cc.ChildComponent.ID == currentCW.Component.ID select cc.ParentComponent.ID ).ToList(); // ТоList для того чтобы в следующем запросе // не приходилось каждый раз заново формировать // коллекцию currentCW.Parents = from ComponentWrapper cw in ComponentWrappers where parentIDs.Contains(cw.Component.ID) // Вот здесь select cw; List<Guid> childrenIDs = (from mm_ComponentComponent cc in componentComponents where cc.ParentComponent.ID == currentCW.Component.ID select cc.ChildComponent.ID ).ToList(); // Вы не поверите, но эта конструкция не Ctrl C, V currentCW.Children = from ComponentWrapper cw in ComponentWrappers where childrenIDs.Contains(cw.Component.ID) select cw; // И это я тоже набирал руками ;) } }
Для показа основного списка компонентов воспользуемся DataGrid-ом. Для этого его привяжем к полю ComponentWrappers. Обратите внимание, что оно же является источником элементов для показа.
<DataGrid AutoGenerateColumns="False" x:Name="dgMain" Grid.Row="1" Grid.RowSpan="2" ItemsSource="{Binding}" DataContext="{Binding ComponentWrappers}"> <DataGrid.Columns> <DataGridTextColumn Header="Компонент" Binding="{Binding Component.Name}"/> DataGrid.Columns> DataGrid>Для показа списка компонентов в которые входит выбранный, также будем использовать
DataGrid, для которого источником данных бедет являться выбранный элемент из первого грида.
<DataGrid Grid.Column="1" Grid.Row="1" ItemsSource="{Binding ElementName=dgMain,Path=SelectedItem.Parents}"> <DataGrid.Columns> <DataGridTextColumn Header="Компонент" Binding="{Binding Component.Name}"/> DataGrid.Columns> DataGrid>Аналогично для показа из чего состоит текущий компонент. Принципиальное отличие заключается только в том, что нам придется применить TreeView и написать для него иерархический шаблон данных. В ресурсках окна/грида компоновки можем задать шаблон так:<HierarchicalDataTemplate x:Key="hdtChildren" ItemsSource="{Binding Children}"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Component.Name}" /> StackPanel> HierarchicalDataTemplate>Ну и соответственно дерево также привязываем к выбранному элементу в первом списке.<TreeView Grid.Column="1" Grid.Row="2" ItemsSource="{Binding ElementName=dgMain,Path=SelectedItem.Children}" ItemTemplate="{StaticResource hdtChildren}"> TreeView> Выглядет это все вот так:Все для этой статьи хватит. В следующей раскажу как реализовал добавление и редактирование данных. P.s. Тестовые данные для примера были сгенерированны ручками. Рекомендую в качестве домашнего задания, попробовать реализовать это все самостоятельно. Будут вопросы, пишите, чем смогу, помогу.
Комментариев нет:
Отправить комментарий