1
votes

Comment centrer des formes en fonction de la taille du canevas à l'aide de MVVM

Avoir un code pour afficher un cercle (en utilisant une ellipse avec une largeur et une hauteur égales, et aussi en utilisant une ui réactive pour les notifications) Je veux dessiner un cercle dans le au milieu du canevas, mais gère également la mise à jour du redimensionnement.

Le code actuel définit Canvas Left et Canvas Top , mais je ne sais pas comment définir le cercle au milieu et pour remplir presque tout le canevas.

classes:

<ItemsControl ItemsSource="{Binding Path=Shapes}">
        <ItemsControl.Resources>           
            <DataTemplate DataType="{x:Type entities:Circle}">
                <Ellipse Width="{Binding Radius}" 
                         Height="{Binding Radius}"
                         Canvas.Top="{Binding Top, Mode=TwoWay}" 
                         Canvas.Left="{Binding Left, Mode=TwoWay}"
                         >
                    <Ellipse.Stroke>
                        <SolidColorBrush Color="{Binding Color}" />
                    </Ellipse.Stroke>
                </Ellipse>
            </DataTemplate>

        </ItemsControl.Resources>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Top" Value="{Binding Path=Top, Mode=TwoWay}" />
                <Setter Property="Canvas.Left" Value="{Binding Path=Left, Mode=TwoWay}" />
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>

xaml:

public class MyViewModel : ReactiveObject
{
    public ObservableCollection<IShape> Shapes
    {
        get => _shapes;
        set => this.RaiseAndSetIfChanged(ref _shapes, value);
    }
    private ObservableCollection<IShape> _shapes;

    public MainViewModel()
    {
      //here the location is set, but how to adjust it to the center of canvas?
        Shapes = new ObservableCollection<IShape>
        {
            new Circle {
                Top = 100,
                Left = 100,
                Radius = 50,
                Color = Color.FromArgb(255, 233,222, 0) 
            }                
        };
    }       
}

public interface IShape
{
    int Top { get; set; }
    int Left { get; set; }
}

public abstract class Shape : ReactiveObject, IShape
{
    private int _top;
    private int _left;

    public int Top
    {
        get { return _top; }
        set { this.RaiseAndSetIfChanged(ref _top, value); }
    }

    public int Left
    {
        get { return _left; }
        set { this.RaiseAndSetIfChanged(ref _left, value); }
    }
}

public class Circle : Shape
{
    private int _radius;
    private Color _color;

    public int Radius
    {
        get => _radius;
        set => this.RaiseAndSetIfChanged(ref _radius, value);
    }

    public System.Windows.Media.Color Color
    {
        get => _color;
        set => this.RaiseAndSetIfChanged(ref _color, value);
    }
}

this produit:

 entrez la description de l'image ici

Comment puis-je changer le xaml ou le code derrière strong > pour centrer et définir le cercle presque comme la taille de la toile comme ?:

entrez la description de l'image ici


0 commentaires

3 Réponses :


0
votes

Donnez à l'ellipse une transformation de rendu:

<Ellipse Width="50" Height="50" Fill="Yellow" Stroke="CornflowerBlue" StrokeThickness="                    
    <Ellipse.RenderTransform>
        <TranslateTransform X="-25" Y="-25" /> <!-- center the ellipse -->
    </Ellipse.RenderTransform>
</Ellipse>


1 commentaires

Ne comprenez pas complètement, comment pourrais-je rendre la largeur et la hauteur variables et les laisser par exemple presque la zone de la toile? Peut-être lier une propriété canvas ou quelque chose de similaire



1
votes

Pour rendre votre elipse presque aussi grande que le canevas en liant la taille de l'elipse à la taille du canevas et en utilisant un convertisseur qui convertira les valeurs d'entrée en (par exemple) 0,9 fois la taille du canevas comme ceci: XAML

    public class ScaleZeroNine : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return System.Convert.ToDouble(value) * 0.9;
    }

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

C#

            <Canvas Name="MyCanvas">
            <Ellipse Height="{Binding ElementName=MyCanvas, Path=ActualHeight, Converter={StaticResource MyScaleConverter}}" Width="{Binding ElementName=MyCanvas, Path=ActualWidth, Converter={StaticResource MyScaleConverter}}"></Ellipse>
        </Canvas>

Concernant le problème central: Pourquoi utilisez-vous même une toile? y a-t-il une raison spécifique à cela? Canvas rend l'utilisation de la pleine puissance de WPF difficile car son arrangement ressemble plus à winForms. Lorsque vous utilisez par exemple une grille, elle se centre automatiquement et peut également être définie pour le faire


