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 wo
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:
INotifyPropertyChanged
.File -> New Project -> WPF Application
and see the results for yourself.Something like :
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;
}
}
}
this?
then use the same for the other grid i suppose