/// <summary> /// Multicasts a request to open up an object to anyone listening. /// </summary> /// <param name="objectArgs">Parameters used to open an object.</param> protected virtual void OnOpenObject(MarkThree.Guardian.Object guardianObject) { // Tell any object that is listening that it should open an object in a viewer. if (this.isOpenEventAllowed && this.OpenObject != null) { OpenObject(this, guardianObject); } }
/// <summary> /// Handles the properties of the objects in the Object Tree. /// </summary> /// <param name="sender">The object that originated the event.</param> /// <param name="e">Event argument.</param> private void menuItemProperties_Click(object sender, System.EventArgs e) { // Only invoke the properties if the node is highlighted. if (FolderList.highlightNode == null) { return; } MarkThree.Guardian.Object guardianObject = (MarkThree.Guardian.Object)FolderList.highlightNode.Tag; guardianObject.ShowProperties(); }
/// <summary> /// Creates a TreeNode with the attributes of an object found in the database. /// </summary> /// <param name="objectRow">A row that describes how to construct the attributes for this node.</param> /// <returns>A node that contains the attributes of the object found in the database, and its descendants.</returns> private TreeNode CreateTreeNode(ArrayList images, ClientMarketData.ObjectRow objectRow) { // This node will be attached to the tree view in a hierarchical order according to the data found in the 'ObjectTree' // data structure. The node itself will be given the properties of the object in the database used to create this // node. TreeNode treeNode = new TreeNode(); // IMPORTANT CONCEPT: This will extract the specification for the type from the database and construct an instance of // an object of that type. For instance, the specification 'MarkThree.Guardian.Blotter, Object Library' would be used // to build a 'MarkThree.Guardian.Blotter' object from the 'Object Library'. The object identifier is used to populate // the object from the persistent store with the properties of that item. Once the object is built, it is installed in // the tree. When the user selects and item on the tree to be opened, this object is passed to the container which will // know how to view the object based on its type and the viewer mapped to that type. TypeSpecification typeSpecification = new TypeSpecification(objectRow.TypeRow.Specification); Assembly assembly = Assembly.Load(typeSpecification.AssemblyName); MarkThree.Guardian.Object folderObject = (MarkThree.Guardian.Object)assembly.CreateInstance( typeSpecification.TypeName, false, BindingFlags.CreateInstance, null, new object[] { objectRow.ObjectId }, System.Globalization.CultureInfo.CurrentCulture, null); // The attributes of the object just created become the main attributes of the TreeNode. treeNode.Tag = folderObject; treeNode.Text = folderObject.Name; // The 'Image' is the picture that is displayed in the TreeView along with the text. It's the visual que that shows // what kind of object is stored on the tree. The 'ImageKey' is a unique key that allows the TreeView control to map a // node to an item in the ImageList. if (folderObject.Image16x16 != null) { treeNode.ImageKey = (string)folderObject.Image16x16.Tag; images.Add(folderObject.Image16x16); } // This is the image that is displayed when the object is selected. if (folderObject.SelectedImage16x16 != null) { treeNode.SelectedImageKey = (string)folderObject.SelectedImage16x16.Tag; images.Add(folderObject.SelectedImage16x16); } // And recursivley add the children. RecurseNodes(images, treeNode, objectRow); // This represents the node that was created above, and all the children found in the hierarchy. return(treeNode); }
/// <summary> /// Adds an object to the ListView control. /// </summary> /// <param name="guardianObject">The object to be added.</param> private void AddObject(MarkThree.Guardian.Object guardianObject) { // See if the object's image exists in the list of images yet. If it doesn't both the small and large bitmaps are // added. int imageIndex = this.listView.LargeImageList.Images.IndexOfKey(guardianObject.GetType().ToString()); if (imageIndex == -1) { imageIndex = this.listView.LargeImageList.Images.Count; this.listView.LargeImageList.Images.Add(guardianObject.GetType().ToString(), guardianObject.Image32x32); this.listView.SmallImageList.Images.Add(guardianObject.GetType().ToString(), guardianObject.Image16x16); } // The 'GuardianViewItem' adds some extra features to the standard ListViewItem, such as the MarkThree.Guardian.Object // that specifies what kind of object is added to the tree. This extra information is used to open up a viewer when // the item is selected. this.listView.Items.Add(new GuardianViewItem(guardianObject, imageIndex)); }
/// <summary> /// Moves a child object from one parent to another. /// </summary> /// <param name="parameter">An array consiting of the target parent, the current parent and the child to be moved.</param> private void MoveChild(object parameter) { // Extract the objects selected by the drag-and-drop operation. object[] parameters = (object[])parameter; MarkThree.Guardian.Object toObject = (MarkThree.Guardian.Object)parameters[0]; MarkThree.Guardian.Object fromObject = (MarkThree.Guardian.Object)parameters[1]; MarkThree.Guardian.Object childObject = (MarkThree.Guardian.Object)parameters[2]; try { // Lock the tables needed for this operation. System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); ClientMarketData.ObjectLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.ObjectTreeLock.AcquireReaderLock(CommonTimeout.LockWait); // It's critical that circular references aren't created, either by accident or design. First, find the object // record associated with the destination node. ClientMarketData.ObjectRow parentRow = ClientMarketData.Object.FindByObjectId(toObject.ObjectId); if (parentRow == null) { throw new Exception("This object has been deleted"); } // This is the object that is being dragged. Find the row ClientMarketData.ObjectRow childRow = ClientMarketData.Object.FindByObjectId(childObject.ObjectId); if (childRow == null) { throw new Exception("This object has been deleted"); } // This will remove the possibility of a circular relationship. if (MarkThree.Guardian.Relationship.IsChildObject(childRow, parentRow)) { Invoke(new MessageDelegate(ShowMessage), Properties.Resources.CircularReferenceError, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } } catch (Exception exception) { // Write the error and stack trace out to the debug listener EventLog.Error("{0}, {1}", exception.Message, exception.StackTrace); } finally { // Release table locks. if (ClientMarketData.ObjectLock.IsReaderLockHeld) { ClientMarketData.ObjectLock.ReleaseReaderLock(); } if (ClientMarketData.ObjectTreeLock.IsReaderLockHeld) { ClientMarketData.ObjectTreeLock.ReleaseReaderLock(); } System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); } // Any commands created below will be constructed in this object and sent to the server for execution. bool isBatchValid = true; Batch batch = new Batch(); // If we made it here, the drag-and-drop is interpreted as a command to move a child from one parent to another. try { // Lock the tables needed for this operation. System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); ClientMarketData.ObjectLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.ObjectTreeLock.AcquireReaderLock(CommonTimeout.LockWait); // Extract the primary identifiers from the user interface nodes. // This is the object that is being dragged. Find the row ClientMarketData.ObjectRow childRow = ClientMarketData.Object.FindByObjectId(childObject.ObjectId); if (childRow == null) { throw new Exception("This object has been deleted"); } // Find the object in the data model and make sure it still exists. ClientMarketData.ObjectTreeRow objectTreeRow = null; foreach (ClientMarketData.ObjectTreeRow innerObjectTreeRow in childRow.GetObjectTreeRowsByObjectObjectTreeChildId()) { if (innerObjectTreeRow.ParentId == fromObject.ObjectId) { objectTreeRow = innerObjectTreeRow; } } if (objectTreeRow == null) { throw new Exception("This relationship has been deleted by someone else."); } // Moving a child object from one parent to another must be accomplished as a transaction. Otherwise, an // orhpan object will be created if the operation fails midway through. TransactionPlan transaction = batch.Transactions.Add(); AssemblyPlan assembly = batch.Assemblies.Add("Core Service"); TypePlan type = assembly.Types.Add("MarkThree.Guardian.Core.ObjectTree"); // Construct a command delete the old parent relation. MethodPlan deleteObjectTree = transaction.Methods.Add(type, "Update"); deleteObjectTree.Parameters.Add(new InputParameter("objectTreeId", objectTreeRow.ObjectTreeId)); deleteObjectTree.Parameters.Add(new InputParameter("parentId", toObject.ObjectId)); deleteObjectTree.Parameters.Add(new InputParameter("childId", childObject.ObjectId)); deleteObjectTree.Parameters.Add(new InputParameter("rowVersion", objectTreeRow.RowVersion)); } catch (Exception exception) { // Write the error and stack trace out to the debug listener EventLog.Error("{0}, {1}", exception.Message, exception.StackTrace); // This indicates that the batch shouldn't be executed. isBatchValid = false; } finally { // Release table locks. if (ClientMarketData.ObjectLock.IsReaderLockHeld) { ClientMarketData.ObjectLock.ReleaseReaderLock(); } if (ClientMarketData.ObjectTreeLock.IsReaderLockHeld) { ClientMarketData.ObjectTreeLock.ReleaseReaderLock(); } System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); } // If the command batch was built successfully, then execute it. if (isBatchValid) { try { // Call the web server to rename the object on the database. Note that this method must be called when there are // no locks to prevent deadlocking. That is why it appears in it's own 'try/catch' block. ClientMarketData.Execute(batch); } catch (BatchException batchException) { // Display each error in the batch. foreach (Exception exception in batchException.Exceptions) { MessageBox.Show(exception.Message, "Guardian Error"); } } } }
/// <summary> /// Rename the object. /// </summary> /// <param name="parameters">The object to be renamed.</param> private void RenameObject(object parameter) { // Extract the thread arguments object[] parameters = (object[])parameter; TreeNode treeNode = (TreeNode)parameters[0]; string name = (string)parameters[1]; // Extract the object that is associated with the TreeView node. MarkThree.Guardian.Object commonObject = (MarkThree.Guardian.Object)treeNode.Tag; // This command batch is constructed below and sent to the server for execution. Note that the batch is designed to // live beyond the block of code that locks the data model. This is to prevent the data model from being locked while // a relatively long server database operation is underway. bool isBatchValid = true; Batch batch = new Batch(); try { // Lock the table System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); ClientMarketData.ObjectLock.AcquireReaderLock(CommonTimeout.LockWait); // Find the object in the data model and make sure it still exists. ClientMarketData.ObjectRow objectRow = ClientMarketData.Object.FindByObjectId(commonObject.ObjectId); if (objectRow == null) { throw new Exception("This object has been deleted."); } // Construct a command to rename the object. AssemblyPlan assembly = batch.Assemblies.Add("Core Service"); TypePlan type = assembly.Types.Add("MarkThree.Guardian.Core.Object"); TransactionPlan transaction = batch.Transactions.Add(); MethodPlan method = transaction.Methods.Add(type, "Update"); method.Parameters.Add(new InputParameter("rowVersion", objectRow.RowVersion)); method.Parameters.Add(new InputParameter("objectId", objectRow.ObjectId)); method.Parameters.Add(new InputParameter("name", name)); } catch (Exception exception) { // This serves as an indication that the batch couldn't be constructed. isBatchValid = false; // Write the error and stack trace out to the debug listener EventLog.Error("{0}, {1}", exception.Message, exception.StackTrace); } finally { // Release table locks. if (ClientMarketData.ObjectLock.IsReaderLockHeld) { ClientMarketData.ObjectLock.ReleaseReaderLock(); } System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); } // If the command batch was built successfully, then execute it. If any part of it should fail, cancel the edit and // display the server errors. if (isBatchValid) { try { // Call the web server to rename the object on the database. Note that this method must be called when there // are no locks to prevent deadlocking. That is why it appears in it's own 'try/catch' block. ClientMarketData.Execute(batch); } catch (BatchException batchException) { // Display each error in the batch. foreach (Exception exception in batchException.Exceptions) { Invoke(new MessageDelegate(ShowMessage), exception.Message, MessageBoxButtons.OK, MessageBoxIcon.Error); } } } }
/// <summary> /// Determines if any of the newly added objects should appear in this control. /// </summary> /// <param name="parameter">A list of newly added object identifiers.</param> private void NewObjectThread(object parameter) { // This will purge the list of new objects to the data model down to only the items that have previously been selected // for this control. The list of objects in this control is a critical resources that is shared between threads. ArrayList newObjectIdList = (ArrayList)parameter; lock (this) { for (int index = 0; index < newObjectIdList.Count;) { if (!this.objectIdList.Contains(newObjectIdList[index])) { newObjectIdList.RemoveAt(index); } else { index++; } } } // Any new objects are created in the background from the object identifier. These objects are passed to the // foreground where they can be added to the control. ArrayList objectList = new ArrayList(); try { // Lock the tables required to build an object. System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); ClientMarketData.AccountLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.BlotterLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.FolderLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.UserLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.ObjectLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.ObjectTreeLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.SystemFolderLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.TypeLock.AcquireReaderLock(CommonTimeout.LockWait); // This will build an object for every object identifier in the list. foreach (int objectId in newObjectIdList) { MarkThree.Guardian.Object guardianObject = MarkThree.Guardian.Object.CreateObject(objectId); if (!System.Object.ReferenceEquals(guardianObject, null)) { objectList.Add(guardianObject); } } } catch (Exception exception) { // Catch the most general error and send it to the debug console. EventLog.Error("{0}, {1}", exception.Message, exception.Message); } finally { // Release the tables used to build the folder list. if (ClientMarketData.AccountLock.IsReaderLockHeld) { ClientMarketData.AccountLock.ReleaseReaderLock(); } if (ClientMarketData.BlotterLock.IsReaderLockHeld) { ClientMarketData.BlotterLock.ReleaseReaderLock(); } if (ClientMarketData.FolderLock.IsReaderLockHeld) { ClientMarketData.FolderLock.ReleaseReaderLock(); } if (ClientMarketData.UserLock.IsReaderLockHeld) { ClientMarketData.UserLock.ReleaseReaderLock(); } if (ClientMarketData.ObjectLock.IsReaderLockHeld) { ClientMarketData.ObjectLock.ReleaseReaderLock(); } if (ClientMarketData.ObjectTreeLock.IsReaderLockHeld) { ClientMarketData.ObjectTreeLock.ReleaseReaderLock(); } if (ClientMarketData.SystemFolderLock.IsReaderLockHeld) { ClientMarketData.SystemFolderLock.ReleaseReaderLock(); } if (ClientMarketData.TypeLock.IsReaderLockHeld) { ClientMarketData.TypeLock.ReleaseReaderLock(); } System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); } // Wait for the window handle to be created before trying to write to the control. The thread only needs to wait once. // After that, this event is permanently signaled. this.handleCreatedEvent.WaitOne(); // Send the new items to the foreground to be place in the control. The 'this.InvokeRequired' is needed during the // shutdown to prevent items from being sent on a thread that no longer exists. if (this.InvokeRequired) { Invoke(this.objectAvailableCallback, new object[] { objectList }); } }
/// <summary> /// Initialize the background attributes of this control. /// </summary> private void InitializationThread(object parameter) { // In the off chance that this control is initialized after all the data has been loaded, then this initialization will // generate the objects that appear in the list view. Generally, this control will be initialized long before the data // arrives, but you never know how a control will be used and abused in the future. ArrayList objectList = new ArrayList(); try { // Lock the tables required to build an object. System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); ClientMarketData.AccountLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.BlotterLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.FolderLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.UserLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.ObjectLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.ObjectTreeLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.SystemFolderLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.TypeLock.AcquireReaderLock(CommonTimeout.LockWait); // Install the event handlers ClientMarketData.Object.ObjectRowChanging += new DataSetMarket.ObjectRowChangeEventHandler(ChangeObjectRow); ClientMarketData.EndMerge += new EventHandler(EndMerge); // Create a list of objects that match the peristent identifers that were stored in the user preferences. foreach (int objectId in this.objectIdList) { MarkThree.Guardian.Object guardianObject = MarkThree.Guardian.Object.CreateObject(objectId); if (!System.Object.ReferenceEquals(guardianObject, null)) { objectList.Add(guardianObject); } } } catch (Exception exception) { // Catch the most general error and send it to the debug console. EventLog.Error("{0}, {1}", exception.Message, exception.StackTrace); } finally { // Release the tables used to build the objects. if (ClientMarketData.AccountLock.IsReaderLockHeld) { ClientMarketData.AccountLock.ReleaseReaderLock(); } if (ClientMarketData.BlotterLock.IsReaderLockHeld) { ClientMarketData.BlotterLock.ReleaseReaderLock(); } if (ClientMarketData.FolderLock.IsReaderLockHeld) { ClientMarketData.FolderLock.ReleaseReaderLock(); } if (ClientMarketData.UserLock.IsReaderLockHeld) { ClientMarketData.UserLock.ReleaseReaderLock(); } if (ClientMarketData.ObjectLock.IsReaderLockHeld) { ClientMarketData.ObjectLock.ReleaseReaderLock(); } if (ClientMarketData.ObjectTreeLock.IsReaderLockHeld) { ClientMarketData.ObjectTreeLock.ReleaseReaderLock(); } if (ClientMarketData.SystemFolderLock.IsReaderLockHeld) { ClientMarketData.SystemFolderLock.ReleaseReaderLock(); } if (ClientMarketData.TypeLock.IsReaderLockHeld) { ClientMarketData.TypeLock.ReleaseReaderLock(); } System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); } // If the initial pass through the data model found some objects that are ready to be installed in the Guardian Bar // list, then pass them on to the foreground. Note that this thread will wait here until there is a window to which // the results can be passed. if (objectList.Count != 0) { this.handleCreatedEvent.WaitOne(); Invoke(this.objectAvailableCallback, new object[] { objectList }); } }
/// <summary> /// Initializes a new instance of the GuardianViewItem class. /// </summary> public GuardianViewItem(MarkThree.Guardian.Object guardianObject, int imageIndex) : base(guardianObject.Name, imageIndex) { // Initialize the class members. this.guardianObject = guardianObject; }