2 votes

Comment combiner plusieurs UserControls dans SilverLight ?

Il s'agit peut-être d'une question simple, mais je n'arrive pas à trouver la réponse. J'ai trois contrôles utilisateur qui ne diffèrent que par la couleur. Il y a un code pour l'un d'entre eux :

<UserControl x:Class="SilverlightApplication14.NodePicture"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SilverlightApplication14">

    <UserControl.Resources>
        <local:NodeViewModel x:Key="Children"  />
    </UserControl.Resources>
    <Grid x:Name="LayoutRootNodePicture" Height="100" Width="100"
          HorizontalAlignment="Center"  DataContext="{Binding Source={StaticResource Children}, Path=Children}" >
        <Canvas x:Name="ParentCanvas" Background="White" Width="100" Height="100" >
            <Rectangle Fill="Yellow" Stroke="Blue" Width="100" Height="100"   >
                </Rectangle  >

        </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>
</UserControl>

Comment puis-je les combiner en ObservableCollection Children ?

public class NodeViewModel : INotifyPropertyChanged
    {

public ObservableCollection<NodeViewModel> Children
        {
            get { return _children; }
            set
            {
                _children = value;
                NotifyChange("Children");
            }
        }

        private void NotifyChange(string propName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
}

Et comment puis-je utiliser les éléments de cette collection de contrôles ?

Existe-t-il une manière simple (ou correcte) de procéder ?

1voto

vorrtex Points 9356

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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X