initial src code import

master
Maciej Aniserowicz 2012-12-18 13:18:05 +01:00
parent b3f807c521
commit 29aa7d229a
28 changed files with 1612 additions and 0 deletions

12
src/UI.WPF/App.config Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<appSettings>
<add key="fimAddress" value="http://localhost"/>
<add key="fimUser" value="Administrator"/>
<add key="fimPassword" value="pass"/>
</appSettings>
</configuration>

8
src/UI.WPF/App.xaml Normal file
View File

@ -0,0 +1,8 @@
<Application x:Class="Predica.FimExplorer.UI.WPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Main\MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

11
src/UI.WPF/App.xaml.cs Normal file
View File

@ -0,0 +1,11 @@
using System.Windows;
namespace Predica.FimExplorer.UI.WPF
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

View File

@ -0,0 +1,28 @@
using System.Windows;
using System.Windows.Controls;
using Microsoft.ResourceManagement.ObjectModel;
using System;
namespace Predica.FimExplorer.UI.WPF.Details
{
/// <summary>
/// Selects hyperlink for references and normal text for other values
/// </summary>
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<RmReference>())
{
return (DataTemplate)contentPresenter.FindResource("normalCell");
}
else
{
return (DataTemplate)contentPresenter.FindResource("referenceCell");
}
}
}
}

View File

@ -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<RmAttributeName, RmAttributeValue> _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<RmAttributeName, RmAttributeValue> attributeDescrpition)
{
_attributeDescrpition = attributeDescrpition;
}
}
}

View File

@ -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<FlattenedAttribute> 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);
}
}
}

View File

@ -0,0 +1,38 @@
<Window x:Class="Predica.FimExplorer.UI.WPF.Details.ObjectDetailsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Predica.FimExplorer.UI.WPF.Details"
Title="{Binding ResourceForWindowTitle}" Height="353" Width="632">
<Window.Resources>
<ResourceDictionary>
<local:DetailsValueCellTemplateSelector x:Key="valueCellTemplateSelector"></local:DetailsValueCellTemplateSelector>
<DataTemplate x:Key="normalCell">
<TextBlock Text="{Binding Value}"></TextBlock>
</DataTemplate>
<DataTemplate x:Key="referenceCell">
<StackPanel Orientation="Horizontal">
<TextBlock><Hyperlink Click="Hyperlink_Click">
<TextBlock Text="{Binding Value}"></TextBlock>
</Hyperlink></TextBlock>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<DataGrid
ItemsSource="{Binding Attributes}"
SelectionUnit="Cell"
AutoGenerateColumns="False"
>
<DataGrid.Columns>
<DataGridTextColumn
Header="Name"
Binding="{Binding AttributeName}"
/>
<DataGridTemplateColumn
Header="Value"
CellTemplateSelector="{StaticResource valueCellTemplateSelector}"
CopyingCellClipboardContent="ValueColumn_CopyingCellClipboardContent"
/>
</DataGrid.Columns>
</DataGrid>
</Window>

View File

@ -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;
}
}
}

View File

@ -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));
}
}
}

View File

@ -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
{
/// <summary>
/// Extracts text contained in Hyperlink contaning TextBlock, wrapped in another TextBlock
/// </summary>
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<TextBlock>())
{
innerTextBlock = (TextBlock)VisualTreeHelper.GetChild(contentPresenter, 0);
}
return innerTextBlock.Text;
}
}
}

View File

@ -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<DataColumn>().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();
}
}

View File

@ -0,0 +1,12 @@
<Window x:Class="Predica.FimExplorer.UI.WPF.Import.ImportedObjectsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Imported objects" Height="300" Width="686">
<Grid>
<DataGrid
IsReadOnly="True"
ItemsSource="{Binding ImportedValues}"
AutoGeneratingColumn="DataGrid_AutoGeneratingColumn"
/>
</Grid>
</Window>

View File

@ -0,0 +1,37 @@
using System.Windows;
using System.Windows.Controls;
namespace Predica.FimExplorer.UI.WPF.Import
{
/// <summary>
/// Interaction logic for ImportedObjectsWindow.xaml
/// </summary>
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<object>(e
, id =>
{
_model.DetailsById(id);
});
}
}
}

View File

