WPF渲染事件没有绘制任何内容

5

我正在尝试将一些WinForm代码转换为WPF,以用于管道网络绘图应用程序。 我一直在参考这篇绘画应用程序文章:

http://www.codeproject.com/Articles/22776/WPF-DrawTools

这是我在WinForms中的代码,我正在尝试将其转换,因为我们需要更多自定义窗口。 我需要做到以下几点:

a) 单击画布以绘制节点 b) 单击并拖动上述节点 c) 悬停并突出显示节点 d) 用链接连接节点

我有以下代码来在画布上绘制矩形,但是当渲染被触发时,画布上没有任何东西出现。 我相对确信已经触发了它,因为在其中放置消息框会导致程序崩溃。

protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);
        SolidColorBrush mySolidColorBrush = new SolidColorBrush();
        mySolidColorBrush.Color = Colors.LimeGreen;
        Pen myPen = new Pen(Brushes.Blue, 10);            
        Rect myRect = new Rect(50, 50, 500, 500);

        drawingContext.DrawRectangle(mySolidColorBrush, myPen, myRect);            
    }

    private void myCanvas_MouseDown(object sender, MouseButtonEventArgs e)
    {
        System.Windows.Forms.MessageBox.Show("click event fired");                         

        DrawingVisual vs = new DrawingVisual();
        DrawingContext dc = vs.RenderOpen();

        OnRender(dc);
    }

“已解雇”消息框只是为了确保单击事件触发,并且它确实触发了。 XML:
<TabItem Header="View Results">
            <Canvas Background="WhiteSmoke" Name="myCanvas" MouseDown="myCanvas_MouseDown" >                    
            </Canvas>
</TabItem>

发生了什么?文章中的那个人使用了用户控件...这就是我遇到问题的原因吗?WPF让我疯狂...我感觉自己在做完全错误的事情,但是我很难找到相关文档。


你似乎在每个鼠标按下事件中创建了一个新的DrawingVisual - 这是你实际的代码吗? - ChrisF
更新了...我认为这主要适用于更新菜单。我很难理解如何不... - Steel Nation
@SteelNation 看,我得回家了。大概会离线2个小时左右,等我上线后,我会帮你解决这个问题。 - Federico Berasategui
@HighCore 我也是。好的,谢谢你,你真是个救星。 - Steel Nation
给你的画布指定宽度和高度。 - GameAlchemist
显示剩余16条评论
1个回答

7

请看我在20分钟内制作的简单示例:

XAML:

<Window x:Class="NodesEditor.MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:NodesEditor"
        Title="Window1" Height="800" Width="800" x:Name="view">
    <Grid Margin="10">
        <Grid.Resources>
            <!-- This CompositeCollection basically Concatenates the Nodes and Connectors in a single one -->
            <CompositeCollection x:Key="Col">
                <CollectionContainer Collection="{Binding DataContext.Connectors,Source={x:Reference view}}"/>
                <CollectionContainer Collection="{Binding DataContext.Nodes,Source={x:Reference view}}"/>
            </CompositeCollection>

            <!-- This is the DataTemplate that will be used to render the Node class -->
            <DataTemplate DataType="{x:Type local:Node}">
                <Thumb DragDelta="Thumb_Drag">
                    <Thumb.Template>
                        <ControlTemplate TargetType="Thumb">
                            <Ellipse Height="10" Width="10" Stroke="Black" StrokeThickness="1" Fill="Blue"
                                     Margin="-5,-5,5,5" x:Name="Ellipse"/>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsDragging" Value="True">
                                    <Setter TargetName="Ellipse" Property="Fill" Value="Yellow"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Thumb.Template>
                </Thumb>
            </DataTemplate>

            <!-- This is the DataTemplate that will be used to render the Connector class -->
            <DataTemplate DataType="{x:Type local:Connector}">
                <Line Stroke="Black" StrokeThickness="1"
                      X1="{Binding Start.X}" Y1="{Binding Start.Y}"
                      X2="{Binding End.X}" Y2="{Binding End.Y}"/>
            </DataTemplate>
        </Grid.Resources>

        <!-- This Border serves as a background and the VisualBrush used to paint its background serves as the "Snapping Grid" -->
        <!-- The "Snapping" Actually occurs in the Node class (see Node.X and Node.Y properties), it has nothing to do with any UI Elements -->
        <Border>
            <Border.Background>
                <VisualBrush TileMode="Tile"
                             Viewport="0,0,50,50" ViewportUnits="Absolute" 
                             Viewbox="0,0,50,50" ViewboxUnits="Absolute">
                    <VisualBrush.Visual>
                        <Rectangle Stroke="Darkgray" StrokeThickness="1" Height="50" Width="50"
                                   StrokeDashArray="5 3"/>
                    </VisualBrush.Visual>
                </VisualBrush>
            </Border.Background>
        </Border>
        <ItemsControl>
            <ItemsControl.ItemsSource>
                <StaticResource ResourceKey="Col"/>
            </ItemsControl.ItemsSource>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas IsItemsHost="True"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ContentPresenter">
                    <Setter Property="Canvas.Left" Value="{Binding X}"/>
                    <Setter Property="Canvas.Top" Value="{Binding Y}"/>
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>
    </Grid>
