简体   繁体   English

如何在WPF中绘制窗口(最佳实践)?

[英]How to draw on a Window in WPF (best practice)?

I am trying to write a small interactive game-like application, where I need to have a Draw method that's gonna draw on screen, but can't figure out how to structure the method for WPF. 我正在尝试编写一个类似于交互式游戏的小应用程序,我需要有一个Draw方法,它将在屏幕上绘制,但无法弄清楚如何构建WPF方法。

If this was Winforms , I could use: 如果这是Winforms ,我可以使用:

public void Draw (Graphics g)
{

}

But for a WPF Window , what should I have on it in the xaml (currently only have a Grid ), and what should this Draw method receive as an argument? 但是对于WPF Window ,我应该在xaml中拥有什么(目前只有一个Grid ),这个Draw方法应该作为参数接收什么?

First I want to do it like this to get it working, then I can think about how to make it more WPF , etc. But now I am more interested in getting this to work. 首先,我想这样做才能让它工作,然后我可以考虑如何使它更多WPF等。但现在我更感兴趣的是让它工作。

Typically, you "draw" in WPF in a completely different manner. 通常,您以完全不同的方式在WPF中“绘制”。

In Windows Forms/GDI, the graphics API is an immediate mode graphics API. 在Windows Forms / GDI中,图形API是立即模式图形API。 Each time the window is refreshed/invalidated, you explicitly draw the contents using Graphics. 每次刷新/无效窗口时,都使用Graphics显式绘制内容。

In WPF, however, things work differently. 但是,在WPF中,事情的工作方式不同。 You rarely ever directly draw - instead, it's a retained mode graphics API. 你很少直接绘制 - 相反,它是一个保留模式图形API。 You tell WPF where you want the objects, and it takes care of the drawing for you. 你告诉WPF你想要对象的位置,它会为你处理绘图。

The best way to think of it is, in Windows Forms, you'd say "Draw a line from X1 to Y1. Then draw a line from X2 to Y2. Then ...". 想到它的最好方法是,在Windows窗体中,你会说“从X1绘制一条线到Y1。然后从X2到Y2画一条线。然后......”。 And you repeat this every time you need to "redraw" since the screen is invalidated. 每次需要“重绘”时重复此操作,因为屏幕无效。

In WPF, instead, you say "I want a line from X1 to Y1. I want a line from X2 to Y2." 相反,在WPF中,你说“我想要一条从X1到Y1的线。我想要一条从X2到Y2的线。” WPF then decides when and how to draw it for you. WPF然后决定何时以及如何为您绘制它。

This is done by placing the shapes on a Canvas, and then letting WPF do all of the hard work. 这是通过将形状放在Canvas上,然后让WPF完成所有艰苦的工作来完成的。

When there are just too many objects to be drawn very quickly (huge Visual Tree) another option would be to use a WriteableBitmap . 当有太多的对象要快速绘制时(巨大的Visual Tree),另一种选择是使用WriteableBitmap Just use the Pixels property to set the pixels and/or use the Render method to draw UIElements . 只需使用Pixels属性设置像素和/或使用Render方法绘制UIElements

I preffer to use OnRender method like in this example: 我喜欢在这个例子中使用OnRender方法:

protected override void OnRender(DrawingContext drawingContext)
{
    base.OnRender(drawingContext);
     drawingContext.DrawRectangle(null, new Pen(Brushes.Black, 2), new Rect(0, 0, ActualWidth, Height));
}

To Implement a Draw loop type behavior in WPF you can use the CompositionTarget.Rendering event. 要在WPF中实现Draw循环类型行为,可以使用CompositionTarget.Rendering事件。 This is raised once per frame when the WPF drawing system is painting frames. 当WPF绘图系统绘制框架时,每帧提升一次。

As others have pointed out this is not very WPF friendly but it will work and can be used to get more immediate drawing behavior out of a WPF app. 正如其他人已经指出的那样,它不是非常友好的WPF,但它可以工作,并且可以用来从WPF应用程序中获得更直接的绘图行为。

In most cases you would use a single root canvas and update say the Canvas position of an element on the CompositionTarget.Rendering event. 在大多数情况下,您将使用单个根画布并更新说明CompositionTarget.Rendering事件上元素的Canvas位置。

For example to make a ellipse fly all over the screen do this: 例如,要使整个屏幕上的椭圆飞行,请执行以下操作:

In your XAML (For a Window that is 640 by 480 in size): 在您的XAML中(对于大小为640 x 480的窗口):

<Canvas x:Name="theCanvas">
    <Ellipse x:Name="theEllipse" Height="10" Width="10" Fill="Black" />     
</Canvas>

In your Code behind for the Window that the above XAML is in (Make sure to add a reference to System.Windows.Media in order to see the CompsitionTarget object : 在上面的XAML所在的Window后面的代码中(确保添加对System.Windows.Media的引用,以便查看CompsitionTarget对象:

    public static Random rand = new Random();
    public View()
    {
        InitializeComponent();
        CompositionTarget.Rendering += CompositionTarget_Rendering;
    }

    void CompositionTarget_Rendering(object sender, System.EventArgs e)
    {
        double newLeft = rand.Next(0, 640);
        double newTop = rand.Next(0, 480);

        theEllipse.SetValue(Canvas.LeftProperty,newLeft);
        theEllipse.SetValue(Canvas.TopProperty, newTop);
    }

Yow should add a Canvas (or change the Grid for a Canvas) and then draw over it. 你应该添加一个画布(或更改画布的网格),然后绘制它。 Here is Microsoft tut on drawing over a canvas 这是微软在画布上绘图

Also, I don't know how related is this other question to yours, but you might want to check it out. 另外,我不知道你的这个问题有多相关,但你可能想看一下。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM