This section will guide you through creating a simple application that allows you to access RDF data in a Virtuoso database as an Entity DataSet and explore that RDF data in an intuitive way by clicking on dereferenceable IRIs.
Step 1 - Create a view of the RDF data.
We want to be able to access the RDF data in Visual Studio and the easiest way to do this is to create a view of the data that we are interested in and bind that view to a DataSet. This can be considered as using server side SPARQL. Virtuoso supports an extension to standard SQL that allows execution of SPARQL. If a SQL query begins with the keyword SPARQL then the rest of the query is interpreted by as SPARQL. If a SPARQL query is used as the definition of a view then that view can be manipulated using SQL like any other view. In this way the result set from a SPARQL query can be easily accessed from Visual Studio using ADO.Net and the Entity Framework.
To create a view of the customers in the Northwind first open the Virtuoso Conductor and log in as dba. Then open iSQL from the menu on the left and execute the following statement.
CREATE VIEW Demo.demo.sparqlview as SPARQL PREFIX nwind: <http://demo.openlinksw.com/schemas/northwind#> SELECT DISTINCT ?s FROM <http://demo.openlinksw.com/Northwind> WHERE {?s a nwind:Customer}
Note: If the view is added to the Visual Studio project as user "demo" (or any other than "dba'), then it must be ensured that the "SPARQL_SELECT" and "SPARQL_SPONGE" roles are assigned to this user, which can be done via the Virtuoso Conductor in the "System Admin" -> "User Accounts" tab.
![]() |
Figure: 2.9.2.1. SPARQL_SPONGE |
Step 2 - Create a simple grid form in Visual Studio
![]() |
Figure: 2.9.2.1. Data Source |
![]() |
Figure: 2.9.2.1. Data Source Configuration Wizard |
![]() |
Figure: 2.9.2.1. CellStyleBuilder |
Step 3 - Change the mapping of the DataSet.
In the Solution Explorer you will now have a DataSet called DemoDataSet.xsd. If you double click on this it opens the DataSet Designer. Select the column called s in the sparqlview table and in the Properties pane change the DataType from System.String to System.Object.
The data returned by a SPARQL query can either be an IRI or a literal value. In order to distinguish between the two the Virtuoso ADO.Net provider defines an additional data type, SQLExtendedString. By setting the column type to System.Object we can cast the fetched data back to SQLExtendedString and find out if an individual value is an IRI or a literal and handle it appropriately.
Step 4 - Create the on_click event handler for the cells in the DataGridView.
Return to the Form Designer and double click on the cell of the DataGridView. This creates the dataGridView1_CellContentClick method in Form1.cs. This is the method that handles clicking on IRI objects in the grid.
Paste in the following block of code into the body of the dataGridView1_CellContentClick method.
int column = e.ColumnIndex; object o = dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value; Type t = dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].ValueType; if (o is SqlExtendedString) { SqlExtendedString se = (SqlExtendedString) o; ExtendedStringHandler seHandler = new ExtendedStringHandler(se, this.sparqlviewTableAdapter.Connection); seHandler.displayData(); } else if (o is SqlRdfBox) { //doesn't do anything at the moment }
As we are using the SQLExtendedString extension from the Virtuoso ADO.Net provider you will also need to add
using OpenLink.Data.Virtuoso;
at the top of the file.
Step 5 - Create a class to handle exploring the RDF data.
using OpenLink.Data.Virtuoso; using System.Data; using System.Windows.Forms; using System.Drawing; using System.Data.Mapping; using System.Data.Common;
StringBuilder DescribeCommand; VirtuosoConnection ParentConnection; List<Label> labelList = new List<Label>(); List<TextBox> textBoxList = new List<TextBox>(); DescribeDataSet describeDataSet = new DescribeDataSet(); Boolean isIRI = false; public ExtendedStringHandler(SqlExtendedString iri, VirtuosoConnection parentConnection) { ParentConnection = parentConnection; if (iri.IriType == SqlExtendedStringType.IRI) { isIRI = true; DescribeCommand = new StringBuilder("sparql select * from <http://demo.openlinksw.com/Northwind> where {<" + iri.ToString() + "> ?p ?o}"); // Replace demo.openlinksw.com with your URIQA DefaultHost setting } } public string describeCommandText { get { return DescribeCommand.ToString(); } } public void getDescribeData() { VirtuosoCommand myCommand = new VirtuosoCommand(this.describeCommandText, this.ParentConnection); VirtuosoDataAdapter myAdapter = new VirtuosoDataAdapter(); myAdapter.SelectCommand = myCommand; myAdapter.Fill(describeDataSet.DataTable1); } public void displayData() { if (isIRI) { getDescribeData(); Form describeForm = new Form(); describeForm.AutoScroll = true; describeForm.Width = 840; Label label1 = new Label(); label1.AutoSize = true; label1.Font = new Font(label1.Font.FontFamily, label1.Font.Size + 3.0F, label1.Font.Style | FontStyle.Bold, label1.Font.Unit); describeForm.Controls.Add(label1); DataTable table1 = describeDataSet.Tables[0]; if (table1.Rows.Count == 0) label1.Text = "No Details Available"; else { foreach (DataRow row in table1.Rows) if (row[0].ToString() == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type") { StringBuilder title = new StringBuilder(row[1].ToString() + " details"); label1.Text = title.ToString(); break; } foreach (DataRow row in table1.Rows) { Label propertyLabel = new Label(); TextBox valueBox = new TextBox(); valueBox.Width = 400; object property = row[0]; object value = row[1]; if (value is SqlExtendedString) { valueBox.ForeColor = Color.Blue; valueBox.Font = new Font(valueBox.Font.FontFamily, valueBox.Font.Size, valueBox.Font.Style | FontStyle.Underline, valueBox.Font.Unit); } propertyLabel.Text = row[0].ToString(); propertyLabel.AutoEllipsis = true; propertyLabel.AutoSize = false; propertyLabel.Width = propertyLabel.PreferredWidth > 380 ? 380 : propertyLabel.PreferredWidth; Binding bind = new Binding("Text", row[1], ""); valueBox.DataBindings.Add(bind); labelList.Add(propertyLabel); textBoxList.Add(valueBox); } for (int i = 0; i < table1.Rows.Count; i++) { textBoxList[i].Click += new EventHandler(this.iri_Click); labelList[i].Location = new Point(10, i * 20 + 50); textBoxList[i].Location = new Point(400, i * 20 + 50); describeForm.Controls.Add(labelList[i]); describeForm.Controls.Add(textBoxList[i]); } describeForm.Height = labelList.Count * 20 + 100 > 500 ? 500 : labelList.Count * 20 + 100; } describeForm.ShowDialog(); } else { Form blankForm = new Form(); Label label1 = new Label(); label1.Text = "Blank Node"; label1.Font = new Font(label1.Font.FontFamily, label1.Font.Size + 3.0F, label1.Font.Style | FontStyle.Bold, label1.Font.Unit); blankForm.ShowDialog(); } } public void iri_Click(object sender, EventArgs e) { int boxNum = 0; for (int i = 0; i < textBoxList.Count; i++) { if (sender == textBoxList[i]) { boxNum = i; break; } } Object o = describeDataSet.DataTable1.Rows[boxNum][1]; if (o is SqlExtendedString) { SqlExtendedString se = (SqlExtendedString)o; ExtendedStringHandler seHandler = new ExtendedStringHandler(se, ParentConnection); seHandler.displayData(); } else if (o is SqlRdfBox) { //doesn't do anything at the moment } }
The ExtendedStringHandler class creates a new SPARQL query based on the IRI that was clicked. This query is executed against Virtuoso using the ADO.Net connection in the same way that any SQL statement would be executed across an ADO.Net connection. This can be considered as Client Side SPARQL. The result set from the query describes the selected object and is returned as an ADO.Net DataAdapter. The DataAdapter is used to fill a DataTable which is displayed on a new form. We now need to add the new DataSet to the project and define the DataTable that will hold the query results.
Step 6 - Add a new DataSet to hold the query results.
![]() |
Figure: 2.9.2.1. Add a new DataSet |
![]() |
Figure: 2.9.2.1. Add two columns |
Step 7 - Build and run the application.
You should see a form displaying all the Northwind customers, like this.
![]() |
Figure: 2.9.2.2. Northwind customers |
When any customer is clicked it opens a new form showing customer details.
![]() |
Figure: 2.9.2.3. Customer details |
Clicking on the links in the new form allows you to drill down further to get order, product, location details etc.
![]() |
Figure: 2.9.2.4. Order, product, location details |
and
![]() |
Figure: 2.9.2.5. Order, product, location details |
Next Steps
You will notice if you keep clicking on the links that this application will only display data that is held in the Northwind graph. Clicking on an external link, for example the link to Berlin in dbpedia, http://dbpedia.org/resource/Berlin, results in a empty window and an error message. The next step is to extend this application so that it can handle dereferencing external IRIs.
This section will guide you through extending the application created in Creating a Windows Forms Application To Access RDF Data Using The Virtuoso ADO.Net Provider so that it will dereference external IRIs.
Pre-requisites
In RDFDemo when the sparql endpoint is queried to get the description of the selected item it executes a query that is restricted to the local Northwind dataset. The query is something like
SPARQL PREFIX nwind: <http://demo.openlinksw.com/schemas/northwind#> SELECT DISTINCT ?s FROM <http://demo.openlinksw.com/Northwind> WHERE {?s a nwind:Customer}
If you examine the ExtendedStringHandler class you will see that the dataset clause, from <http://localhost:8890/Northwind>, is hard coded. This means that when when the selected IRI is a link to an external data store, such as dbpedia, there is no matching data and an error is displayed. If the application is to be able to dereference external IRIs then the hard coded dataset clause needs to be removed and then we can use a Virtuoso extension to SPARQL, get:soft, that tells Virtuoso that it needs to go and look elsewhere for the graph. However, this will result in a loss of performance when exploring the local Northwind dataset. To minimize the impact on performance we will first query the local Northwind dataset and if there are no matching triples returned then we will use a more generic query that will look elsewhere for matching data.
Step 1 - Add the alternative query to the ExtendedString Class.
StringBuilder DescribeCommand;
and substitute the following:
StringBuilder DescribeCommandSimple, DescribeCommandGeneral;
DescribeCommandSimple = new StringBuilder("sparql select * from <http://demo.openlinksw.com/Northwind> where {<" + iri.ToString() + "> ?p ?o}"); // Replace demo.openlinksw.com with your URIQA DefaultHost setting DescribeCommandGeneral = new StringBuilder("sparql define get:soft " + '"'.ToString() + "soft" + '"'.ToString() + " select * from <" + iri.ToString() + "> where { <" + iri.ToString() + "> ?p ?o }");
public string describeCommandSimpleText { get { return DescribeCommandSimple.ToString(); } } public string describeCommandGeneralText { get { return DescribeCommandGeneral.ToString(); } }
public void getDescribeData() { VirtuosoCommand myCommand = new VirtuosoCommand(this.describeCommandSimpleText, this.ParentConnection); VirtuosoDataAdapter myAdapter = new VirtuosoDataAdapter(); myAdapter.SelectCommand = myCommand; myAdapter.Fill(describeDataSet.DataTable1); // Tried the simple version if fails to get the data try // to look elsewhere. if (describeDataSet.DataTable1.Rows.Count == 0) { myCommand.CommandText = describeCommandGeneralText; myAdapter.Fill(describeDataSet.DataTable1); } }
Step 2 - Build and Run the Application
You will see the same starting form:
![]() |
Figure: 2.9.3.1.1. Build and Run the Application |
Select a Customer and then select the link to the City in dbpedia. This will now open up another window displaying information about the city from dbpedia. Be patient as it may take a little while to open.
![]() |
Figure: 2.9.3.1.2. Customer |
Step 3 - Changing the Form Title
Notice that in displayData method that we look for a http://www.w3.org/1999/02/22-rdf-syntax-ns#type and create a title for the form from it.
foreach (DataRow row in table1.Rows) if (row[0].ToString() == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type") { StringBuilder title = new StringBuilder(row[1].ToString() + " details"); label1.Text = title.ToString(); break; }
This worked well for the Northwind subjects but less well now we are getting data from other graphs. To change the title of the forms used to display the data:
StringBuilder DescribeCommandSimple, DescribeCommandGeneral; VirtuosoConnection ParentConnection; List<Label> labelList = new List<Label>(); List<TextBox> textBoxList = new List<TextBox>(); DescribeDataSet describeDataSet = new DescribeDataSet(); Boolean isIRI = false; SqlExtendedString ParentIRI;
public ExtendedStringHandler(SqlExtendedString iri, VirtuosoConnection parentConnection) { ParentConnection = parentConnection; if (iri.IriType == SqlExtendedStringType.IRI) { ParentIRI = iri; isIRI = true; DescribeCommandSimple = new StringBuilder("sparql select * from <http://demo.openlinksw.com/Northwind> where {<" + iri.ToString() + "> ?p ?o}"); // Replace demo.openlinksw.com with your URIQA DefaultHost setting DescribeCommandGeneral = new StringBuilder("sparql define get:soft " + '"'.ToString() + "soft" + '"'.ToString() + " select * from <" + iri.ToString() + "> where { <" + iri.ToString() + "> ?p ?o }"); } }
StringBuilder title = new StringBuilder(ParentIRI.ToString() + " details"); label1.Text = title.ToString();
![]() |
Figure: 2.9.3.1.1. Build and run the application |
Next Steps
The application now allows you to explore data and follow links from your locally held data into the external web of data. Looking at the data displayed in the form it would be nice to make the labels for the properties more compact. The label http://dbpedia.org/property/population is a very precise definition but for our purposes it would be clearer to label the property just population. In the next step will be to modify the application so that it displays more readable labels.
This section will guide you through extending the application created in Extending RDFDemo to Allow Dereferencing of External IRIs so that the data is displayed in a more readable form.
Pre-requisites
The RDF demo application presents the user with a list of Customers from the Northwind database in the form of dereferenceable IRIs. When a customer is selected from the list the application uses a sparql query to describe that customer and the results are displayed in a form as rows of labels and data. The labels correspond to RDF predicates and the data corresponds to RDF objects while the subject is the customer initially selected. In many cases the objects are dereferencable IRIs which are then used as the subject when the 'drilling down' into the data. However, the predicates are also IRIs so it is possible to gain more information about these as well.
The RDF Schema defines a property http://www.w3.org/2000/01/rdf-schema#label that may be used to provide a human-readable version of a resource's name. We can obtain further details of each of the predicates in a resultset and check to see if one of the properties is an http://www.w3.org/2000/01/rdf-schema#label. If it is we can use the associated text as the label in our form instead of the the predicate IRI. The benefit should be a more human readable form.
Step 1 - Add a New Method to Get the Label Text
This method takes the predicate IRI and issues a sparql query to get its description. It then cycles through the returned dataset to find a http://www.w3.org/2000/01/rdf-schema#label. If there is one then the associated text is returned by the method. Otherwise the method returns the IRI string.
private string getLabelText(Object label) { string labelText = label.ToString(); if (label is SqlExtendedString) { SqlExtendedString se = (SqlExtendedString)label; StringBuilder getLabelCommandText = new StringBuilder("sparql define get:soft \"soft\" select * from <" + se.ToString() + "> where {<" + se.ToString() + "> ?p ?o}"); VirtuosoCommand getLabelCommand = new VirtuosoCommand(getLabelCommandText.ToString(), ParentConnection); VirtuosoDataAdapter getLabelAdapter = new VirtuosoDataAdapter(); getLabelAdapter.SelectCommand = getLabelCommand; DataSet getLabelDataSet = new DataSet(); try { getLabelAdapter.Fill(getLabelDataSet); foreach (DataRow getLabelRow in getLabelDataSet.Tables[0].Rows) { if (getLabelRow[0].ToString() == "http://www.w3.org/2000/01/rdf-schema#label") { labelText = getLabelRow[1].ToString(); break; } } } catch { } } return labelText; }
propertyLabel.Text = row[0].ToString();
propertyLabel.Text = getLabelText(row[0]);
Step 1 - Add a New Method to Get the Label Text
When you run the application you will see that the initial form is the same. In fact, when you select the Customer you will also see that the customer details are also the same. It is only when you start exploring data outside the Northwind graph that you will see the labels in the form change.
![]() |
Figure: 2.9.4.1.1. Northwind graph |
Next Steps
It is clear from running the application that the Northwind ontology does not define an http://www.w3.org/2000/01/rdf-schema#label for its members. In order to benefit from this modified version of RDFDemo we need to update our Northwind ontology so that http://www.w3.org/2000/01/rdf-schema#label is defined for each resource. The next step will be to modify our Northwind ontology.
This section will guide you through modifying the Northwind Ontology created when you installed the demo database VAD package so that each resources is identified by an http://www.w3.org/2000/01/rdf-schema#label. This will improve the readability of the information displayed by the application created in Extending RDFDemo to Display More Compact Labels.
Pre-requisites
Get a Working Copy of the Northwind Ontology
The the file describing the Northwind Ontology, nw.owl, is installed in the DAV when the demo vad is loaded. To get a working copy open the Virtuoso Conductor and log in as dba. Select WebDAV Browser in the navigation panel on the left. This will open a window that allows you to browse the WebDAV Repository. The Northwind Ontology file can be found in DAV/VAD/demo/sql. Take a copy of the file.
Editing nw.owl
In the first instance is edited nw.owl so that the property name consistently begin with a lower case letter. This matches the results for describing resources held in the Northwind database. Also are added missing properties so that there should be a label in all cases.
Registering the Changes in Virtuoso
There are two methods for registering the changes in Virtuoso:
Modify RDFDemo so that it looks for the graph used to describe the Northwind data and searches that graph for the predicate details:
StringBuilder DescribeCommandSimple, DescribeCommandGeneral; VirtuosoConnection ParentConnection; List<Label> labelList = new List<Label>(); List<TextBox> textBoxList = new List<TextBox>(); List<String> graphList = new List<String>(); DescribeDataSet describeDataSet = new DescribeDataSet(); Boolean isIRI = false; SqlExtendedString ParentIRI;
// Later we will want to get property labels and for that // we will need the graph where the resource is defined. foreach (DataRow row in table1.Rows) if (row[0].ToString() == "http://www.openarchives.org/ore/terms/isDescribedBy" && row[1].ToString() != ParentIRI.ToString()) { String graph = row[1].ToString(); graphList.Add(graph); }
private string getLabelText(Object label) { string labelText = label.ToString(); if (label is SqlExtendedString) { Boolean foundLabel = false; SqlExtendedString se = (SqlExtendedString)label; VirtuosoDataAdapter getLabelAdapter = new VirtuosoDataAdapter(); DataSet getLabelDataSet = new DataSet(); //Try finding it in resources graph first foreach (String graph in graphList) { StringBuilder getLabelCommandText = new StringBuilder("sparql select * from <" + graph + "> where {<" + se.ToString() + "> ?p ?o}"); VirtuosoCommand getLabelCommand = new VirtuosoCommand(getLabelCommandText.ToString(), ParentConnection); getLabelAdapter.SelectCommand = getLabelCommand; try { getLabelAdapter.Fill(getLabelDataSet); foreach (DataRow getLabelRow in getLabelDataSet.Tables[0].Rows) { if (getLabelRow[0].ToString() == "http://www.w3.org/2000/01/rdf-schema#label") { labelText = getLabelRow[1].ToString(); foundLabel = true; break; } } } catch { } if (foundLabel) break; } // If we still have no label try the predicate itself as the graph if (!foundLabel) { StringBuilder getLabelCommandText = new StringBuilder("sparql define get:soft \"soft\" select * from <" + se.ToString() + "> where {<" + se.ToString() + "> ?p ?o}"); VirtuosoCommand getLabelCommand = new VirtuosoCommand(getLabelCommandText.ToString(), ParentConnection); getLabelAdapter.SelectCommand = getLabelCommand; try { getLabelAdapter.Fill(getLabelDataSet); foreach (DataRow getLabelRow in getLabelDataSet.Tables[0].Rows) { if (getLabelRow[0].ToString() == "http://www.w3.org/2000/01/rdf-schema#label") { labelText = getLabelRow[1].ToString(); break; } } } catch { } } } return labelText; }
This extended method first checks the graphs in the graph list for the predicate information then if no label has been found tries the predicate itself as the graph.
![]() |
Figure: 2.9.5.1.1. Northwind resources |
Improving The Appearance of the Form
The following changes are not strictly necessary but improve the appearance of the form:
propertyLabel.Text = getLabelText(row[0]); propertyLabel.AutoEllipsis = true; propertyLabel.AutoSize = false; propertyLabel.Width = 130; propertyLabel.TextAlign = ContentAlignment.MiddleRight;
describeForm.Width = 660;
textBoxList[i].Click += new EventHandler(this.iri_Click); labelList[i].Location = new Point(10, i * 22 + 50); textBoxList[i].Location = new Point(150, i * 22 + 50); describeForm.Controls.Add(labelList[i]); describeForm.Controls.Add(textBoxList[i]);
![]() |
Figure: 2.9.5.1.1. Alter the positioning |
Next Steps
The image below shows some of the information about an employee in the Northwind dataset.
![]() |
Figure: 2.9.5.1.1. employee |
In the next step we will extend the application so the images and web pages can be viewed and long text fields are displayed in full.
This section will guide you through extending RDFDemo so that longer text fields can be displayed as a block of text and so that links to images and web pages can be viewed in a browser window.
Pre-requisites
Displaying Text
We will modify the form that show the details of the selected item so that when the text in the boxes is too long to be seen in full a button will appear beside the box on the form and if you click the button the complete text will be displayed in a separate window.
![]() |
Figure: 2.9.6.1.1. Add a new class |
using System.Windows.Forms;
class MoreButton : Button
String longText; public MoreButton(String text) { longText = text; this.Text = "More"; } protected override void OnClick(EventArgs e) { Form moreForm = new Form(); TextBox moreBox = new TextBox(); moreBox.Text = longText; moreBox.Width = 300; moreBox.Height = 250; moreBox.ScrollBars = ScrollBars.Vertical; moreBox.Multiline = true; moreBox.WordWrap = true; moreBox.Select(0, 0); moreBox.ReadOnly = true; moreForm.Controls.Add(moreBox); moreForm.Width = 320; moreForm.Height = 280; moreForm.ShowDialog(); }
if (textBoxList[i].DataBindings[0].DataSource.ToString().Length > 80 && !(textBoxList[i].DataBindings[0].DataSource is SqlExtendedString)) { moreButtonList.Add(new MoreButton(textBoxList[i].DataBindings[0].DataSource.ToString())); moreButtonList[moreButtonList.Count - 1].Location = new Point(550, i * 22 + 50); describeForm.Controls.Add(moreButtonList[moreButtonList.Count -1]); }
List<MoreButton> moreButtonList = new List<MoreButton>();
![]() |
Figure: 2.9.6.1.1. Notes |
Displaying Images and Web Pages
Next we will modify the form so that item identified as images or web pages will be opened in a browser window. Again we will do this by adding a button beside the box on the form that will open the browser window.
using System.Windows.Forms;
class OpenButton : Button
String urlText; public OpenButton(String text) { urlText = text; this.Text = "Open"; } protected override void OnClick(EventArgs e) { System.Diagnostics.Process.Start(urlText); }
if (labelList[i].Text == "website" || labelList[i].Text == "image" || labelList[i].Text == "depiction" || labelList[i].Text == "page" || labelList[i].Text == "url" || labelList[i].Text == "image skyline") { openButtonList.Add(new OpenButton(textBoxList[i].DataBindings[0].DataSource.ToString())); openButtonList[openButtonList.Count - 1].Location = new Point (550, i * 22 + 50); describeForm.Controls.Add(openButtonList[openButtonList.Count - 1]); }
List<OpenButton> openButtonList = new List<OpenButton>();
![]() |
Figure: 2.9.6.1.1. Image |
Next Steps
It has already been mentioned that the property labels are also dereferenceable IRIs. We used this feature to find a short name to display rather that the complete IRI. The next step is to make the labels clickable so the ontology itself can also be explored.
This section will guide you through extending RDFDemo to make the property labels clickable. Clicking on the property label will take you to a page describing that property.
Pre-requisites
Making the Labels Clickable
Making the property labels clickable is fairly straight forward. We will use basically the same code as is used to make the values clickable.
for (int i = 0; i < table1.Rows.Count; i++) { textBoxList[i].Click += new EventHandler(this.iri_Click); labelList[i].Location = new Point(10, i * 22 + 50); textBoxList[i].Location = new Point(150, i * 22 + 50); describeForm.Controls.Add(labelList[i]); describeForm.Controls.Add(textBoxList[i]);
becomes
for (int i = 0; i < table1.Rows.Count; i++) { textBoxList[i].Click += new EventHandler(this.iri_Click); labelList[i].Location = new Point(10, i * 22 + 50); labelList[i].Click += new EventHandler(this.label_Click); textBoxList[i].Location = new Point(150, i * 22 + 50); describeForm.Controls.Add(labelList[i]); describeForm.Controls.Add(textBoxList[i]);
public void label_Click(object sender, EventArgs e) { int labelNum = 0; for (int i = 0; i < labelList.Count; i++) { if (sender == labelList[i]) { labelNum = i; break; } } Object o = describeDataSet.DataTable1.Rows[labelNum][0]; if (o is SqlExtendedString) { SqlExtendedString se = (SqlExtendedString)o; ExtendedStringHandler seHandler = new ExtendedStringHandler(se, ParentConnection); seHandler.displayData(); } else if (o is SqlRdfBox) { //doesn't do anything at the moment } }
If you compare this method to the EventHandler for the values, iri_Click, you will see that it is basically the same. The only difference is that it uses the property element from the data table rather than value.
propertyLabel.Text = getLabelText(row[0]); propertyLabel.AutoEllipsis = true; propertyLabel.AutoSize = false; propertyLabel.Width = 130; propertyLabel.TextAlign = ContentAlignment.MiddleRight;
becomes:
propertyLabel.Text = getLabelText(row[0]); propertyLabel.ForeColor = Color.Blue; propertyLabel.Font = new Font(propertyLabel.Font.FontFamily, propertyLabel.Font.Size, propertyLabel.Font.Style | FontStyle.Underline, propertyLabel.Font.Unit); propertyLabel.AutoEllipsis = true; propertyLabel.AutoSize = false; propertyLabel.Width = 130; propertyLabel.TextAlign = ContentAlignment.MiddleRight;
![]() |
Figure: 2.9.7.1.1. hyperlinks |
If you click on one of the label hyperlinks you will see a new form showing detailed information about the property which can itself be explored further by clicking on labels and values.
![]() |
Figure: 2.9.7.1.1. labels and values |
These simple changes work up to a point but are not robust. If you explore the properties used by the local Northwind graph you quickly find that the property details are not found. The application needs some further changes to work consistently. The problem is finding the graph where the property information is defined. The general handler for Extended Strings first checks the local Northwind graph, http://localhost:8890/Northwind, where the Northwind data is held, and then uses the IRI itself as the graph and tries to load that dynamically. This is not working for information about the Northwind properties. These properties are defined in the Northwind ontology, http://demo.openlinksw.com/schemas/northwind. We have already had to find this graph when getting the short label name. We need to be able to associate the property label with the graph where its definition is stored. Then we can search this graph for details about the property when the label is clicked.
Using the Graph Where the Property Label was Found to Find the Property Details
![]() |
Figure: 2.9.7.1.1. Add a new class |
using System.Windows.Forms; using OpenLink.Data.Virtuoso; using System.Data;
to the using block at the top of the class file. The class definition should look like this:
class IRILabel : Label
SqlExtendedString SourceIRI; String GraphUsed; VirtuosoConnection ParentConnection; public IRILabel(Object iri, List<String> graphList, VirtuosoConnection parentConnection) { ParentConnection = parentConnection; if (iri is SqlExtendedString) { SourceIRI = (SqlExtendedString)iri; Text = this.getLabelText(graphList); } else Text = iri.ToString(); } public SqlExtendedString iri { get { return SourceIRI; } } public String graph { get { return GraphUsed; } } private string getLabelText(List<String> graphList) { string labelText = SourceIRI.ToString(); Boolean foundLabel = false; VirtuosoDataAdapter getLabelAdapter = new VirtuosoDataAdapter(); DataSet getLabelDataSet = new DataSet(); //Try finding it in resources graph first foreach (String graph in graphList) { StringBuilder getLabelCommandText = new StringBuilder("sparql select * from <" + graph + "> where {<" + SourceIRI.ToString() + "> ?p ?o}"); VirtuosoCommand getLabelCommand = new VirtuosoCommand(getLabelCommandText.ToString(), ParentConnection); getLabelAdapter.SelectCommand = getLabelCommand; try { getLabelAdapter.Fill(getLabelDataSet); foreach (DataRow getLabelRow in getLabelDataSet.Tables[0].Rows) { if (getLabelRow[0].ToString() == "http://www.w3.org/2000/01/rdf-schema#label") { labelText = getLabelRow[1].ToString(); foundLabel = true; break; } } } catch { } if (foundLabel) { GraphUsed = graph; break; } } // If we still have no label try the predicate itself as the graph if (!foundLabel) { GraphUsed = SourceIRI.ToString(); StringBuilder getLabelCommandText = new StringBuilder("sparql define get:soft \"soft\" select * from <" + GraphUsed + "> where {<" + SourceIRI.ToString() + "> ?p ?o}"); VirtuosoCommand getLabelCommand = new VirtuosoCommand(getLabelCommandText.ToString(), ParentConnection); getLabelAdapter.SelectCommand = getLabelCommand; try { getLabelAdapter.Fill(getLabelDataSet); foreach (DataRow getLabelRow in getLabelDataSet.Tables[0].Rows) { if (getLabelRow[0].ToString() == "http://www.w3.org/2000/01/rdf-schema#label") { labelText = getLabelRow[1].ToString(); break; } } } catch { } } return labelText; }
Notice that the getLabelText method has been moved into this new class and is now called from the constructor. When the IRILabel is constructed the label text is found using the list of graphs provided to the constructor. The graph containing the label is noted. We need to alter ExtendedStringHandler so that the labels are the new IRILabel type and so that the correct information is supplied to the constructor.
List<IRILabel> labelList = new List<IRILabel>();
Label propertyLabel = new Label();
becomes:
IRILabel propertyLabel = new IRILabel(row[0], graphList, ParentConnection);
propertyLabel.Text = getLabelText(row[0]);
from describeData in ExtendedStringHandler.
public void label_Click(object sender, EventArgs e) { int labelNum = 0; for (int i = 0; i < labelList.Count; i++) { if (sender == labelList[i]) { labelNum = i; break; } } SqlExtendedString se = labelList[labelNum].iri; ExtendedStringHandler seHandler = new ExtendedStringHandler(se, ParentConnection, labelList[labelNum].graph); seHandler.displayData(); }
public ExtendedStringHandler(SqlExtendedString iri, VirtuosoConnection parentConnection, String graph) { ParentConnection = parentConnection; if (iri.IriType == SqlExtendedStringType.IRI) { ParentIRI = iri; isIRI = true; DescribeCommandSimple = new StringBuilder("sparql select * from <http://localhost:8890/Northwind> where {<" + iri.ToString() + "> ?p ?o}"); DescribeCommandGeneral = new StringBuilder("sparql define get:soft " + '"'.ToString() + "soft" + '"'.ToString() + " select * from <" + graph + "> where { <" + iri.ToString() + "> ?p ?o }"); } }
This new constructor uses the supplied graph to build the alternative sparql select statement that looks for the details about the supplied IRI. With these changes in place the application will find the description of the Northwind properties.
![]() |
Figure: 2.9.7.1.1. Northwind dataset |
Previous
Using Visual Studio 2008 to Build an ADO.NET Data Services based Application |
Chapter Contents |
Next
Creating a Web Browser Application to Access RDF Data Using The Virtuoso ADO.Net Provider |