Index: src/DeltaShell/DeltaShell.Gui/DeltaShell.Gui.csproj =================================================================== diff -u -r7311da16a34bc2588440d0166f3a09402e4fd16f -r50d4f9e34ad8050478c2d63675b1f17a376fb43d --- src/DeltaShell/DeltaShell.Gui/DeltaShell.Gui.csproj (.../DeltaShell.Gui.csproj) (revision 7311da16a34bc2588440d0166f3a09402e4fd16f) +++ src/DeltaShell/DeltaShell.Gui/DeltaShell.Gui.csproj (.../DeltaShell.Gui.csproj) (revision 50d4f9e34ad8050478c2d63675b1f17a376fb43d) @@ -140,6 +140,9 @@ ProgressDialog.cs + + Component + SplashScreen.xaml @@ -184,12 +187,6 @@ OptionsDialog.cs - - UserControl - - - PropertyGrid.cs - UserControl @@ -490,10 +487,6 @@ ProgressDialog.cs - - PropertyGrid.cs - Designer - ViewSelectionContextMenuController.cs Designer Index: src/DeltaShell/DeltaShell.Gui/Forms/MainWindow/MainWindow.xaml.cs =================================================================== diff -u -rb301f31bca6957a9326b4b0ab912bd7b5da60195 -r50d4f9e34ad8050478c2d63675b1f17a376fb43d --- src/DeltaShell/DeltaShell.Gui/Forms/MainWindow/MainWindow.xaml.cs (.../MainWindow.xaml.cs) (revision b301f31bca6957a9326b4b0ab912bd7b5da60195) +++ src/DeltaShell/DeltaShell.Gui/Forms/MainWindow/MainWindow.xaml.cs (.../MainWindow.xaml.cs) (revision 50d4f9e34ad8050478c2d63675b1f17a376fb43d) @@ -62,7 +62,7 @@ private bool resetDefaultLayout; private MessageWindow.MessageWindow messageWindow; - private PropertyGrid.PropertyGrid propertyGrid; + private PropertyGridView propertyGrid; private WindowInteropHelper windowInteropHelper; private IEnumerable ribbonCommandHandlers; @@ -267,7 +267,7 @@ { if ((propertyGrid == null) || (propertyGrid.IsDisposed)) { - propertyGrid = new PropertyGrid.PropertyGrid(Gui); + propertyGrid = new PropertyGridView(Gui); } propertyGrid.Text = Properties.Resources.Properties; Fisheye: Tag 50d4f9e34ad8050478c2d63675b1f17a376fb43d refers to a dead (removed) revision in file `src/DeltaShell/DeltaShell.Gui/Forms/PropertyGrid/PropertyGrid.Designer.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 50d4f9e34ad8050478c2d63675b1f17a376fb43d refers to a dead (removed) revision in file `src/DeltaShell/DeltaShell.Gui/Forms/PropertyGrid/PropertyGrid.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 50d4f9e34ad8050478c2d63675b1f17a376fb43d refers to a dead (removed) revision in file `src/DeltaShell/DeltaShell.Gui/Forms/PropertyGrid/PropertyGrid.resx'. Fisheye: No comparison available. Pass `N' to diff? Index: src/DeltaShell/DeltaShell.Gui/PropertyGridView.cs =================================================================== diff -u --- src/DeltaShell/DeltaShell.Gui/PropertyGridView.cs (revision 0) +++ src/DeltaShell/DeltaShell.Gui/PropertyGridView.cs (revision 50d4f9e34ad8050478c2d63675b1f17a376fb43d) @@ -0,0 +1,352 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Security.Permissions; +using System.Windows.Forms; +using DelftTools.Controls; +using DelftTools.Shell.Core; +using DelftTools.Shell.Gui; +using DelftTools.Shell.Gui.Forms; +using DelftTools.Utils.PropertyBag.Dynamic; +using DeltaShell.Gui.Properties; +using log4net; + +namespace DeltaShell.Gui +{ + public class PropertyGridView : System.Windows.Forms.PropertyGrid, IPropertyGrid, IObserver + { + private static readonly ILog Log = LogManager.GetLogger(typeof(PropertyGridView)); + + /// + /// todo: This is still an unwanted dependency. PropertyGrid uses gui to subscribe to the SelectionChanged + /// delegate and in responce queries the gui.Selection + /// nicer? : custom public delegate in IPropertyGrid with selection as parameter + /// + private readonly IGui gui; + + private object selectedObject; + private IObservable observableProperty; + + public PropertyGridView(IGui gui) + { +// InitializeComponent(); + HideTabsButton(); + + this.gui = gui; + + gui.SelectionChanged += GuiSelectionChanged; + } + + public new object SelectedObject + { + get + { + return selectedObject; + } + set + { + // Performance optimization + if (selectedObject == value) + { + return; + } + + selectedObject = value; + + OnSelectedObjectChanged(); + } + } + +// public override void Refresh() +// { +// Refresh(); +// } + + public void UpdateObserver() + { + Refresh(); + } + + public object GetObjectProperties(object sourceData) + { + if (sourceData == null) + { + return null; + } + + // Obtain all property information + var propertyInfos = gui.Plugins.SelectMany(p => p.GetPropertyInfos()).ToList(); + + // 1. Match property information based on ObjectType and on AdditionalDataCheck + propertyInfos = propertyInfos.Where(pi => pi.ObjectType.IsInstanceOfType(sourceData) && (pi.AdditionalDataCheck == null || pi.AdditionalDataCheck(sourceData))).ToList(); + + // 2. Match property information based on object type inheritance + propertyInfos = FilterPropertyInfoByTypeInheritance(propertyInfos, pi => pi.ObjectType); + + // 3. Match property information based on property type inheritance + propertyInfos = FilterPropertyInfoByTypeInheritance(propertyInfos, pi => pi.PropertyType); + + if (propertyInfos.Count == 0) + { + // No (or multiple) object properties found: return 'null' so that no object properties are shown in the property grid + return null; + } + + if (propertyInfos.Count > 1) + { + // 4. We assume that the propertyInfos with AdditionalDataCheck are the most specific + propertyInfos = propertyInfos.Where(pi => pi.AdditionalDataCheck != null).ToList(); + } + + if (propertyInfos.Count == 1) + { + return CreateObjectProperties(propertyInfos.ElementAt(0), sourceData); + } + + Log.Debug(Resources.PropertyGrid_GetObjectProperties_Multiple_object_property_instances_found_for_the_same_data_object__no_object_properties_are_displayed_in_the_property_grid); + return null; + } + + protected override void Dispose(bool disposing) + { + if (gui != null) + { + gui.SelectionChanged -= GuiSelectionChanged; + } + + if (observableProperty != null) + { + observableProperty.Detach(this); + } + + base.Dispose(disposing); + } + + private void HideTabsButton() + { + // removing "property tabs" button and separator before it + // TODO: case we used derived functionality for this? + var strip = Controls.OfType().ToList()[0]; + strip.Items[3].Visible = false; + strip.Items[4].Visible = false; + } + + private void GuiSelectionChanged(object sender, EventArgs e) + { + if (observableProperty != null) + { + observableProperty.Detach(this); + } + + var selection = gui.Selection; + if (selection == null) + { + SelectedObject = null; + return; + } + + observableProperty = selection as IObservable; + if (observableProperty != null) + { + observableProperty.Attach(this); + } + + SelectedObject = GetObjectProperties(selection); + } + + private List FilterPropertyInfoByTypeInheritance(List propertyInfo, Func getTypeAction) + { + var propertyInfoCount = propertyInfo.Count; + var propertyInfoWithUnInheritedType = propertyInfo.ToList(); + + for (var i = 0; i < propertyInfoCount; i++) + { + var firstType = getTypeAction(propertyInfo.ElementAt(i)); + + for (var j = 0; j < propertyInfoCount; j++) + { + if (i == j) + { + continue; + } + + var secondType = getTypeAction(propertyInfo.ElementAt(j)); + + if (firstType != secondType && firstType.IsAssignableFrom(secondType)) + { + propertyInfoWithUnInheritedType.Remove(propertyInfo.ElementAt(i)); + + break; + } + } + } + + return propertyInfoWithUnInheritedType.Any() + ? propertyInfoWithUnInheritedType.ToList() // One or more specific property information objects found: return the filtered list + : propertyInfo; // No specific property information found: return the original list + } + + private object CreateObjectProperties(PropertyInfo propertyInfo, object sourceData) + { + try + { + // Try to create object properties for the source data + var objectProperties = propertyInfo.CreateObjectProperties(sourceData); + + // Return a dynamic property bag containing the created object properties + return objectProperties is DynamicPropertyBag + ? (object) objectProperties + : new DynamicPropertyBag(objectProperties); + } + catch (Exception) + { + Log.Debug(Resources.PropertyGrid_CreateObjectProperties_Could_not_create_object_properties_for_the_data); + + // Directly return the source data (TODO: Shouldn't we return "null" instead?) + return sourceData; + } + } + + /// + /// If the selected objects changed rebuild the internal dictionary that counts the number of + /// objects of each type. + /// + private void OnSelectedObjectChanged() + { + if (SelectedObject == null) + { + base.SelectedObject = null; + return; + } + + var selectedType = GetRelevantType(SelectedObject); + + Log.DebugFormat(Resources.PropertyGrid_OnSelectedObjectsChanged_Selected_object_of_type___0_, selectedType.Name); + + base.SelectedObject = SelectedObject; + } + + private static Type GetRelevantType(object obj) + { + if (obj is DynamicPropertyBag) + { + var bag = obj as DynamicPropertyBag; + return bag.GetContentType(); + } + return obj.GetType(); + } + + #region IPropertyGrid Members + + public object Data + { + get + { + return SelectedObject; + } + set + { + SelectedObject = value; + } + } + + public Image Image + { + get + { + return Resources.PropertiesHS; + } + set {} + } + + public void EnsureVisible(object item) {} + + public ViewInfo ViewInfo { get; set; } + + #endregion + + #region Enable tab key navigation on property grid + + /// + /// Do special processing for Tab key. + /// http://www.codeproject.com/csharp/wdzPropertyGridUtils.asp + /// + /// + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + if ((keyData == Keys.Tab) || (keyData == (Keys.Tab | Keys.Shift))) + { + var selectedItem = SelectedGridItem; + var root = selectedItem; + if (selectedItem == null) + { + return false; + } + while (root.Parent != null) + { + root = root.Parent; + } + // Find all expanded items and put them in a list. + var items = new ArrayList(); + AddExpandedItems(root, items); + + // Find selectedItem. + int foundIndex = items.IndexOf(selectedItem); + if ((keyData & Keys.Shift) == Keys.Shift) + { + foundIndex--; + if (foundIndex < 0) + { + foundIndex = items.Count - 1; + } + SelectedGridItem = (GridItem) items[foundIndex]; + if (SelectedGridItem.GridItems.Count > 0) + { + SelectedGridItem.Expanded = false; + } + } + else + { + foundIndex++; + if (items.Count > 0) + { + if (foundIndex >= items.Count) + { + foundIndex = 0; + } + SelectedGridItem = (GridItem) items[foundIndex]; + } + if (SelectedGridItem.GridItems.Count > 0) + { + SelectedGridItem.Expanded = true; + } + } + + return true; + } + return base.ProcessCmdKey(ref msg, keyData); + } + + private static void AddExpandedItems(GridItem parent, IList items) + { + if (parent.PropertyDescriptor != null) + { + items.Add(parent); + } + if (parent.Expanded) + { + foreach (GridItem child in parent.GridItems) + { + AddExpandedItems(child, items); + } + } + } + + #endregion + } +} \ No newline at end of file Index: test/DeltaShell/DeltaShell.Tests/DeltaShell.Tests.csproj =================================================================== diff -u -r6dbce7d02bbc8849c767bf7963df7127a98c2897 -r50d4f9e34ad8050478c2d63675b1f17a376fb43d --- test/DeltaShell/DeltaShell.Tests/DeltaShell.Tests.csproj (.../DeltaShell.Tests.csproj) (revision 6dbce7d02bbc8849c767bf7963df7127a98c2897) +++ test/DeltaShell/DeltaShell.Tests/DeltaShell.Tests.csproj (.../DeltaShell.Tests.csproj) (revision 50d4f9e34ad8050478c2d63675b1f17a376fb43d) @@ -48,6 +48,10 @@ true + + False + ..\..\..\lib\DevExpress.Utils.v9.3.dll + False ..\..\..\lib\log4net.dll Index: test/DeltaShell/DeltaShell.Tests/Gui/Forms/PropertyGrid/PropertyGridTest.cs =================================================================== diff -u -r5fc71a385897af92ccb092f2f969b5709afab85a -r50d4f9e34ad8050478c2d63675b1f17a376fb43d --- test/DeltaShell/DeltaShell.Tests/Gui/Forms/PropertyGrid/PropertyGridTest.cs (.../PropertyGridTest.cs) (revision 5fc71a385897af92ccb092f2f969b5709afab85a) +++ test/DeltaShell/DeltaShell.Tests/Gui/Forms/PropertyGrid/PropertyGridTest.cs (.../PropertyGridTest.cs) (revision 50d4f9e34ad8050478c2d63675b1f17a376fb43d) @@ -25,7 +25,7 @@ mocks.ReplayAll(); - var propertyGrid = new DeltaShell.Gui.Forms.PropertyGrid.PropertyGrid(gui); + var propertyGrid = new PropertyGridView(gui); var objectProperties = propertyGrid.GetObjectProperties(1.0); Assert.IsNull(objectProperties); @@ -52,7 +52,7 @@ mocks.ReplayAll(); - var propertyGrid = new DeltaShell.Gui.Forms.PropertyGrid.PropertyGrid(gui); + var propertyGrid = new PropertyGridView(gui); var objectProperties = propertyGrid.GetObjectProperties(new A()); Assert.IsTrue(objectProperties is DynamicPropertyBag); @@ -83,7 +83,7 @@ mocks.ReplayAll(); - var propertyGrid = new DeltaShell.Gui.Forms.PropertyGrid.PropertyGrid(gui); + var propertyGrid = new PropertyGridView(gui); var objectProperties = propertyGrid.GetObjectProperties(new A()); Assert.IsNull(objectProperties); @@ -120,7 +120,7 @@ mocks.ReplayAll(); - var propertyGrid = new DeltaShell.Gui.Forms.PropertyGrid.PropertyGrid(gui); + var propertyGrid = new PropertyGridView(gui); var objectProperties = propertyGrid.GetObjectProperties(new C()); //we ask for C Assert.AreSame(typeof(SimpleProperties), @@ -149,7 +149,7 @@ mocks.ReplayAll(); - var propertyGrid = new DeltaShell.Gui.Forms.PropertyGrid.PropertyGrid(gui); + var propertyGrid = new PropertyGridView(gui); var objectProperties = propertyGrid.GetObjectProperties(new D()); Assert.IsTrue(objectProperties is DynamicPropertyBag); @@ -178,7 +178,7 @@ mocks.ReplayAll(); - var propertyGrid = new DeltaShell.Gui.Forms.PropertyGrid.PropertyGrid(gui); + var propertyGrid = new PropertyGridView(gui); var objectProperties = propertyGrid.GetObjectProperties(new D()); Assert.IsTrue(objectProperties is DynamicPropertyBag); @@ -213,7 +213,7 @@ mocks.ReplayAll(); - var propertyGrid = new DeltaShell.Gui.Forms.PropertyGrid.PropertyGrid(gui); + var propertyGrid = new PropertyGridView(gui); var objectProperties = propertyGrid.GetObjectProperties(new D()); Assert.IsTrue(objectProperties is DynamicPropertyBag); @@ -241,7 +241,7 @@ mocks.ReplayAll(); - var propertyGrid = new DeltaShell.Gui.Forms.PropertyGrid.PropertyGrid(gui); + var propertyGrid = new PropertyGridView(gui); var objectProperties = propertyGrid.GetObjectProperties(new B()); Assert.IsTrue(objectProperties is DynamicPropertyBag); @@ -273,7 +273,7 @@ mocks.ReplayAll(); - var propertyGrid = new DeltaShell.Gui.Forms.PropertyGrid.PropertyGrid(gui); + var propertyGrid = new PropertyGridView(gui); var objectProperties = propertyGrid.GetObjectProperties(new B()); Assert.IsTrue(objectProperties is DynamicPropertyBag); @@ -303,7 +303,7 @@ mocks.ReplayAll(); - var propertyGrid = new DeltaShell.Gui.Forms.PropertyGrid.PropertyGrid(gui); + var propertyGrid = new PropertyGridView(gui); var objectProperties = propertyGrid.GetObjectProperties(new B()); Assert.IsTrue(objectProperties is DynamicPropertyBag); @@ -337,7 +337,7 @@ mocks.ReplayAll(); - var propertyGrid = new DeltaShell.Gui.Forms.PropertyGrid.PropertyGrid(gui); + var propertyGrid = new PropertyGridView(gui); var objectProperties = propertyGrid.GetObjectProperties(new B()); Assert.IsTrue(objectProperties is DynamicPropertyBag); @@ -366,7 +366,7 @@ mocks.ReplayAll(); object propertyObject = new B(); - var propertyGrid = new DeltaShell.Gui.Forms.PropertyGrid.PropertyGrid(gui); + var propertyGrid = new PropertyGridView(gui); TestHelper.AssertLogMessageIsGenerated(() => propertyObject = propertyGrid.GetObjectProperties(new B()), "Multiple object property instances found for the same data object: no object properties are displayed in the property grid"); @@ -411,7 +411,7 @@ mocks.ReplayAll(); - var propertyGrid = new DeltaShell.Gui.Forms.PropertyGrid.PropertyGrid(gui); + var propertyGrid = new PropertyGridView(gui); var objectProperties = propertyGrid.GetObjectProperties(new C()); Assert.IsTrue(objectProperties is DynamicPropertyBag);