2

ウィンドウフォームに2つのグリッドがあります。ユーザーが最初のグリッドセルをクリックしたときに、あるグリッドから他のグリッドへのセル間の線によるマッピングを表示する必要があります。また、この機能はスクロールバーで動作し、セルの位置に応じて線の位置が変化することを意味しますユーザーが垂直スクロール バーを移動したとき。

詳細については、以下のリンクを使用して画像を参照してください。

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

よろしく
お願い
いたします。

4

2 に答える 2

4

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

結果:

ここに画像の説明を入力

  • 解像度に依存しません。ウィンドウのサイズを変更してみてください。
  • コードは本当にシンプルです。コード ビハインドのほとんどは、実際には例をサポートする定型文です (ランダム値の生成など)。
  • 「オーナードロー」なし、P/Invokeなし。シンプルでシンプルなプロパティとINotifyPropertyChanged.
  • WPF ルール。コードをコピーして a に貼り付けるだけでFile -> New Project -> WPF Application、結果を確認できます。
于 2013-04-18T16:00:00.473 に答える