Si j'ai bien compris, vous avez 3 contrôles utilisateur qui ont des noms comme NodePicture, GreenNodePicture et BlueNodePicture. Tout d'abord, si les trois contrôles diffèrent très peu, il serait préférable de n'avoir qu'un seul contrôle qui modifie la couleur en fonction de la valeur d'une propriété.
Supposons que vos contrôles se différencient par la couleur de fond du rectangle sur le canevas. Je modifierais donc votre contrôle de la manière suivante
<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>
J'ai supprimé le Resources
car la vue ne doit pas créer de nouveaux objets de modèle de vue, elle doit utiliser un DataContext existant. Vous pouvez voir que la couleur d'arrière-plan du rectangle est basée sur la propriété NodeColor
du modèle de vue. Ajoutons cette propriété au modèle de vue :
public class NodeViewModel : INotifyPropertyChanged
{
private SolidColorBrush _nodeColor;
public SolidColorBrush NodeColor
{
get { return _nodeColor; }
set
{
_nodeColor = value;
NotifyChange("NodeColor");
}
}
//...
Et maintenant, si vous voulez afficher 3 contrôles avec des couleurs différentes, vous devez créer 3 modèles de vue avec des propriétés différentes. Voici l'exemple des modèles de vue rouge, bleu et vert :
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; }
}
Les modèles de vues sont traduits en vues à l'aide de modèles de données :
<ListBox ItemsSource="{Binding Nodes}">
<ListBox.ItemTemplate>
<DataTemplate>
<local:NodePicture DataContext="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Je n'ai pas utilisé la propriété Children car je n'ai pas compris où l'utiliser. Peut-être que les nœuds enfants sont affichés sur le canevas. Quoi qu'il en soit, si c'est important, vous pouvez fournir des informations supplémentaires et je vous aiderai.
Mise à jour :
La manière la plus simple de dessiner des éléments enfants sur le canevas est d'ajouter la propriété de dépendance qui met à jour le canevas lorsque la collection est mise à jour :
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);
}
Les propriétés TopOffset et LeftOffset sont celles de la classe NodeViewModel. Ensuite, vous devez définir cette propriété dans le code xaml :
<DataTemplate>
<local:NodePicture DataContext="{Binding}" ChildViewModels="{Binding Children}" />
</DataTemplate>
Il ne fonctionnera pas avec le ObservableColelction
parce que je n'ai pas géré le CollectionChanged
événement. Une autre approche consiste à utiliser la fonction ListBox
avec le contrôle personnalisé ItemsPanelTemplate
y ListBoxItem ControlTemplate
. Mais c'est la solution la plus complexe.