2013-04-17 110 views
2

我在窗体窗体上有两个网格,当用户单击第一个网格单元时,我需要显示从一个网格到另一个网格的单元格之间的线条映射,用滚动条表示当用户移动垂直滚动条时,行位置将根据单元格位置而改变。如何在两个DataGridView控件之间绘制线条

请使用下面的链接查看图片以获取更多说明。

http://s8.postimg.org/49s7i2lvp/Mapping.png

任何帮助表示赞赏,并在此先感谢
问候
SHAILESH

+0

我已经在示例WPF应用程序中实现了这一点。我不知道在WinForms中是否有可能。 winforms是一种古老的技术,并不能真正支持这种交互性。如果你对WPF示例感兴趣,请告诉我。 –

+0

请给我WPF样本,以便我能理解.. – mike

回答

0

喜欢的东西:

foreach (DataGridViewRow row in dataGrid.Rows) 
{ 
    var cellValue = row.Cells["field"].Value; 

    if (cellValue != null && cellValue.ToString() == "something") 
    { 
     dataGrid.Rows[row.Index].Selected = true; 
     try 
     { 
      dataGrid.FirstDisplayedScrollingRowIndex = row.Index - 4; 

     } 
     catch (Exception execc) 
     { 
      dataGrid.FirstDisplayedScrollingRowIndex = row.Index; 
     } 
    } 
} 

呢?

然后使用其它栅格相同的I假设

+0

虽然这是有点作弊,但它不会为你做任何行,只要使用这个,你可以使第一个网格上的位置与第二个网格相同。但我想它不是你的后 – lemunk

+0

可能投掷行的颜色代码?或者它是否需要绘制一条线? – lemunk

4

确定。我张贴这个作为答案,因为OP要求它。

这是我的WPF采取的是:背后

<Window x:Class="MiscSamples.DataGridConnectors" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="DataGridConnectors" Height="300" Width="300"> 
    <Grid x:Name="Root"> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition/> 
      <ColumnDefinition/> 
      <ColumnDefinition/> 
     </Grid.ColumnDefinitions> 

     <ItemsControl ItemsSource="{Binding VisibleConnectors}" Grid.ColumnSpan="3"> 
      <ItemsControl.ItemsPanel> 
       <ItemsPanelTemplate> 
        <Canvas IsItemsHost="True"/> 
       </ItemsPanelTemplate> 
      </ItemsControl.ItemsPanel> 
      <ItemsControl.ItemTemplate> 
       <DataTemplate> 
        <Line X1="{Binding StartPoint.X}" 
          Y1="{Binding StartPoint.Y}" 
          X2="{Binding EndPoint.X}" 
          Y2="{Binding EndPoint.Y}" 
          Stroke="Black" 
          StrokeThickness="2"/> 
       </DataTemplate> 
      </ItemsControl.ItemTemplate> 
     </ItemsControl> 

     <DataGrid ItemsSource="{Binding Items1}" x:Name="DG1" AutoGenerateColumns="False"> 
      <DataGrid.Columns> 
       <DataGridTextColumn Binding="{Binding Path=.}"/> 
      </DataGrid.Columns> 
     </DataGrid> 

     <DataGrid ItemsSource="{Binding Items2}" x:Name="DG2" AutoGenerateColumns="False" Grid.Column="2"> 
      <DataGrid.Columns> 
       <DataGridTextColumn Binding="{Binding Path=.}"/> 
      </DataGrid.Columns> 
     </DataGrid> 

     <StackPanel Grid.Column="1"> 
      <Button Content="Sequential" Click="Sequential_Click"/> 
      <Button Content="Random" Click="Random_Click"/> 
     </StackPanel> 
    </Grid> 
</Window> 

代码:

