Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
936 views
in Technique[技术] by (71.8m points)

c# - How to Draw line/s between Two DataGridView Controls

i have two grid on Window form , i need to show mapping by lines between cell/s from one grid to other grid when user click on first grid cell and also this functionality works with scroll bar means line position will change according to the cell position when user moves vertical scroll bar.

please use below link to see image for more clarification.

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

Any help is appreciated and Thanks in advance
Best Regards
Shailesh

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Ok. Im posting this as an answer because the OP asked for it.

This is my WPF take on that:

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

Code Behind:

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

Connector:

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

Base class to support two way binding:

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

Result:

enter image description here

  • Resolution independent. Try resizing the window and see it for yourself.
  • The code is really really simple. Most of the code behind is actually boilerplate to support the example (generate random values, etc).
  • No "owner draw", No P/Invoke. Just simple, simple properties and INotifyPropertyChanged.
  • WPF rules. Just copy and paste my code in a File -> New Project -> WPF Application and see the results for yourself.

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...