@ -0,0 +1,333 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Linq;
using Microsoft.ResourceManagement.Client;
using Microsoft.ResourceManagement.ObjectModel;
using Microsoft.ResourceManagement.ObjectModel.ResourceTypes;
using Predica.FimCommunication;
using Predica.FimCommunication.Export;
using Predica.FimCommunication.Querying;
namespace Predica.FimExplorer.UI.WPF.Main
{
public class MainModel : INotifyPropertyChanged
{
private readonly MainWindow _parent;
private readonly IFimClient _fimClient;
private readonly WindowsManager _windowsManager;
private readonly IXmlExporter _xmlExporter;
#region Bound properties
#region XPath
private string _xPath;
public string XPath
{
get { return _xPath; }
set
{
_xPath = value;
NotifyChanged("XPath");
}
}
#endregion
#region IdForSearch
private string _idForSearch;
public string IdForSearch
{
get { return _idForSearch; }
set
{
_idForSearch = value;
NotifyChanged("IdForSearch");
}
}
#endregion
#region QueriedValues
private DataTable _queriedValues;
public DataTable QueriedValues
{
get { return _queriedValues; }
set
{
_queriedValues = value;
NotifyChanged("QueriedValues");
}
}
#endregion
#region SelectedObjectType
private RmObjectTypeDescription _selectedObjectType;
public RmObjectTypeDescription SelectedObjectType
{
get { return _selectedObjectType; }
set
{
_selectedObjectType = value;
XPath = "/" + value.Name;
FetchAttributesForSelectedObjectType();
}
}
#endregion
private DataRowView _selectedRow;
public DataRowView SelectedRow
{
get { return _selectedRow; }
set
{
_selectedRow = value;
IdForSearch = _selectedRow["Resource ID"].ToString();
}
}
public ObservableCollection<RmObjectTypeDescription> ObjectTypes { get; private set; }
public ObservableCollection<SelectableAttribute> CurrentAttributes { get; private set; }
#endregion
public MainModel(MainWindow parent, IFimClient fimClient, WindowsManager windowsManager, IXmlExporter xmlExporter)
{
_parent = parent;
_fimClient = fimClient;
_windowsManager = windowsManager;
_xmlExporter = xmlExporter;
ObjectTypes = new ObservableCollection<RmObjectTypeDescription>();
CurrentAttributes = new ObservableCollection<SelectableAttribute>();
}
public void Initialize()
{
FetchObjectTypes();
// cannot use ResourceTypeExtractor here - it is unable to extract type from RmResource
SelectedObjectType = ObjectTypes.Single(x => x.Name == "Resource");
}
#region Fetching object types
private void FetchObjectTypes()
{
_parent.LongRunningOperation(_parent.loadingIndicator, () =>
{
var attributesToFetch = new AttributesToFetch(RmResource.AttributeNames.DisplayName.Name, RmAttributeTypeDescription.AttributeNames.Name.Name);
var list = _fimClient.EnumerateAll<RmObjectTypeDescription>("/ObjectTypeDescription", attributesToFetch)
.ToList();
ObjectTypes.Clear();
list.ForEach(x => ObjectTypes.Add(x));
});
}
#endregion
#region Fetching attributes
private readonly Dictionary<string, List<SelectableAttribute>> _attributesCache =
new Dictionary<string, List<SelectableAttribute>>();
private void FetchAttributesForSelectedObjectType()
{
string typeName = SelectedObjectType.Name;
FetchAttributesForObjectType(typeName);
FillCurrentAttributesCollection(_attributesCache[typeName]);
}
private void FetchAttributesForObjectType(string typeName)
{
if (_attributesCache.ContainsKey(typeName))
{
return;
}
_parent.LongRunningOperation(_parent.loadingIndicator, () =>
{
var attributesToFetch = new AttributesToFetch(
RmResource.AttributeNames.DisplayName.Name
, RmAttributeTypeDescription.AttributeNames.Name.Name
, RmAttributeTypeDescription.AttributeNames.DataType.Name
);
string query = "/BindingDescription[BoundObjectType = "
+ "/ObjectTypeDescription[Name = '{0}']]".FormatWith(typeName)
+ "/BoundAttributeType";
var attributes = _fimClient.EnumerateAll<RmAttributeTypeDescription>(query, attributesToFetch)
.ToList();
_attributesCache.Add(typeName, attributes.Select(x => new SelectableAttribute(x)).ToList());
});
}
private void FillCurrentAttributesCollection(IEnumerable<SelectableAttribute> list)
{
CurrentAttributes.Clear();
list.ForEach(x => CurrentAttributes.Add(x));
}
#endregion
#region Executing query
private bool querying_selected_type()
{
return XPath.StartsWith("/" + SelectedObjectType.Name);
}
public void ExecuteQuery()
{
var requiredAttributes = new List<SelectableAttribute>();
// attributes selection is possible only when directly querying for type selected in the list
// otherwise there is no smart/easy way to tell if queried object contains any of currently displayed attributes
if (querying_selected_type())
{
if (CurrentAttributes.Any(x => x.IsSelected))
{
requiredAttributes = CurrentAttributes.Where(x => x.IsSelected).ToList();
}
else
{
requiredAttributes = CurrentAttributes.ToList();
}
if (requiredAttributes.None(x => x.Attribute.Name == RmResource.AttributeNames.ObjectID.Name))
{
requiredAttributes.Add(CurrentAttributes.Single(x => x.Attribute.Name == RmResource.AttributeNames.ObjectID.Name));
}
}
// empty array if querying for other type than selected => all attributes will be fetched
var attributesToFetch = new AttributesToFetch(requiredAttributes.Select(x => x.Attribute.Name).ToArray());
string query = XPath.Replace(Environment.NewLine, string.Empty);
RmResource[] results = _parent.LongRunningOperation(_parent.loadingIndicator,
() => _fimClient.EnumerateAll<RmResource>(query, attributesToFetch)
.ToArray()
);
DataTable table = new DataTable();
if (results.IsNotEmpty())
{
// assuming that all results are of the same type
var firstResult = results.First();
var resultType = firstResult.GetResourceType();
var resultTypeAttributes = firstResult.Attributes;
SelectableAttribute[] fetchedAttributes;
if (requiredAttributes.IsNotEmpty())
{
fetchedAttributes = requiredAttributes.ToArray();
}
else
{
FetchAttributesForObjectType(resultType);
fetchedAttributes = _attributesCache[resultType].ToArray();
}
var resultTableColumnNames = new List<string>();
foreach (var a in resultTypeAttributes.OrderBy(x => x.Key.Name))
{
var selectedAttribute = fetchedAttributes.SingleOrDefault(x => x.Attribute.Name == a.Key.Name);
if (selectedAttribute == null)
{
continue;
}
var column = table.Columns.Add(selectedAttribute.Attribute.DisplayName);
resultTableColumnNames.Add(selectedAttribute.Attribute.Name);
// window can detect references by their 'object' type as opposed to 'string' defined for all other fields
if (selectedAttribute.Attribute.DataType == RmFactory.RmAttributeType.Reference.ToString())
{
column.DataType = typeof(object);
}
else
{
column.DataType = typeof(string);
}
}
foreach (var resource in results)
{
var newRowValues = resultTableColumnNames
.Select(x =>
{
if (resource.Attributes.Keys.Any(y => y.Name == x))
{
return resource.Attributes.First(y => y.Key.Name == x)
.Value.Value;
}
else
{
return null;
}
})
.ToArray();
table.Rows.Add(newRowValues);
}
}
QueriedValues = table;
}
#endregion
#region Showing details by object id
public void ShowDetailsById()
{
RmResource result = _parent.LongRunningOperation(_parent.loadingIndicator,
() => _fimClient.FindById(IdForSearch)
);
_windowsManager.ObjectDetailsDialog(result);
}
#endregion
public void ExportToXml(string filePath)
{
using (var fileStream = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.Write))
{
_xmlExporter.WriteXml(fileStream, XPath);
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private void NotifyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}

View File

@ -0,0 +1,65 @@
<Window x:Class="Predica.FimExplorer.UI.WPF.Main.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Predica FIM Explorer" Height="600" Width="1200">
<Grid>
<Grid x:Name="mainContainer">
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="350" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0">
<StackPanel Orientation="Horizontal">
<TextBox
Width="250"
Height="40"
TextWrapping="Wrap" AcceptsReturn="True" VerticalScrollBarVisibility="Visible"
Text="{Binding XPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
/>
<Button Content="Run query" Click="btnRunQuery_Click" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Width="250"
x:Name="tbIdToFind"
Text="{Binding IdForSearch, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
/>
<Button Content="Find by ID" Click="btnFindById_Click" />
</StackPanel>
<Button Click="btnExportXml_Click">Export to XML</Button>
<Button Click="btnImportXml_Click">Import XML</Button>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="0" Orientation="Horizontal">
<ListBox ItemsSource="{Binding ObjectTypes}"
SelectedItem="{Binding SelectedObjectType, Mode=TwoWay}"
DisplayMemberPath="DisplayName" />
<ListBox
ItemsSource="{Binding CurrentAttributes}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox
Content="{Binding Attribute.DisplayName}"
IsChecked="{Binding IsSelected,Mode=TwoWay}"
/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
<DataGrid
Grid.Row="0" Grid.Column="1" Grid.RowSpan="2"
SelectionUnit="FullRow"
IsReadOnly="True"
ItemsSource="{Binding QueriedValues}"
SelectedItem="{Binding SelectedRow,Mode=TwoWay}"
AutoGeneratingColumn="DataGrid_AutoGeneratingColumn"
AutoGeneratedColumns="DataGrid_AutoGeneratedColumns"
MouseDoubleClick="DataGrid_MouseDoubleClick" />
</Grid>
<Grid x:Name="loadingIndicator"
Visibility="Collapsed" Opacity="0.5" Background="Black">
</Grid>
</Grid>
</Window>

View File

@ -0,0 +1,150 @@
using System;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using Predica.FimCommunication.Export;
namespace Predica.FimExplorer.UI.WPF.Main
{
public partial class MainWindow : Window
{
MainModel _model;
ReferenceColumnDetector _referenceColumnDetector;
WindowsManager _windowsManager;
public MainWindow()
{
try
{
_windowsManager = new WindowsManager();
_model = new MainModel(this, FimClientFactory.CreateClient(), _windowsManager, new XmlExporter());
_referenceColumnDetector = new ReferenceColumnDetector();
InitializeComponent();
this.DataContext = _model;
_model.Initialize();
}
catch (Exception exc)
{
MessageBox.Show(@"There was an error during application startup. Verify that your configuration file points to a valid FIM service instance.
Application will now close.
Exception details:
" + exc, "Predica.FimExplorer Initialization error", MessageBoxButton.OK, MessageBoxImage.Error);
Application.Current.Shutdown();
}
}
private void btnRunQuery_Click(object sender, RoutedEventArgs e)
{
if (_model.CurrentAttributes.Count == 0)
{
_windowsManager.Error("Object type must be selected first. Only currently selected object type can be searched by xpath.");
return;
}
try
{
_model.ExecuteQuery();
}
catch (Exception exc)
{
_windowsManager.Error(exc.ToString());
}
}
private void btnFindById_Click(object sender, RoutedEventArgs e)
{
_model.ShowDetailsById();
}
private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
// only references are marked as objects, all other are strings
_referenceColumnDetector.ProcessAutogeneratedColumn<object>(e
, id =>
{
_model.IdForSearch = id;
_model.ShowDetailsById();
});
DecideColumnOrder(e);
}
// field used to correctly set column display indexes (ID and DisplayName); reset when all columns are generated
private bool resourceID_column_already_configured = false;
private void DecideColumnOrder(DataGridAutoGeneratingColumnEventArgs e)
{
// ObjectID always as 1st column
if (e.PropertyName == "Resource ID")
{
e.Column.DisplayIndex = 0;
resourceID_column_already_configured = true;
}
// DisplayName is 2nd
if (e.PropertyName == "Display Name")
{
// set to 0 if ResourceID was not already configured - it will be configured later, pushing this column further
e.Column.DisplayIndex = resourceID_column_already_configured ? 1 : 0;
}
}
private void DataGrid_AutoGeneratedColumns(object sender, EventArgs e)
{
resourceID_column_already_configured = false;
}
private void DataGrid_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
_model.ShowDetailsById();
}
private void btnExportXml_Click(object sender, RoutedEventArgs e)
{
try
{
var saveFileDialog = new SaveFileDialog();
saveFileDialog.DefaultExt = ".xml";
saveFileDialog.Filter = "XML documents|*.xml|All files|*.*";
if (saveFileDialog.ShowDialog() != true)
{
return;
}
_model.ExportToXml(saveFileDialog.FileName);
_windowsManager.Info("Export finished");
}
catch (Exception exc)
{
_windowsManager.Error(exc.ToString());
}
}
private void btnImportXml_Click(object sender, RoutedEventArgs e)
{
try
{
var openFileDialog = new OpenFileDialog();
openFileDialog.DefaultExt = ".xml";
openFileDialog.Filter = "XML documents|*.xml|All files|*.*";
if (openFileDialog.ShowDialog() != true)
{
return;
}
_windowsManager.ImportObjectsDialog(openFileDialog.FileName);
}
catch (Exception exc)
{
_windowsManager.Error(exc.ToString());
}
}
}
}

View File

@ -0,0 +1,15 @@
using Microsoft.ResourceManagement.ObjectModel.ResourceTypes;
namespace Predica.FimExplorer.UI.WPF.Main
{
public class SelectableAttribute
{
public RmAttributeTypeDescription Attribute { get; set; }
public bool IsSelected { get; set; }
public SelectableAttribute(RmAttributeTypeDescription attribute)
{
Attribute = attribute;
}
}
}

View File

@ -0,0 +1,50 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
namespace Predica.FimExplorer.UI.WPF.Main
{
public static class WindowExtensions
{
public static TResult LongRunningOperation<TResult>(this Window @this, Panel loadingIndicator, Func<TResult> action)
{
TResult result = default(TResult);
@this.LongRunningOperation(loadingIndicator, new Action(() => result = action()));
return result;
}
public static void LongRunningOperation(this Window @this, Panel loadingIndicator, Action action)
{
var originalCursor = @this.Cursor;
@this.Cursor = Cursors.Wait;
loadingIndicator.Visibility = Visibility.Visible;
DispatcherFrame frame = new DispatcherFrame();
DispatcherOperation dispatcherOperation = Dispatcher.CurrentDispatcher
.BeginInvoke(DispatcherPriority.ContextIdle,
new Action(() =>
{
try
{
action();
}
finally
{
loadingIndicator.Visibility = Visibility.Collapsed;
@this.Cursor = originalCursor;
}
})
);
dispatcherOperation.Completed += delegate
{
frame.Continue = false;
};
Dispatcher.PushFrame(frame);
}
}
}

View File

@ -0,0 +1,23 @@
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
[assembly: AssemblyTitle("Predica.FimExplorer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Predica Business Solutions")]
[assembly: AssemblyProduct("Predica.FimExplorer")]
[assembly: AssemblyCopyright("Copyright © Predica Business Solutions")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
)]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.225
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Predica.FimExplorer.UI.WPF.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Predica.FimExplorer.UI.WPF.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.225
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Predica.FimExplorer.UI.WPF.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View File

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@ -0,0 +1,56 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
namespace Predica.FimExplorer.UI.WPF
{
/// <summary>
/// Detects (based on type) if given autogenerated column is a reference
/// and replaces it with hyperlink column with 'click' event handled
/// </summary>
public class ReferenceColumnDetector
{
public void ProcessAutogeneratedColumn<TReferenceType>(DataGridAutoGeneratingColumnEventArgs e, Action<string> handleReferenceClick)
{
var originalColumn = (DataGridBoundColumn)e.Column;
Type propertyType = e.PropertyType;
if (propertyType.Is<TReferenceType>())
{
var linkStyle = CreateLinkStyle(handleReferenceClick);
e.Column = CreateHyperlinkColumn(originalColumn, linkStyle);
}
}
private Style CreateLinkStyle(Action<string> handleReferenceClick)
{
var linkStyle = new Style(typeof (TextBlock));
RoutedEventHandler clickHandler = (o, e) =>
{
var textExtractor = new HyperlinkTextExtractor();
string id = textExtractor.ExtractText((Hyperlink) e.Source);
handleReferenceClick(id);
};
linkStyle.Setters.Add(new EventSetter(Hyperlink.ClickEvent, clickHandler));
return linkStyle;
}
private static DataGridHyperlinkColumn CreateHyperlinkColumn(DataGridBoundColumn originalColumn, Style linkStyle)
{
var newColumn = new DataGridHyperlinkColumn
{
ElementStyle = linkStyle,
Binding = originalColumn.Binding,
Header = originalColumn.Header,
HeaderStringFormat = "[ref] {0}",
};
return newColumn;
}
}
}