</Window>

代码后台:

using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls.Primitives;

namespace NodesEditor
{
    public partial class MainWindow : Window
    {
        public List<Node> Nodes { get; set; }
        public List<Connector> Connectors { get; set; }

        public MainWindow()
        {
            InitializeComponent();

            Nodes = NodesDataSource.GetRandomNodes().ToList();
            Connectors = NodesDataSource.GetRandomConnectors(Nodes).ToList();

            DataContext = this;
        }

        private void Thumb_Drag(object sender, DragDeltaEventArgs e)
        {
            var thumb = sender as Thumb;
            if (thumb == null)
                return;

            var data = thumb.DataContext as Node;
            if (data == null)
                return;

            data.X += e.HorizontalChange;
            data.Y += e.VerticalChange;
        }
    }
}

数据模型:

public class Node: INotifyPropertyChanged
    {
        private double _x;
        public double X
        {
            get { return _x; }
            set
            {
                //"Grid Snapping"
                //this actually "rounds" the value so that it will always be a multiple of 50.
                _x = (Math.Round(value / 50.0)) * 50;
                OnPropertyChanged("X");
            }
        }

        private double _y;
        public double Y
        {
            get { return _y; }
            set
            {
                //"Grid Snapping"
                //this actually "rounds" the value so that it will always be a multiple of 50.
                _y = (Math.Round(value / 50.0)) * 50;
                OnPropertyChanged("Y");
            }
        }


        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

public class Connector
{
    public Node Start { get; set; }
    public Node End { get; set; }
}

随机数据源(用于填充示例)

using System;
using System.Collections.Generic;
using System.Linq;

namespace NodesEditor
{
    public static class NodesDataSource
    {
        public static Random random = new Random();

        public static Node GetRandomNode()
        {
            return new Node
                {
                    X = random.Next(0,500),
                    Y = random.Next(0,500)
                };

        }

        public static IEnumerable<Node> GetRandomNodes()
        {
            return Enumerable.Range(5, random.Next(6, 10)).Select(x => GetRandomNode());
        }

        public static Connector GetRandomConnector(IEnumerable<Node> nodes)
        {
            return new Connector { Start = nodes.FirstOrDefault(), End = nodes.Skip(1).FirstOrDefault() };
        }

        public static IEnumerable<Connector> GetRandomConnectors(List<Node> nodes)
        {
            var result = new List<Connector>();
            for (int i = 0; i < nodes.Count() - 1; i++)
            {
                result.Add(new Connector() {Start = nodes[i], End = nodes[i + 1]});
            }
            return result;
        }
    }
}

这是我的电脑上的界面截图: 这里输入图片描述

1
+1. 你应该修改你默认的批评“WinForms方法比WPF差”的策略 :). 因为除此之外,你提供了很好的建设性回答(我至少看到过两个完整的例子来帮助OPs),这些回答非常有启发性。我知道,因为我曾经处于错误的一端。话说回来,我试图自己制作一个WPF应用程序,但由于我的WPF技能不佳而无法实现。我会看看你的应用程序。谢谢。 - nakiya
如果OP还有一个要求,即在用户双击位置时将节点插入到此网格中,我该如何在此基础上实现它? - nakiya
嗯,通过处理GridOnMouseRightButtonUp事件,我已经让它工作了。虽然在Grid中找不到双击事件。 - nakiya
@Nakiya 抱歉我没有看到你的评论。我刚刚在看那个,但请注意,您应该将任何新元素创建为List中的元素,而不是直接在UI中创建。(您还需要将这些List更改为ObservableCollections)。 - Federico Berasategui
使用VisualBrush TileMode="Tile"是非常棒的。谢谢! - user3690202

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接