diff --git a/src/UI.WPF/App.config b/src/UI.WPF/App.config
new file mode 100644
index 0000000..c5e2970
--- /dev/null
+++ b/src/UI.WPF/App.config
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/UI.WPF/App.xaml b/src/UI.WPF/App.xaml
new file mode 100644
index 0000000..9f0aba0
--- /dev/null
+++ b/src/UI.WPF/App.xaml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/src/UI.WPF/App.xaml.cs b/src/UI.WPF/App.xaml.cs
new file mode 100644
index 0000000..c11ab54
--- /dev/null
+++ b/src/UI.WPF/App.xaml.cs
@@ -0,0 +1,11 @@
+using System.Windows;
+
+namespace Predica.FimExplorer.UI.WPF
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/src/UI.WPF/Details/DetailsValueCellTemplateSelector.cs b/src/UI.WPF/Details/DetailsValueCellTemplateSelector.cs
new file mode 100644
index 0000000..2f1fe0b
--- /dev/null
+++ b/src/UI.WPF/Details/DetailsValueCellTemplateSelector.cs
@@ -0,0 +1,28 @@
+using System.Windows;
+using System.Windows.Controls;
+using Microsoft.ResourceManagement.ObjectModel;
+using System;
+
+namespace Predica.FimExplorer.UI.WPF.Details
+{
+ ///
+ /// Selects hyperlink for references and normal text for other values
+ ///
+ public class DetailsValueCellTemplateSelector : DataTemplateSelector
+ {
+ public override DataTemplate SelectTemplate(object item, DependencyObject container)
+ {
+ var contentPresenter = (ContentPresenter)container;
+ var attribute = (FlattenedAttribute)item;
+
+ if (attribute == null || attribute.ValueType.IsNot())
+ {
+ return (DataTemplate)contentPresenter.FindResource("normalCell");
+ }
+ else
+ {
+ return (DataTemplate)contentPresenter.FindResource("referenceCell");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/UI.WPF/Details/FlattenedAttribute.cs b/src/UI.WPF/Details/FlattenedAttribute.cs
new file mode 100644
index 0000000..5b23bc3
--- /dev/null
+++ b/src/UI.WPF/Details/FlattenedAttribute.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.ResourceManagement.ObjectModel;
+
+namespace Predica.FimExplorer.UI.WPF.Details
+{
+ public class FlattenedAttribute
+ {
+ private readonly KeyValuePair _attributeDescrpition;
+
+ public string AttributeName { get { return _attributeDescrpition.Key.Name; } }
+ public string Value
+ {
+ get
+ {
+ return _attributeDescrpition.Value.Value == null
+ ? string.Empty
+ : _attributeDescrpition.Value.Value.ToString();
+ }
+ }
+ public Type ValueType
+ {
+ get
+ {
+ return _attributeDescrpition.Value.Value == null
+ ? null
+ : _attributeDescrpition.Value.Value.GetType();
+ }
+ }
+ public string ValueTypeName
+ {
+ get
+ {
+ return _attributeDescrpition.Value.Value == null
+ ? string.Empty
+ : _attributeDescrpition.Value.Value.GetType().Name;
+ }
+ }
+
+ public FlattenedAttribute(KeyValuePair attributeDescrpition)
+ {
+ _attributeDescrpition = attributeDescrpition;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/UI.WPF/Details/ObjectDetailsModel.cs b/src/UI.WPF/Details/ObjectDetailsModel.cs
new file mode 100644
index 0000000..4bd9a88
--- /dev/null
+++ b/src/UI.WPF/Details/ObjectDetailsModel.cs
@@ -0,0 +1,42 @@
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.ResourceManagement.ObjectModel;
+using Predica.FimCommunication;
+
+namespace Predica.FimExplorer.UI.WPF.Details
+{
+ public class ObjectDetailsModel
+ {
+ private readonly IFimClient _fimClient;
+ private readonly WindowsManager _windowsManager;
+
+ public RmResource Resource { get; set; }
+ public IEnumerable Attributes { get; set; }
+
+ public string ResourceForWindowTitle
+ {
+ get
+ {
+ return "[{0}] {1} ({2})".FormatWith(Resource.ObjectType, Resource.DisplayName, Resource.ObjectID);
+ }
+ }
+
+ public ObjectDetailsModel(RmResource resource, IFimClient fimClient, WindowsManager windowsManager)
+ {
+ _fimClient = fimClient;
+ _windowsManager = windowsManager;
+
+ Resource = resource;
+
+ Attributes = resource.Attributes
+ .Select(x => new FlattenedAttribute(x))
+ .ToList();
+ }
+
+ public void ShowDetailsById(string id)
+ {
+ RmResource result = _fimClient.FindById(id);
+ _windowsManager.ObjectDetailsDialog(result);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/UI.WPF/Details/ObjectDetailsWindow.xaml b/src/UI.WPF/Details/ObjectDetailsWindow.xaml
new file mode 100644
index 0000000..944e120
--- /dev/null
+++ b/src/UI.WPF/Details/ObjectDetailsWindow.xaml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/UI.WPF/Details/ObjectDetailsWindow.xaml.cs b/src/UI.WPF/Details/ObjectDetailsWindow.xaml.cs
new file mode 100644
index 0000000..76bf48e
--- /dev/null
+++ b/src/UI.WPF/Details/ObjectDetailsWindow.xaml.cs
@@ -0,0 +1,40 @@
+using System.Windows;
+using System.Windows.Documents;
+
+namespace Predica.FimExplorer.UI.WPF.Details
+{
+ public partial class ObjectDetailsWindow : Window
+ {
+ private ObjectDetailsModel _model;
+
+ public ObjectDetailsWindow()
+ {
+ InitializeComponent();
+ }
+
+ public void Initialize(ObjectDetailsModel model)
+ {
+ _model = model;
+ this.DataContext = model;
+ }
+
+ private void Hyperlink_Click(object sender, RoutedEventArgs e)
+ {
+ var textExtractor = new HyperlinkTextExtractor();
+ string id = textExtractor.ExtractText((Hyperlink) e.Source);
+ _model.ShowDetailsById(id);
+ }
+
+ private void ValueColumn_CopyingCellClipboardContent(object sender, System.Windows.Controls.DataGridCellClipboardEventArgs e)
+ {
+ var attribute = e.Item as FlattenedAttribute;
+
+ if (attribute == null)
+ {
+ return;
+ }
+
+ e.Content = attribute.Value;
+ }
+ }
+}
diff --git a/src/UI.WPF/FimClientFactory.cs b/src/UI.WPF/FimClientFactory.cs
new file mode 100644
index 0000000..009d2bb
--- /dev/null
+++ b/src/UI.WPF/FimClientFactory.cs
@@ -0,0 +1,18 @@
+using System.Configuration;
+using System.Net;
+using Predica.FimCommunication;
+
+namespace Predica.FimExplorer.UI.WPF
+{
+ public class FimClientFactory
+ {
+ public static IFimClient CreateClient()
+ {
+ var url = ConfigurationManager.AppSettings["fimAddress"];
+ var username = ConfigurationManager.AppSettings["fimUser"];
+ var password = ConfigurationManager.AppSettings["fimPassword"];
+
+ return new FimClient(url, new NetworkCredential(username, password));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/UI.WPF/HyperlinkTextExtractor.cs b/src/UI.WPF/HyperlinkTextExtractor.cs
new file mode 100644
index 0000000..f335ae1
--- /dev/null
+++ b/src/UI.WPF/HyperlinkTextExtractor.cs
@@ -0,0 +1,33 @@
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Media;
+using System;
+
+namespace Predica.FimExplorer.UI.WPF
+{
+ ///
+ /// Extracts text contained in Hyperlink contaning TextBlock, wrapped in another TextBlock
+ ///
+ public class HyperlinkTextExtractor
+ {
+ public string ExtractText(Hyperlink hyperlink)
+ {
+ TextBlock textBlock = (TextBlock)hyperlink.Parent;
+
+ DependencyObject containerVisual = VisualTreeHelper.GetChild(textBlock, 0);
+ DependencyObject contentPresenter = VisualTreeHelper.GetChild(containerVisual, 0);
+
+ var innerTextBlock = contentPresenter as TextBlock;
+
+ // true for link cells in main grid
+ // false for link cells in details grid
+ if (contentPresenter.GetType().IsNot())
+ {
+ innerTextBlock = (TextBlock)VisualTreeHelper.GetChild(contentPresenter, 0);
+ }
+
+ return innerTextBlock.Text;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/UI.WPF/Import/ImportedObjectsModel.cs b/src/UI.WPF/Import/ImportedObjectsModel.cs
new file mode 100644
index 0000000..4d9661f
--- /dev/null
+++ b/src/UI.WPF/Import/ImportedObjectsModel.cs
@@ -0,0 +1,122 @@
+using System.ComponentModel;
+using System.Data;
+using System.IO;
+using Microsoft.ResourceManagement.ObjectModel;
+using System.Linq;
+using NLog;
+using Predica.FimCommunication;
+using Predica.FimCommunication.Import;
+
+namespace Predica.FimExplorer.UI.WPF.Import
+{
+ public class ImportedObjectsModel : INotifyPropertyChanged
+ {
+ private readonly Stream _inputStream;
+ private readonly IXmlImporter _xmlImporter;
+ private readonly WindowsManager _windowsManager;
+
+ private ImportResult _importResult;
+
+ #region ImportedValues
+
+ private DataTable _importedValues;
+ public DataTable ImportedValues
+ {
+ get { return _importedValues; }
+ set
+ {
+ _importedValues = value;
+ NotifyChanged("ImportedValues");
+ }
+ }
+
+ #endregion
+
+ public ImportedObjectsModel(Stream inputStream, IXmlImporter xmlImporter, WindowsManager windowsManager)
+ {
+ _inputStream = inputStream;
+ _xmlImporter = xmlImporter;
+ _windowsManager = windowsManager;
+ }
+
+ public void Initialize()
+ {
+ _importResult = _xmlImporter.Import(_inputStream);
+
+ if (_importResult.PrimaryImportObjects.IsEmpty())
+ {
+ return;
+ }
+
+ var table = new DataTable();
+
+ var resourceWithMostAttributes = _importResult.PrimaryImportObjects.OrderByDescending(x => x.Attributes.Count).First();
+
+ foreach (var attr in resourceWithMostAttributes.Attributes)
+ {
+ var column = table.Columns.Add(attr.Key.Name);
+
+ // window can detect references by their 'object' type as opposed to 'string' defined for all other fields
+ if (resourceWithMostAttributes[attr.Key.Name].Value is RmReference)
+ {
+ column.DataType = typeof(object);
+ }
+ else
+ {
+ column.DataType = typeof(string);
+ }
+
+ }
+
+ foreach (var importTarget in _importResult.PrimaryImportObjects)
+ {
+ var newRowValues = table.Columns.Cast().Select(x =>
+ {
+ if (importTarget.Attributes.Keys.Any(y => y.Name == x.ColumnName))
+ {
+ return importTarget.Attributes.First(y => y.Key.Name == x.ColumnName).Value.Value;
+ }
+ else
+ {
+ return null;
+ }
+ })
+ .ToArray();
+
+ table.Rows.Add(newRowValues);
+ }
+
+ _log.Debug("Created {0}-column table with {1} imported rows", table.Columns.Count, table.Rows.Count);
+
+ ImportedValues = table;
+ }
+
+ public void DetailsById(string id)
+ {
+ var obj = _importResult.AllImportedObjects.FirstOrDefault(x => x.ObjectID.Value == id);
+
+ if (obj == null)
+ {
+ _windowsManager.Error("Object with id {0} was not imported".FormatWith(id));
+ }
+ else
+ {
+ _log.Debug("showing details of imported object {0}", id);
+ _windowsManager.ObjectDetailsDialog(obj);
+ }
+ }
+
+ #region INotifyPropertyChanged
+
+ public event PropertyChangedEventHandler PropertyChanged = delegate { };
+
+ private void NotifyChanged(string propertyName)
+ {
+ PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ #endregion
+
+ private static readonly Logger _log = LogManager.GetCurrentClassLogger();
+ }
+}
\ No newline at end of file
diff --git a/src/UI.WPF/Import/ImportedObjectsWindow.xaml b/src/UI.WPF/Import/ImportedObjectsWindow.xaml
new file mode 100644
index 0000000..dc73ee4
--- /dev/null
+++ b/src/UI.WPF/Import/ImportedObjectsWindow.xaml
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/src/UI.WPF/Import/ImportedObjectsWindow.xaml.cs b/src/UI.WPF/Import/ImportedObjectsWindow.xaml.cs
new file mode 100644
index 0000000..381be86
--- /dev/null
+++ b/src/UI.WPF/Import/ImportedObjectsWindow.xaml.cs
@@ -0,0 +1,37 @@
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Predica.FimExplorer.UI.WPF.Import
+{
+ ///
+ /// Interaction logic for ImportedObjectsWindow.xaml
+ ///
+ public partial class ImportedObjectsWindow : Window
+ {
+ ImportedObjectsModel _model;
+ ReferenceColumnDetector _referenceColumnDetector;
+
+ public ImportedObjectsWindow()
+ {
+ InitializeComponent();
+
+ _referenceColumnDetector = new ReferenceColumnDetector();
+ }
+
+ public void Initialize(ImportedObjectsModel model)
+ {
+ _model = model;
+ this.DataContext = model;
+ }
+
+ private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
+ {
+ // only references are marked as objects, all other are strings
+ _referenceColumnDetector.ProcessAutogeneratedColumn