How to set x, y coordinates of WPF canvas children

2020-08-16 08:28发布

问题:

How to set x, y coordinates of WPF canvas children through code? Below is my sample code.

Canvas root = new Canvas();
double y = 5;
for (int i=0; i< 10; i++)
{
    Ellipse e = new Ellipse();
    e.Height=10;
    e.Width=10;
    e.Stroke =Brushes.Black;

    root.Children.Add(e);
    y +=10;
}

MyRootCanvas = root;

MyRootCanvas is a property of type Canvas bound to WPF UserControl's content.

回答1:

Use Canvas.SetLeft and Canvas.SetTop methods to set x, y coordinate for child:

for (int I=1; I<= 10; I++)
{
    Ellipse e = new Ellipse();
    e.Height=10;
    e.Width=10;
    e.Stroke =Brushes.Black;

    Canvas.SetLeft(e, 10); <-- HERE
    Canvas.SetTop(e, Y);

    root.Children.Add(e);
    Y +=10;
}


回答2:

I know this question has already been answered but what HighCore is talking about (i.e. don't manipulate UI elements in code) cannot be stated strongly enough. To do this properly you should create a class to encapsulate the data you want to display:

public class Widget : ViewModelBase
{
    private double _X;
    public double X
    {
        get { return _X;}
        set { _X = value; RaisePropertyChanged(() => this.X); }
    }

    private double _Y;
    public double Y
    {
        get { return _Y;}
        set { _Y = value; RaisePropertyChanged(() => this.Y); }
    }

    private double _Width;
    public double Width
    {
        get { return _Width;}
        set { _Width = value; RaisePropertyChanged(() => this.Width); }
    }

    private double _Height;
    public double Height
    {
        get { return _Height;}
        set { _Height = value; RaisePropertyChanged(() => this.Height); }
    }

    private System.Windows.Media.Color _Color;
    public System.Windows.Media.Color Color
    {
        get { return _Color;}
        set { _Color = value; RaisePropertyChanged(() => this.Color); }
    }
}

It's a bit annoying having to create these dependency properties for each parameter, I use the built-in Code Snippets Manager to create a snippet for me so that I don't have to type in the whole thing every time. Next you want to create an array of these and put them in a view model somewhere, that's where the logic to generate your ellipses should go:

public MyViewModel()
{
    for (int y = 0; y < 10; y++)
        for (int x = 0; x < 10; x++)
            this.Items.Add(new Widget {
                X = x * 20,
                Y = y * 20,
                Width = 10,
                Height = 10,
                Color = Color.FromArgb(255, (byte)(x * 20), (byte)(y * 20), 0)
            });
}

You would then set an instance of MyViewModel as the data context for the window containing your canvas. All that remains is the XAML that loosely binds to this view model, you want to render a list of items onto a canvas so you use ItemsControl and replace the default items panel with a canvas:

<ItemsControl ItemsSource="{Binding Items}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Ellipse Width="{Binding Width}" Height="{Binding Height}">
                <Ellipse.Stroke>
                    <SolidColorBrush Color="{Binding Color}" />
                </Ellipse.Stroke>
            </Ellipse>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemContainerStyle>
        <Style>
            <Setter Property="Canvas.Left" Value="{Binding X}" />
            <Setter Property="Canvas.Top" Value="{Binding Y}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>

And here's the result:

You now have your front-end data-bound to your view model so you can add/remove items or change individual properties etc and the changes will propagate through. You can also unit test your logic without having to actually create the view controls at all.