【问题标题】:How to set x, y coordinates of WPF canvas children through code?如何通过代码设置WPF画布子项的x、y坐标?
【发布时间】:2014-03-05 08:17:37
【问题描述】:

如何通过代码设置WPF画布子项的x、y坐标? 下面是我的示例代码。

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 是绑定到 WPF UserControl 内容的 Canvas 类型的属性。

【问题讨论】:

  • @HighCore 我需要动态加载控件。
  • 然后使用ItemsControl。 WPF 的dynamically 思想与其他技术确实不同。我强烈建议你学会正确使用WPF,否则你会因为Visual Tree的复杂性而遇到各种麻烦。
  • @HighCore 我正在使用 ItemsControl。我的 UserControl 被加载到 ItemsControl 中。
  • 如果您使用的是ItemsControl,则无需在程序代码中创建或操作 UI 元素。这就是 XAML 的用途。正确使用 DataBinding。
  • 如果您有很多 UI 元素,那么适当的 DataBinding 方法会更好,因为它最终可能会由于使用 UI 虚拟化的能力而提高性能,而您当前的过程代码方法不会。来自MSDN以下是禁用 UI 虚拟化的条件列表: - 项目容器直接添加到 ItemsControl。例如,如果应用程序将 ListBoxItem 对象显式添加到 ListBox,则 ListBox 不会虚拟化 ListBoxItem 对象。

标签: c# .net wpf wpf-controls


【解决方案1】:

使用Canvas.SetLeftCanvas.SetTop 方法为孩子设置x,y坐标

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;
}

【讨论】:

  • 您确定订单吗?在设置其位置之前不应该添加孩子。无论如何,我做到了,它有效,因此我会标记你的答案,但是,请为其他用户更正它。
  • Canvas.SetLeftCanvas.SetTop 是画布的静态方法。您可以在将项目实际添加到画布之前进行设置。附加属性始终可以在添加父项之前设置。
【解决方案2】:

我知道这个问题已经得到解答,但 HighCore 所谈论的内容(即不要在代码中操纵 UI 元素)不能足够有力地说明。要正确执行此操作,您应该创建一个类来封装您要显示的数据:

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); }
    }
}

为每个参数创建这些依赖属性有点烦人,我使用内置的代码片段管理器为我创建一个 sn-p,这样我就不必每次都输入整个内容。接下来,您要创建一个数组并将它们放在某个视图模型中,那是生成椭圆的逻辑应该去的地方:

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)
            });
}

然后,您可以将 MyViewModel 的一个实例设置为包含您的画布的窗口的数据上下文。剩下的就是松散绑定到此视图模型的 XAML,您希望将项目列表呈现到画布上,以便使用 ItemsControl 并将默认项目面板替换为画布:

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

结果如下:

您现在将前端数据绑定到您的视图模型,因此您可以添加/删除项目或更改单个属性等,并且更改将传播。您还可以对您的逻辑进行单元测试,而无需实际创建视图控件。

【讨论】:

  • 我知道这篇文章是多年前的,但我发现它很有用。
  • @Brock 谢谢!很高兴看到旧帖子仍在帮助人们!
  • 如果我稍后(例如响应按钮)在项目中添加或删除小部件,UI 不会更新。我玩过额外的 RaisePropertyChange 调用,但没有成功。更改属性(例如 X 或 Y)确实会更新 UI,但删除或添加不会。对我有什么建议吗?我已经几十年没有在 Windows 中编程了。
  • @Brock 听起来您的 Items 集合不支持 INotifyCollectionChanged,尝试将其设为 ObservableCollection<T> 类型。
  • 你这个人。修改集合时,我还必须使用App.Current.Dispatcher.Invoke((Action)delegate,但它现在可以工作了。
猜你喜欢
  • 2011-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-02
  • 2022-01-21
  • 2012-07-25
  • 2022-01-07
相关资源
最近更新 更多