WPF渲染事件不绘制任何东西

问题描述:

我试图将一些WinForm代码转换为WPF管道网络绘图应用程序。我一直立足而过这种涂料的应用文章: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让我感到疯狂......我觉得我正在做一些完全错误的事情,但是我可以在这个主题上找到很少的文档。

+0

你显然每个鼠标按下事件创建一个新的DrawingVisual - 是你的实际代码? – ChrisF 2013-03-14 23:03:33

+0

'WPF让我疯狂......' - 那是因为你使用WPF以winforms的方式。为什么在这个世界上你会搞乱'OnRender()'??。请张贴你需要的截图,我们可以给你一个正确的方法来在XAML中做到这一点。还记得我告诉过你不要在代码中操纵UI元素吗?还记得我还告诉过你,你需要“以一种抽象的方式来思考你的UI”吗? – 2013-03-14 23:03:45

+0

更新...我认为主要适用于更新菜单。我有很多麻烦理解如何不... – 2013-03-14 23:11:56

看,这是一个简单的例子,我在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; } 
} 

随机数据来源(填补实例东西mple)

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

这就是它看起来像在我的电脑:

enter image description here

+1

+1。你应该修改你的'WinForms方法到WPF'的默认策略:)。因为否则你提供很好的建设性的答案(我已经看到至少有两个完整的例子给你,以帮助OP),这是非常有启发性的。我知道,因为我曾经犯过一次错误的结局。不过在主题上,我试图让自己做一个WPF应用程序,但由于我的可怜的WPF sw was声而无法完成。将看看你的应用程序。谢谢。 – nakiya 2013-03-15 02:27:00

+0

如果OP有要求在用户双击某个位置时将节点插入此网格又该怎么办?我应该如何去实现这个呢? – nakiya 2013-03-15 02:48:01

+0

嗯,通过处理'Grid'的OnMouseRightButtonUp事件来实现这个工作。尽管在Grid中找不到双击事件。 – nakiya 2013-03-15 03:10:01