Насколько я понял, у вас есть 3 пользовательских элемента управления, имена которых имеют что-то вроде NodePicture, GreenNodePicture и BlueNodePicture.
Прежде всего, если 3 элемента управления отличаются в очень незначительной степени, было бы лучше иметь только один элемент управления, который переключает цвет с использованием некоторого значения свойства.
Предположим, что ваши элементы управления отличаются цветом фона прямоугольника на холсте. Поэтому я бы изменил ваш контроль так:
<Grid x:Name="LayoutRootNodePicture" Height="100" Width="100"
HorizontalAlignment="Center">
<Canvas x:Name="ParentCanvas" Background="{Binding NodeColor}" Width="100" Height="100" >
</Canvas>
<Image HorizontalAlignment="Center"
Source="add.png"
Stretch="Fill"
Width="16"
VerticalAlignment="Top"
Margin="0,0,2,2"
Height="16" MouseLeftButtonDown="Image_MouseLeftButtonDown">
</Image>
</Grid>
Я удалил раздел Resources
, потому что представление не должно создавать новые объекты модели представления, оно должно использовать существующий DataContext. Вы можете видеть, что цвет фона прямоугольника основан на свойстве NodeColor
модели представления.
Давайте добавим это свойство в модель представления:
public class NodeViewModel : INotifyPropertyChanged
{
private SolidColorBrush _nodeColor;
public SolidColorBrush NodeColor
{
get { return _nodeColor; }
set
{
_nodeColor = value;
NotifyChange("NodeColor");
}
}
//...
И теперь, если вы хотите отобразить 3 элемента управления с разным цветом, вы должны создать 3 модели представления с различными свойствами. Вот пример красной, синей и зеленой моделей:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
var redBrush = new SolidColorBrush(Color.FromArgb(255, 255, 0, 0));
var greenBrush = new SolidColorBrush(Color.FromArgb(255, 0, 255, 0));
var blueBrush = new SolidColorBrush(Color.FromArgb(255, 0, 0, 255));
this.DataContext = new MainViewModel
{
Nodes = new ObservableCollection<NodeViewModel>{
new NodeViewModel
{
NodeColor = redBrush,
Children = new ObservableCollection<NodeViewModel>{
new NodeViewModel { NodeColor = greenBrush, LeftOffset = 65, TopOffset = 10},
new NodeViewModel { NodeColor = greenBrush, LeftOffset = 55, TopOffset = 60}
}
}, //red
new NodeViewModel { NodeColor = greenBrush}, //green
new NodeViewModel { NodeColor = blueBrush} //blue
}
};
}
}
public class MainViewModel
{
public ObservableCollection<NodeViewModel> Nodes { get; set; }
}
Модели представлений переводятся в представления с использованием шаблонов данных:
<ListBox ItemsSource="{Binding Nodes}">
<ListBox.ItemTemplate>
<DataTemplate>
<local:NodePicture DataContext="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Я не использовал свойство Children, потому что не понял, где его использовать. Возможно, дочерние узлы отображаются на холсте. В любом случае, если это важно - вы можете предоставить дополнительную информацию, и я помогу с этим.
Обновление:
Самый простой способ рисовать дочерние элементы на холсте - добавить свойство зависимостей, которое обновляет холст при обновлении коллекции:
public partial class NodePicture : UserControl
{
public NodePicture()
{
InitializeComponent();
}
public IEnumerable<NodeViewModel> ChildViewModels
{
get { return (IEnumerable<NodeViewModel>)GetValue(ChildViewModelsProperty); }
set { SetValue(ChildViewModelsProperty, value); }
}
public static readonly DependencyProperty ChildViewModelsProperty =
DependencyProperty.Register("ChildViewModels", typeof(IEnumerable<NodeViewModel>), typeof(NodePicture),
new PropertyMetadata(null, (s, e) => ((NodePicture)s).UpdateCanvas()));
private void UpdateCanvas()
{
this.ParentCanvas.Children.Clear();
var items = this.ChildViewModels;
if(items == null)
return;
var controls = items.Select(item=>
{
var e = new Ellipse{Width = 20, Height = 20};
e.Fill = item.NodeColor;
//Or using the data binding
//BindingOperations.SetBinding(e, Ellipse.FillProperty, new Binding("NodeColor") { Source = item });
Canvas.SetLeft(e, item.LeftOffset);
Canvas.SetTop(e, item.TopOffset);
return e;
});
foreach(var c in controls)
this.ParentCanvas.Children.Add(c);
}
Где TopOffset и LeftOffset - это свойства класса NodeViewModel.
После этого вы должны установить это свойство в коде xaml:
<DataTemplate>
<local:NodePicture DataContext="{Binding}" ChildViewModels="{Binding Children}" />
</DataTemplate>
Он не будет работать с классом ObservableColelction
, потому что я не обработал событие CollectionChanged
.
Другой подход - использовать элемент управления ListBox
с пользовательскими ItemsPanelTemplate
и ListBoxItem ControlTemplate
. Но это гораздо более сложное решение.