public partial class DataGridConnectors : Window 
    { 
     public List<string> Items1 { get; set; } 

     public List<string> Items2 { get; set; } 

     public List<DataItemConnector> Connectors { get; set; } 

     private ObservableCollection<DataItemConnector> _visibleConnectors; 
     public ObservableCollection<DataItemConnector> VisibleConnectors 
     { 
      get { return _visibleConnectors ?? (_visibleConnectors = new ObservableCollection<DataItemConnector>()); } 
     } 

     public DataGridConnectors() 
     { 
      Connectors = new List<DataItemConnector>(); 

      InitializeComponent(); 
      Loaded += OnLoaded; 

      Items1 = Enumerable.Range(0, 1000).Select(x => "Item1 - " + x.ToString()).ToList(); 
      Items2 = Enumerable.Range(0, 1000).Select(x => "Item2 - " + x.ToString()).ToList(); 

      DataContext = this; 
     } 

     private void OnLoaded(object sender, RoutedEventArgs routedEventArgs) 
     { 
      var scrollviewer1 = FindDescendent<ScrollViewer>(DG1).FirstOrDefault(); 
      var scrollviewer2 = FindDescendent<ScrollViewer>(DG2).FirstOrDefault(); 

      if (scrollviewer1 != null) 
       scrollviewer1.ScrollChanged += scrollviewer_ScrollChanged; 

      if (scrollviewer2 != null) 
       scrollviewer2.ScrollChanged += scrollviewer_ScrollChanged; 
     } 

     private void scrollviewer_ScrollChanged(object sender, ScrollChangedEventArgs e) 
     { 
      var visiblerows1 = GetVisibleContainers(Items1, DG1.ItemContainerGenerator); 
      var visiblerows2 = GetVisibleContainers(Items2, DG2.ItemContainerGenerator); 

      var visibleitems1 = visiblerows1.Select(x => x.DataContext); 
      var visibleitems2 = visiblerows2.Select(x => x.DataContext); 

      var visibleconnectors = Connectors.Where(x => visibleitems1.Contains(x.Start) && 
                  visibleitems2.Contains(x.End)); 

      VisibleConnectors.Where(x => !visibleconnectors.Contains(x)) 
          .ToList() 
          .ForEach(x => VisibleConnectors.Remove(x)); 

      visibleconnectors.Where(x => !VisibleConnectors.Contains(x)) 
          .ToList() 
          .ForEach(x => VisibleConnectors.Add(x)); 

      foreach(var connector in VisibleConnectors) 
      { 
       var startrow = visiblerows1.FirstOrDefault(x => x.DataContext == connector.Start); 
       var endrow = visiblerows2.FirstOrDefault(x => x.DataContext == connector.End); 

       if (startrow != null) 
        connector.StartPoint = Point.Add(startrow.TransformToAncestor(Root).Transform(new Point(0, 0)), 
                new Vector(startrow.ActualWidth + 5, (startrow.ActualHeight/2)*-1)); 

       if (endrow != null) 
        connector.EndPoint = Point.Add(endrow.TransformToAncestor(Root).Transform(new Point(0, 0)), 
                new Vector(-5,(endrow.ActualHeight/2) * -1)); 

      } 

     } 

     private static List<FrameworkElement> GetVisibleContainers(IEnumerable<object> source, ItemContainerGenerator generator) 
     { 
      return source.Select(generator.ContainerFromItem).Where(x => x != null).OfType<FrameworkElement>().ToList(); 
     } 

     public static List<T> FindDescendent<T>(DependencyObject element) where T : DependencyObject 
     { 
      var f = new List<T>(); 
      for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) 
      { 
       var child = VisualTreeHelper.GetChild(element, i); 

       if (child is T) 
        f.Add((T)child); 

       f.AddRange(FindDescendent<T>(child)); 
      } 
      return f; 
     } 

     private void Sequential_Click(object sender, RoutedEventArgs e) 
     { 
      Connectors.Clear(); 
      Enumerable.Range(0, 1000).Select(x => new DataItemConnector() { Start = Items1[x], End = Items2[x] }) 
            .ToList() 
            .ForEach(x => Connectors.Add(x)); 

      scrollviewer_ScrollChanged(null, null); 
     } 

     private void Random_Click(object sender, RoutedEventArgs e) 
     { 
      Connectors.Clear(); 
      var random = new Random(); 

      Enumerable.Range(500, random.Next(600, 1000)) 
         .Select(x => new DataItemConnector() 
            { 
             Start = Items1[random.Next(0, 999)], 
             End = Items2[random.Next(0, 999)] 
            }) 
         .ToList() 
         .ForEach(Connectors.Add); 


      scrollviewer_ScrollChanged(null, null); 
     } 
    } 

连接器:

public class DataItemConnector: PropertyChangedBase 
    { 
     public object Start { get; set; } 
     public object End { get; set; } 

     private Point _startPoint; 
     public Point StartPoint 
     { 
      get { return _startPoint; } 
      set 
      { 
       _startPoint = value; 
       OnPropertyChanged("StartPoint"); 
      } 
     } 

     private Point _endPoint; 
     public Point EndPoint 
     { 
      get { return _endPoint; } 
      set 
      { 
       _endPoint = value; 
       OnPropertyChanged("EndPoint"); 
      } 
     } 
    } 

基类来支持双向绑定:

public class PropertyChangedBase:INotifyPropertyChanged 
    { 
     public event PropertyChangedEventHandler PropertyChanged; 

     protected virtual void OnPropertyChanged(string propertyName) 
     { 
      Application.Current.Dispatcher.BeginInvoke((Action) (() => 
       { 
        PropertyChangedEventHandler handler = PropertyChanged; 
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); 
       })); 
     } 
    } 

结果:

enter image description here

  • 分辨率无关。尝试调整窗口大小并亲自查看。代码真的很简单。大部分背后的代码实际上是支持该示例的样板(生成随机值等)。
  • 没有“所有者抽签”,没有P/Invoke。只是简单,简单的属性和INotifyPropertyChanged
  • WPF规则。只需将我的代码复制并粘贴到File -> New Project -> WPF Application中即可自行查看结果。
相关问题