in tools/Graph Explorer/GraphExplorer/ViewModels/EditorViewModel.cs [517:705]
public EditorViewModel(MainWindow v, Models.Model model)
{
this.view = v;
this.model = model;
// Initialize the mapping from names onto renderers.
this.NodeRenderers = new Dictionary<string, INodeRenderer>();
this.EdgeRenderers = new Dictionary<string, IEdgeRenderer>();
// Load the list of renderers through MEF.
var catalog = new AggregateCatalog();
// Put in the directory where the table extractor lives into the catalog
// Use the directory where the main executable is found
var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
catalog.Catalogs.Add(new DirectoryCatalog(assemblyPath));
CompositionContainer container = new CompositionContainer(catalog);
try
{
// Load all the plugins in the catalog, i.e. in the directory provided.
container.SatisfyImportsOnce(this);
// at this point the extractor should have been filled in. The metadata is
// also available.
foreach (var plugin in this.NodePlugins)
{
this.NodeRenderers[plugin.Metadata.Label] = plugin.Value.CreateRenderer(model);
}
foreach (var plugin in this.EdgePlugins)
{
this.EdgeRenderers[plugin.Metadata.Label] = plugin.Value.CreateRenderer(model);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
NodeSelected += UpdateNodeInfoPage;
EdgeSelected += UpdateEdgeInfoPage;
this.SetTheme(this.model.IsDarkMode);
// Handler for events in this view model
this.PropertyChanged += (object sender, PropertyChangedEventArgs e) =>
{
};
// Handler for events bubbling up from the model.
model.PropertyChanged += async (object sender, PropertyChangedEventArgs e) =>
{
if (e.PropertyName == nameof(Model.QueryFontSize))
{
QueryEditorFontSize = this.model.QueryFontSize;
this.OnPropertyChanged(nameof(QueryEditorFontSize));
}
else if (e.PropertyName == nameof(Model.ErrorMessage))
{
this.ErrorMessage = this.model.ErrorMessage;
this.OnPropertyChanged(nameof(ErrorMessage));
}
else if (e.PropertyName == nameof(Model.CaretPositionString))
{
this.CaretPositionString = this.model.CaretPositionString;
}
else if (e.PropertyName == nameof(Model.EditorPosition))
{
var p = this.model.EditorPosition;
this.view.CypherEditor.TextArea.Caret.Position = new ICSharpCode.AvalonEdit.TextViewPosition(p.Item1, p.Item2);
this.view.CypherEditor.TextArea.Caret.BringCaretToView();
}
else if (e.PropertyName == nameof(Model.IsDarkMode))
{
this.SetTheme(this.model.IsDarkMode);
// Update the view
await this.RepaintNodesAsync(this.model.NodesShown);
}
else if (e.PropertyName == nameof(Model.StyleDocumentSource))
{
// Store the file along with the script
var uri = this.model.ScriptUri;
var fileName = uri.LocalPath;
var directory = Path.GetDirectoryName(fileName);
var configFileName = Path.Combine(directory, "Config.js");
File.WriteAllText(configFileName, model.StyleDocumentSource);
// The config file was changed, but the browser will keep a cached copy. To avoid
// this, the script is updated to reflect a new file name.
var script = File.ReadAllText(fileName);
// The script contains a reference to the config file:
// <script type="text/javascript" src="Config.js?ts=1234567890"></script>
// The digit sequence needs to be changed so that the browser does not
// use a cached version of the config file.
var idx = script.IndexOf("Config.js?ts=") + "Config.js?ts=".Length;
var cnt = 0;
while (char.IsDigit(script[idx + cnt]))
cnt++;
script = script.Remove(idx, cnt);
script = script.Insert(idx, DateTime.Now.Ticks.ToString());
File.WriteAllText(fileName, script);
this.view.Browser.NavigationCompleted += Browser_NavigationCompleted;
this.view.Browser.Reload();
}
else if (e.PropertyName == nameof(Model.NodesShown))
{
// The nodes have been changed, so do a repaint
await this.RepaintNodesAsync(this.model.NodesShown);
}
else if (e.PropertyName == nameof(Model.QueryResults))
{
if (this.GraphModeSelected)
{
this.model.NodesShown = Model.HarvestNodeIdsFromGraph(this.model.QueryResults);
}
else
{
var html = Model.GenerateHtml(this.model.QueryResults);
this.view.TextBrowser.NavigateToString(html);
}
}
};
this.executeQueryCommand = new RelayCommand(
async p =>
{
string source = this.GetSource();
this.SelectedNode = 0;
this.SelectedEdge = 0;
// First execute the query to get the result graph in memory:
List<IRecord> result = await this.model.ExecuteCypherAsync(source);
if (result != null)
{
// The query executed correctly.
if (this.model.ConnectResultNodes)
{
if (this.GraphModeSelected && !Model.CanBeRenderedAsGraph(result))
{
this.GraphModeSelected = false;
this.TextModeSelected = true;
}
this.model.QueryResults = result;
}
else
{
string resultJson = Model.GenerateJSON(result);
var backgroundColorBrush = this.view.FindResource("MaterialDesignPaper") as SolidColorBrush;
var foregroundColorBrush = this.view.FindResource("MaterialDesignBody") as SolidColorBrush;
var backgroundColor = JavascriptColorString(backgroundColorBrush.Color);
var foregroundColor = JavascriptColorString(foregroundColorBrush.Color);
await view.Browser.ExecuteScriptAsync(string.Format("draw({0}, '{1}', '{2}');", resultJson, backgroundColor, foregroundColor));
}
}
},
// Running is allowed when there is text there to submit as a query and
// there is a connection to the database.
p =>
{
return v.CypherEditor.Document.Text.Any();
});
this.printGraphCommand = new RelayCommand(
async p =>
{
await this.view.Browser.ExecuteScriptAsync("this.print();");
}
);
}