7 commentaires

Bonne réponse Denis, je veux faire quelque chose en utilisant MVVM, alors quelle serait la meilleure approche pour initialiser toutes les formes au centre de l'écran, en ajoutant simplement une grille comme: ´Définir des définitions de grille comme: ´ suffirait, ou comment initier tous les éléments au centre?


Je ne sais pas si je comprends vraiment votre question à ce stade ... mais si vous voulez vraiment suivre la voie facile et éviter le convertisseur, vous pouvez le faire comme ceci: Ce n'est pas la manière la plus élégante mais peut-être que cela aide ... Sinon, essayez de le faire d'une manière différente


* demander En général, il est logique de ne pas définir la taille de la grille afin que l'ensemble du contrôle reste évolutif dès que vous modifiez la taille de la fenêtre .. MVVM n'a absolument rien à voir avec cette disposition spécifique..es-vous sûr de comprendre le concept de MVVM?


Eh bien, la question est de savoir comment initier les objets créés au modèle de vue, je veux dire, pour les créer au centre de l'écran par exemple, c'est parce que, sur MainViewModel (), j'ajoute les formes comme: Shapes = new ObservableCollection { ...}


D'accord, lorsque vous utilisez le modèle MVVM, vous obtenez votre vue réelle, par exemple MainWindow, son code-behind et le ViewModel. Dans le ViewModel, vous voulez avoir le code de l'application, c'est-à-dire la fonction réelle de votre programme. Vous ne souhaitez pas modifier l'interface graphique directement à partir d'ici car cela briserait le modèle MVVM. Cela signifie également que vous ne souhaitez pas initialiser les composants de l'interface graphique à partir d'ici. Ce que vous voulez faire, je suppose, c'est que le cercle et les lignes apparaissent et se cachent au moment de l'exécution ...


Pour ce faire, créez correctement l'élément dans votre vue à l'aide de XAML et liez sa visibilité à une propriété de dépendance dans votre vue. Vous pouvez désormais le masquer et l'afficher pendant l'exécution.


Si vous décidez de ne pas emprunter la route MVVM et de créer ces éléments pendant l'exécution, vous aurez besoin d'une instance de la classe à laquelle vous essayez d'ajouter un élément. Si c'est la MainWindow, c'est assez simple, vous pouvez faire ce qui suit: Ellipse eli = new Ellipse (); eli.Margin = nouvelle épaisseur (5); eli.Stroke = Brushes.Black; eli.StrokeThickness = 1; (Application.Current.MainWindow as MainWindow) .YourGridNameHere.Children.Add (eli);



0
votes

Pour compléter la réponse, j'ai utilisé la multi-liaison avec un convertisseur de valeur personnalisé.

 <DockPanel>
        <DockPanel.Resources>
            <local:HalfValueConverter x:Key="HalfValue" />
        </DockPanel.Resources>
        <Canvas x:Name="canvas">
            <Ellipse 
                     Height="{Binding ElementName=canvas, Path=ActualHeight, Converter={StaticResource Escalated}}" 
                Width="{Binding ElementName=canvas, Path=ActualWidth, Converter={StaticResource Escalated}}"
                     Stroke="Red"
                     x:Name="ellipse">
                <Canvas.Left>
                    <MultiBinding Converter="{StaticResource HalfValue}">
                        <Binding ElementName="canvas" Path="ActualWidth" />
                        <Binding ElementName="ellipse" Path="ActualWidth" />
                    </MultiBinding>
                </Canvas.Left>
                <Canvas.Top>
                    <MultiBinding Converter="{StaticResource HalfValue}">
                        <Binding ElementName="canvas" Path="ActualHeight" />
                        <Binding ElementName="ellipse" Path="ActualHeight" />
                    </MultiBinding>
                </Canvas.Top>
            </Ellipse>
        </Canvas>
    </DockPanel>

puis en xaml

public class HalfValueConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values,
                          Type targetType,
                          object parameter,
                          CultureInfo culture)
    {
        if (values == null || values.Length < 2)
        {
            throw new ArgumentException(
                "HalfValueConverter expects 2 double values to be passed" +
                " in this order -> totalWidth, width",
                "values");
        }

        double totalWidth = (double)values[0];
        double width = (double)values[1];
        return (object)((totalWidth - width) / 2);
    }

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

    #endregion
}


0 commentaires