この詳細なガイドでは、データをファイルから Network Node コントロールに読み込む方法を示します。例として、Graph Modelling Language (GML) ファイルが使用されます。トピックの最後で、完全なコード例を提供します。トピックは以下のとおりです。
Network Node コントロールは、IEnumerable の形式でデータをサポートしますが、そのデータはファイルから簡単に読み取ることができます。この詳細なガイドでは、データを GML から Network Node コントロールに読み込む方法を示します。
以下は最終結果のプレビューです。
この記事は、「Network Node コントロールの追加」の記事を既に読んでいることを前提とし、開始点として詳細説明でこのコードを使用します。概要: 以下は実装の概念的概要です。
ノード レイアウトの構成
GML データの読み込み
DownloadStringCompleted イベントの処理
(オプション) 結果を確認します
GML データを読み込みます。
提供される GML パーサーはデータ モデルを以下のように使用します。
XAML の場合:
<Grid x:Name="LayoutRoot" Background="White"> <ig:XamNetworkNode x:Name="xnn"> <ig:XamNetworkNode.GlobalNodeLayouts> <ig:NetworkNodeNodeLayout TargetTypeName = "GmlNode" DisplayMemberPath = "Id" ToolTipMemberPath = "Label" ConnectionsMemberPath = "Connections" ConnectionTargetMemberPath = "Target" /> </ig:XamNetworkNode.GlobalNodeLayouts> </ig:XamNetworkNode> </Grid>
アプリケーション コンストラクターで、InitializeComponent を呼び出した後に、WebClient インスタンスを使用して、GML データを文字列として読み込みます。便宜上提供された GML ファイル Graph2.gml を使用します。
C# の場合:
public MainPage() { InitializeComponent(); var webClient = new WebClient(); webClient.DownloadStringCompleted += GmlFileLoaded; webClient.DownloadStringAsync(new Uri("Graph2.gml", UriKind.RelativeOrAbsolute)); }
Visual Basic の場合:
Public Sub New() InitializeComponent() Dim webClient = New WebClient() AddHandler webClient.DownloadStringCompleted, AddressOf GmlFileLoaded webClient.DownloadStringAsync(New Uri("Graph2.gml", UriKind.RelativeOrAbsolute)) End Sub
DownloadStringCompleted イベントを処理します。
ハンドラーはファイル データを解析するように構成され、Network Node コントロールの ItemsSource プロパティを設定します。
C# の場合:
private void GmlFileLoaded(object sender, DownloadStringCompletedEventArgs e) { var stream = new StringReader(e.Result); var nodes = new GmlParser().Parse(stream); xnn.ItemsSource = nodes; }
Visual Basic の場合:
Private Sub GmlFileLoaded(sender As Object, e As DownloadStringCompletedEventArgs) Dim stream = New StringReader(e.Result) Dim nodes = New GmlParser().Parse(stream) xnn.ItemsSource = nodes End Sub
DownloadStringCompletedEventArgs Result プロパティは文字列として書式設定された GML データを持ち、GmlParser はインフラジスティックスが提供するクラスです。GmlParser.Parse は TextReader オブジェクトを取得し、文字列を解析して、IEnumerable を返します。GmlNode と その他のユーティリティ クラスは GmlParser で定義されます。
(オプション) 結果を確認します。
実装を検証するために、アプリケーションを実行します。この時点で、Graph2.gml からのデータは、上記の図 1 に示すように表示されます。
以下は完全なコードです。
XAML の場合:
<UserControl x:Class="xamNetworkNode_LoadingGmlData.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ig="http://schemas.infragistics.com/xaml" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <Grid x:Name="LayoutRoot" Background="White"> <ig:XamNetworkNode x:Name="xnn"> <ig:XamNetworkNode.GlobalNodeLayouts> <ig:NetworkNodeNodeLayout TargetTypeName = "GmlNode" DisplayMemberPath = "Id" ToolTipMemberPath = "Label" ConnectionsMemberPath = "Connections" ConnectionTargetMemberPath = "Target" /> </ig:XamNetworkNode.GlobalNodeLayouts> </ig:XamNetworkNode> </Grid> </UserControl>
C# の場合:
using System; using System.IO; using System.Net; using System.Windows.Controls; using xamNetworkNode_LoadingGmlData.Resources; namespace xamNetworkNode_LoadingGmlData { public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); var webClient = new WebClient(); webClient.DownloadStringCompleted += GmlFileLoaded; webClient.DownloadStringAsync(new Uri("Graph2.gml", UriKind.RelativeOrAbsolute)); } private void GmlFileLoaded(object sender, DownloadStringCompletedEventArgs e) { var stream = new StringReader(e.Result); var nodes = new GmlParser().Parse(stream); xnn.ItemsSource = nodes; } } }
Visual Basic の場合:
Imports System Imports System.IO Imports System.Net Imports System.Windows.Controls Imports xamNetworkNode_LoadingGmlData.Resources Namespace xamNetworkNode_LoadingGmlData Public Partial Class MainPage Inherits UserControl Public Sub New() InitializeComponent() Dim webClient = New WebClient() AddHandler webClient.DownloadStringCompleted, AddressOf GmlFileLoaded webClient.DownloadStringAsync(New Uri("Graph2.gml", UriKind.RelativeOrAbsolute)) End Sub Private Sub GmlFileLoaded(sender As Object, e As DownloadStringCompletedEventArgs) Dim stream = New StringReader(e.Result) Dim nodes = New GmlParser().Parse(stream) xnn.ItemsSource = nodes End Sub End Class End Namespace
C# の場合:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; /* * このパーサーは、劣等一般公衆利用許諾書の下で、Universitat Passau によってリリースされた C コードに基づいています * http://www.fim.uni-passau.de/en/fim/faculty/chairs/theoretische-informatik/projects.html * * したがってこのクラスは、同じライセンスの下で配布されます */ namespace xamNetworkNode_LoadingGmlData.Resources { public class GmlNode { public int Id { get; set; } public string Label { get; set; } public IList<GmlConnection> Connections { get; set; } public GmlNode() { Connections = new List<GmlConnection>(); } } public class GmlConnection { public GmlNode Target { get; set; } public string Label { get; set; } } public class GmlParser { private List<GmlNode> _nodes; public IEnumerable<GmlNode> Parse(TextReader reader) { var scanner = new GmlScanner(); string propertyKey = null; GmlConnection connection = null; var lastElement = 0; _nodes = new List<GmlNode>(); foreach (var token in scanner.GetTokens(reader)) { switch (token.Type) { case GmlTokenType.Key: switch (token.Data) { case "node": _nodes.Add(new GmlNode()); lastElement = 1; break; case "edge": connection = new GmlConnection(); lastElement = 2; break; case "id": case "label": case "source": case "target": if (lastElement > 0) { propertyKey = token.Data; } else { propertyKey = null; } break; default: lastElement = 0; break; } break; case GmlTokenType.Int: if (propertyKey != null) { switch (propertyKey) { case "id": if (lastElement == 1) { _nodes[_nodes.Count - 1].Id = Convert.ToInt32(token.Data); } break; case "source": var sourceId = Convert.ToInt32(token.Data); FindNodeById(sourceId).Connections.Add(connection); break; case "target": var targetId = Convert.ToInt32(token.Data); connection.Target = FindNodeById(targetId); break; } } break; case GmlTokenType.String: if (propertyKey == "label") { if (lastElement == 1) { _nodes[_nodes.Count - 1].Label = token.Data; } else if (lastElement == 2) { connection.Label = token.Data; } } break; } } return _nodes; } private GmlNode FindNodeById(int id) { return _nodes.First(node => node.Id == id); } } public class GmlScanner { private static readonly string[] IsoTable = { " ", "¡", "¢", "£", "¤", "¥", "¦", "§", "¨", "©", "ª", "«", "¬", "", "®", "¯", "°", "±", "²", "³", "´", "µ", "¶", "·", "¸", "¹", "º", "»", "¼", "½", "¾", "¿", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "×", "Ø", "Ù", "Ú", "Û", "Ü", "Ý", "Þ", "ß", "à", "á", "â", "ã", "ä", "å", "æ", "ç", "è", "é", "ê", "ë", "ì", "í", "î", "ï", "ð", "ñ", "ò", "ó", "ô", "õ", "ö", "÷", "ø", "ù", "ú", "û", "ü", "ý", "þ", "ÿ" }; private static int DecodeIsoChar(string str, int len) { int i; int ret = '&'; if (string.Compare(str, 0, """, 0, len) == 0) { return 34; } if (string.Compare(str, 0, "&", 0, len) == 0) { return 38; } if (string.Compare(str, 0, "<", 0, len) == 0) { return 60; } if (string.Compare(str, 0, ">", 0, len) == 0) { return 62; } for (i = 0; i < 96; i++) { if (string.Compare(str, 0, IsoTable[i], 0, len) == 0) { ret = i + 160; break; } } return ret; } public IEnumerable<GmlToken> GetTokens(TextReader reader) { var line = 1; var column = 1; var sb = new StringBuilder(); var i = reader.Peek(); while (i > -1) { var c = (char)i; if (char.IsWhiteSpace(c)) { if (c == '\n') { line++; column = 1; } else { column++; } reader.Read(); } else { int previousLine; int previousColumn; if (char.IsDigit(c) || c == '.' || c == '+' || c == '-') { var isDouble = false; previousLine = line; previousColumn = column; do { if (c == '.') { if (isDouble) { throw new GmlParseException(line, column); } isDouble = true; } sb.Append(c); column++; reader.Read(); c = (char)reader.Peek(); } while (char.IsDigit(c) || c == '.'); if (!char.IsWhiteSpace(c)) { throw new GmlParseException(line, column); } if (c == '\r' || c == '\n') { line++; column = 1; } if (isDouble) { yield return new GmlToken(GmlTokenType.Double, previousLine, previousColumn, sb.ToString()); } else { yield return new GmlToken(GmlTokenType.Int, previousLine, previousColumn, sb.ToString()); } } else if (char.IsLetter(c) || c == '_') { previousLine = line; previousColumn = column; do { sb.Append(c); column++; reader.Read(); c = (char)reader.Peek(); } while (char.IsLetterOrDigit(c) || c == '_'); if (c == '\r' || c == '\n') { line++; column = 1; } if (!char.IsWhiteSpace(c)) { throw new GmlParseException(line, column); } yield return new GmlToken(GmlTokenType.Key, previousLine, previousColumn, sb.ToString()); } else { previousLine = line; previousColumn = column; switch (c) { case '#': do { reader.Read(); c = (char)reader.Peek(); } while (c != '\r' && c != '\n' && c != -1); line++; column = 1; break; case '[': line++; reader.Read(); yield return new GmlToken(GmlTokenType.LBracket, previousLine, previousColumn); break; case ']': line++; reader.Read(); yield return new GmlToken(GmlTokenType.RBracket, previousLine, previousColumn); break; case '"': column++; reader.Read(); c = (char)reader.Peek(); while (c != '"') { if (c == '&') { var iso = new StringBuilder(8); while (c != ';') { if (c == '"' || c == -1) { iso.Clear(); break; } if (iso.Length < 8) { iso.Append(c); } reader.Read(); c = (char)reader.Peek(); } if (iso.Length == 8) { c = '&'; } else { iso.Append(";"); c = (char)DecodeIsoChar(iso.ToString(), iso.Length); } } sb.Append(c); column++; reader.Read(); c = (char)reader.Peek(); if (c == -1) { throw new GmlParseException(line, column); } if (c == '\r' || c == '\n') { line++; column = 1; } } reader.Read(); yield return new GmlToken(GmlTokenType.String, previousLine, previousColumn, sb.ToString()); break; default: throw new GmlParseException(line, column); } } } i = reader.Peek(); sb.Clear(); } } } public class GmlParseException : Exception { private readonly int _line; private readonly int _column; /// <summary> <see cref="T:System.Exception"/> クラスの新しいインスタンスを初期化します /// </summary> public GmlParseException(int line, int column) { _line = line; _column = column; } /// <summary> /// 指定したエラー メッセージで <see cref="T:System.Exception"/> クラスの新しいインスタンスを初期化します /// </summary> /// <param name="message">エラーを説明するメッセージ</param> /// <param name="line"></param> /// <param name="column"></param> public GmlParseException(string message, int line, int column) : base(message) { _line = line; _column = column; } /// <summary> /// 指定したエラー メッセージおよびこの例外の原因である内部例外への参照を含む <see cref="T:System.Exception"/> クラスの新しいインスタンスを初期化します /// </summary> /// <param name="message">例外の理由を説明するエラー メッセージ</param><param name="innerException">現在の例外の原因である例外。内部例外が指定されていない場合は null 参照 (Visual Basic で Nothing)</param> /// <param name="line"></param> /// <param name="column"></param> public GmlParseException(string message, Exception innerException, int line, int column) : base(message, innerException) { _line = line; _column = column; } public int Line { get { return _line; } } public int Column { get { return _column; } } } public enum GmlTokenType { Key, Int, Double, String, LBracket, RBracket } public class GmlToken { public GmlTokenType Type { get; private set; } public string Data { get; private set; } public int Line { get; private set; } public int Column { get; private set; } public GmlToken(GmlTokenType type, int line, int column, string data) { Type = type; Data = data; Line = line; Column = column; } public GmlToken(GmlTokenType type, int line, int column) : this(type, line, column, null) { } } }
Visual Basic の場合:
Imports System Imports System.Collections.Generic Imports System.IO Imports System.Linq Imports System.Text ' ' * このパーサーは、劣等一般公衆利用許諾書の下で、Universitat Passau によってリリースされた C コードに基づいています ' * http://www.fim.uni-passau.de/en/fim/faculty/chairs/theoretische-informatik/projects.html ' * ' * したがってこのクラスは、同じライセンスの下で配布されます ' Namespace xamNetworkNode_LoadingGmlData.Resources Public Class GmlNode Public Property Id() As Integer Get Return m_Id End Get Set m_Id = Value End Set End Property Private m_Id As Integer Public Property Label() As String Get Return m_Label End Get Set m_Label = Value End Set End Property Private m_Label As String Public Property Connections() As IList(Of GmlConnection) Get Return m_Connections End Get Set m_Connections = Value End Set End Property Private m_Connections As IList(Of GmlConnection) Public Sub New() Connections = New List(Of GmlConnection)() End Sub End Class Public Class GmlConnection Public Property Target() As GmlNode Get Return m_Target End Get Set m_Target = Value End Set End Property Private m_Target As GmlNode Public Property Label() As String Get Return m_Label End Get Set m_Label = Value End Set End Property Private m_Label As String End Class Public Class GmlParser Private _nodes As List(Of GmlNode) Public Function Parse(reader As TextReader) As IEnumerable(Of GmlNode) Dim scanner = New GmlScanner() Dim propertyKey As String = Nothing Dim connection As GmlConnection = Nothing Dim lastElement = 0 _nodes = New List(Of GmlNode)() For Each token As var In scanner.GetTokens(reader) Select Case token.Type Case GmlTokenType.Key Select Case token.Data Case "node" _nodes.Add(New GmlNode()) lastElement = 1 Exit Select Case "edge" connection = New GmlConnection() lastElement = 2 Exit Select Case "id", "label", "source", "target" If lastElement > 0 Then propertyKey = token.Data Else propertyKey = Nothing End If Exit Select Case Else lastElement = 0 Exit Select End Select Exit Select Case GmlTokenType.Int If propertyKey IsNot Nothing Then Select Case propertyKey Case "id" If lastElement = 1 Then _nodes(_nodes.Count - 1).Id = Convert.ToInt32(token.Data) End If Exit Select Case "source" Dim sourceId = Convert.ToInt32(token.Data) FindNodeById(sourceId).Connections.Add(connection) Exit Select Case "target" Dim targetId = Convert.ToInt32(token.Data) connection.Target = FindNodeById(targetId) Exit Select End Select End If Exit Select Case GmlTokenType.[String] If propertyKey = "label" Then If lastElement = 1 Then _nodes(_nodes.Count - 1).Label = token.Data ElseIf lastElement = 2 Then connection.Label = token.Data End If End If Exit Select End Select Next Return _nodes End Function Private Function FindNodeById(id As Integer) As GmlNode Return _nodes.First(Function(node) node.Id = id) End Function End Class Public Class GmlScanner Private Shared ReadOnly IsoTable As String() = {" ", "¡", "¢", "£", "¤", "¥", _ "¦", "§", "¨", "©", "ª", "«", _ "¬", "", "®", "¯", "°", "±", _ "²", "³", "´", "µ", "¶", "·", _ "¸", "¹", "º", "»", "¼", "½", _ "¾", "¿", "À", "Á", "Â", "Ã", _ "Ä", "Å", "Æ", "Ç", "È", "É", _ "Ê", "Ë", "Ì", "Í", "Î", "Ï", _ "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", _ "Ö", "×", "Ø", "Ù", "Ú", "Û", _ "Ü", "Ý", "Þ", "ß", "à", "á", _ "â", "ã", "ä", "å", "æ", "ç", _ "è", "é", "ê", "ë", "ì", "í", _ "î", "ï", "ð", "ñ", "ò", "ó", _ "ô", "õ", "ö", "÷", "ø", "ù", _ "ú", "û", "ü", "ý", "þ", "ÿ"} Private Shared Function DecodeIsoChar(str As String, len As Integer) As Integer Dim i As Integer Dim ret As Integer = "&"C If String.Compare(str, 0, """, 0, len) = 0 Then Return 34 End If If String.Compare(str, 0, "&", 0, len) = 0 Then Return 38 End If If String.Compare(str, 0, "<", 0, len) = 0 Then Return 60 End If If String.Compare(str, 0, ">", 0, len) = 0 Then Return 62 End If For i = 0 To 95 If String.Compare(str, 0, IsoTable(i), 0, len) = 0 Then ret = i + 160 Exit For End If Next Return ret End Function Public Function GetTokens(reader As TextReader) As IEnumerable(Of GmlToken) Dim line = 1 Dim column = 1 Dim sb = New StringBuilder() Dim i = reader.Peek() While i > -1 Dim c = ChrW(i) If Char.IsWhiteSpace(c) Then If c = ControlChars.Lf Then line += 1 column = 1 Else column += 1 End If reader.Read() Else Dim previousLine As Integer Dim previousColumn As Integer If Char.IsDigit(c) OrElse c = "."C OrElse c = "+"C OrElse c = "-"C Then Dim isDouble = False previousLine = line previousColumn = column Do If c = "."C Then If isDouble Then Throw New GmlParseException(line, column) End If isDouble = True End If sb.Append(c) column += 1 reader.Read() c = ChrW(reader.Peek()) Loop While Char.IsDigit(c) OrElse c = "."C If Not Char.IsWhiteSpace(c) Then Throw New GmlParseException(line, column) End If If c = ControlChars.Cr OrElse c = ControlChars.Lf Then line += 1 column = 1 End If If isDouble Then yield Return New GmlToken(GmlTokenType.[Double], previousLine, previousColumn, sb.ToString()) Else yield Return New GmlToken(GmlTokenType.Int, previousLine, previousColumn, sb.ToString()) End If ElseIf Char.IsLetter(c) OrElse c = "_"C Then previousLine = line previousColumn = column Do sb.Append(c) column += 1 reader.Read() c = ChrW(reader.Peek()) Loop While Char.IsLetterOrDigit(c) OrElse c = "_"C If c = ControlChars.Cr OrElse c = ControlChars.Lf Then line += 1 column = 1 End If If Not Char.IsWhiteSpace(c) Then Throw New GmlParseException(line, column) End If yield Return New GmlToken(GmlTokenType.Key, previousLine, previousColumn, sb.ToString()) Else previousLine = line previousColumn = column Select Case c Case "#"C Do reader.Read() c = ChrW(reader.Peek()) Loop While c <> ControlChars.Cr AndAlso c <> ControlChars.Lf AndAlso c <> -1 line += 1 column = 1 Exit Select Case "["C line += 1 reader.Read() yield Return New GmlToken(GmlTokenType.LBracket, previousLine, previousColumn) Exit Select Case "]"C line += 1 reader.Read() yield Return New GmlToken(GmlTokenType.RBracket, previousLine, previousColumn) Exit Select Case """"C column += 1 reader.Read() c = ChrW(reader.Peek()) While c <> """"C If c = "&"C Then Dim iso = New StringBuilder(8) While c <> ";"C If c = """"C OrElse c = -1 Then iso.Clear() Exit While End If If iso.Length < 8 Then iso.Append(c) End If reader.Read() c = ChrW(reader.Peek()) End While If iso.Length = 8 Then c = "&"C Else iso.Append(";") c = ChrW(DecodeIsoChar(iso.ToString(), iso.Length)) End If End If sb.Append(c) column += 1 reader.Read() c = ChrW(reader.Peek()) If c = -1 Then Throw New GmlParseException(line, column) End If If c = ControlChars.Cr OrElse c = ControlChars.Lf Then line += 1 column = 1 End If End While reader.Read() yield Return New GmlToken(GmlTokenType.[String], previousLine, previousColumn, sb.ToString()) Exit Select Case Else Throw New GmlParseException(line, column) End Select End If End If i = reader.Peek() sb.Clear() End While End Function End Class Public Class GmlParseException Inherits Exception Private ReadOnly _line As Integer Private ReadOnly _column As Integer ''' <summary> ''' <see cref="T:System.Exception"/> クラスの新しいインスタンスを初期化します ''' </summary> Public Sub New(line As Integer, column As Integer) _line = line _column = column End Sub ''' <summary> ''' 指定したエラー メッセージで <see cref="T:System.Exception"/> クラスの新しいインスタンスを初期化します ''' </summary> ''' <param name="message">エラーを説明するメッセージ</param> ''' <param name="line"></param> ''' <param name="column"></param> Public Sub New(message As String, line As Integer, column As Integer) MyBase.New(message) _line = line _column = column End Sub ''' <summary> ''' 指定したエラー メッセージおよびこの例外の原因である内部例外への参照を含む <see cref="T:System.Exception"/> クラスの新しいインスタンスを初期化します ''' </summary> ''' <param name="message">例外の理由を説明するエラー メッセージ</param><param name="innerException">現在の例外の原因である例外。内部例外が指定されていない場合は null 参照 (Visual Basic で Nothing)</param> ''' <param name="line"></param> ''' <param name="column"></param> Public Sub New(message As String, innerException As Exception, line As Integer, column As Integer) MyBase.New(message, innerException) _line = line _column = column End Sub Public ReadOnly Property Line() As Integer Get Return _line End Get End Property Public ReadOnly Property Column() As Integer Get Return _column End Get End Property End Class Public Enum GmlTokenType Key Int [Double] [String] LBracket RBracket End Enum Public Class GmlToken Public Property Type() As GmlTokenType Get Return m_Type End Get Private Set m_Type = Value End Set End Property Private m_Type As GmlTokenType Public Property Data() As String Get Return m_Data End Get Private Set m_Data = Value End Set End Property Private m_Data As String Public Property Line() As Integer Get Return m_Line End Get Private Set m_Line = Value End Set End Property Private m_Line As Integer Public Property Column() As Integer Get Return m_Column End Get Private Set m_Column = Value End Set End Property Private m_Column As Integer Public Sub New(type__1 As GmlTokenType, line__2 As Integer, column__3 As Integer, data__4 As String) Type = type__1 Data = data__4 Line = line__2 Column = column__3 End Sub Public Sub New(type As GmlTokenType, line As Integer, column As Integer) Me.New(type, line, column, Nothing) End Sub End Class End Namespace