2013-03-14 265 views
4

我試圖將一些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

回答

6

看,這是一個簡單的例子,我在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