151
src/UI.WPF/UI.WPF.csproj Normal file
View File

@ -0,0 +1,151 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{72AF0280-1398-4DB7-A2FA-24DAA9B4CDAB}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Predica.FimExplorer.UI.WPF</RootNamespace>
<AssemblyName>Predica.FimExplorer</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="NLog">
<HintPath>..\..\FimClient\lib\NLog2.netfx40\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.configuration" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="Details\DetailsValueCellTemplateSelector.cs" />
<Compile Include="FimClientFactory.cs" />
<Compile Include="HyperlinkTextExtractor.cs" />
<Compile Include="Import\ImportedObjectsModel.cs" />
<Compile Include="Import\ImportedObjectsWindow.xaml.cs">
<DependentUpon>ImportedObjectsWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Main\WindowExtensions.cs" />
<Compile Include="ReferenceColumnDetector.cs" />
<Compile Include="WindowsManager.cs" />
<Compile Include="_Utils\EnumerableExtensions.cs" />
<Compile Include="_Utils\TypeExtensions.cs" />
<Page Include="Import\ImportedObjectsWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Main\MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="Details\ObjectDetailsModel.cs" />
<Compile Include="Main\SelectableAttribute.cs" />
<Compile Include="Main\MainModel.cs" />
<Compile Include="Main\MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Page Include="Details\ObjectDetailsWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Compile Include="Details\FlattenedAttribute.cs" />
<Compile Include="Details\ObjectDetailsWindow.xaml.cs">
<DependentUpon>ObjectDetailsWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="app.config">
<SubType>Designer</SubType>
</None>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<AppDesigner Include="Properties\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\FimClient\src\FimCommunication\FimCommunication.csproj">
<Project>{b296ee14-b117-4b23-8e36-3e15932d8e8e}</Project>
<Name>FimCommunication</Name>
</ProjectReference>
<ProjectReference Include="..\..\FimClient\src\_external\fim2010client\Microsoft.ResourceManagement.Client\Fim2010Client.Client.csproj">
<Project>{6c1064e6-72b3-41aa-b543-4164ec056f68}</Project>
<Name>Fim2010Client.Client</Name>
</ProjectReference>
<ProjectReference Include="..\..\FimClient\src\_external\fim2010client\Microsoft.ResourceManagement.ObjectModel\Fim2010Client.ObjectModel.csproj">
<Project>{cecb3a49-a780-4558-a402-1f3ff42b0b18}</Project>
<Name>Fim2010Client.ObjectModel</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,61 @@
using System.IO;
using System.Windows;
using Microsoft.ResourceManagement.ObjectModel;
using NLog;
using Predica.FimCommunication.Import;
using Predica.FimExplorer.UI.WPF.Details;
using Predica.FimExplorer.UI.WPF.Import;
namespace Predica.FimExplorer.UI.WPF
{
public class WindowsManager
{
public void ObjectDetailsDialog(RmResource target)
{
if (target == null)
{
_log.Debug("Cannot show details of null object");
return;
}
var model = new ObjectDetailsModel(target, FimClientFactory.CreateClient(), this);
var detailsWindow = new ObjectDetailsWindow();
detailsWindow.Initialize(model);
_log.Debug("Showing details of [{0}:{1}]", target.ObjectType, target.ObjectID);
detailsWindow.Show();
}
public void ImportObjectsDialog(string fileName)
{
_log.Debug("Showing import window for file {0}", fileName);
var importWindow = new ImportedObjectsWindow();
ImportedObjectsModel model;
using (var stream = File.OpenRead(fileName))
{
model = new ImportedObjectsModel(stream, new XmlImporter(), this);
model.Initialize();
importWindow.Initialize(model);
}
importWindow.ShowDialog();
}
public void Info(string message)
{
MessageBox.Show(message, "Information", MessageBoxButton.OK, MessageBoxImage.Information);
}
public void Error(string message)
{
MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Predica.FimExplorer.UI.WPF
{
public static class EnumerableExtensions
{
public static bool IsEmpty<T>(this IEnumerable<T> @this)
{
return !@this.Any();
}
public static bool IsNotEmpty<T>(this IEnumerable<T> @this)
{
return !@this.IsEmpty();
}
public static void ForEach<T>(this IEnumerable<T> @this, Action<T> operation)
{
foreach (var element in @this)
{
operation(element);
}
}
public static bool None<T>(this IEnumerable<T> @this, Func<T, bool> predicate)
{
return !@this.Any(predicate);
}
}
}

View File

@ -0,0 +1,17 @@
using System;
namespace Predica.FimExplorer.UI.WPF
{
public static class TypeExtensions
{
public static bool Is<TOther>(this Type @this)
{
return @this == (typeof(TOther));
}
public static bool IsNot<TOther>(this Type @this)
{
return !@this.Is<TOther>();
}
}
}