494 lines
20 KiB
C#
494 lines
20 KiB
C#
|
|
//------------------------------------------------------------------------------
|
||
|
|
/// <copyright from='1997' to='2002' company='Microsoft Corporation'>
|
||
|
|
/// Copyright (c) Microsoft Corporation. All Rights Reserved.
|
||
|
|
///
|
||
|
|
/// This source code is intended only as a supplement to Microsoft
|
||
|
|
/// Development Tools and/or on-line documentation. See these other
|
||
|
|
/// materials for detailed information regarding Microsoft code samples.
|
||
|
|
///
|
||
|
|
/// </copyright>
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
namespace SampleDesignerHost
|
||
|
|
{
|
||
|
|
using System;
|
||
|
|
using System.ComponentModel;
|
||
|
|
using System.ComponentModel.Design;
|
||
|
|
using System.Collections;
|
||
|
|
using System.Windows.Forms;
|
||
|
|
using System.Windows.Forms.Design;
|
||
|
|
|
||
|
|
using System.Diagnostics;
|
||
|
|
|
||
|
|
/// This class keeps track of the selected component(s) and provides events
|
||
|
|
/// to notify about selection changes.
|
||
|
|
internal class SampleSelectionService : ISelectionService {
|
||
|
|
|
||
|
|
// These constitute the current selection at any moment.
|
||
|
|
private SampleSelectionItem primarySelection; // the primary selection
|
||
|
|
private Hashtable selectionsByComponent; // hashtable of selections
|
||
|
|
|
||
|
|
// Hookups to other services
|
||
|
|
private IDesignerHost host; // The host interface
|
||
|
|
private IContainer container; // The container we're showing selection for
|
||
|
|
|
||
|
|
// These are used when the host is in batch mode: we want to queue up selection
|
||
|
|
// changes in this case.
|
||
|
|
private bool batchMode; // Are we in batch mode?
|
||
|
|
private bool selectionChanged; // true, if the selection changed in batch mode
|
||
|
|
private bool selectionContentsChanged; // true, if the selection contents changed in batch mode
|
||
|
|
public bool need_clear_selection = false;
|
||
|
|
|
||
|
|
internal SampleSelectionService(IDesignerHost host) {
|
||
|
|
this.host = host;
|
||
|
|
this.container = host.Container;
|
||
|
|
this.selectionsByComponent = new Hashtable();
|
||
|
|
this.selectionChanged = false;
|
||
|
|
|
||
|
|
// We initialize this to true to catch anything that would cause
|
||
|
|
// an update during load.
|
||
|
|
this.batchMode = true;
|
||
|
|
|
||
|
|
// And configure the events we want to listen to.
|
||
|
|
IComponentChangeService cs = (IComponentChangeService)host.GetService(typeof(IComponentChangeService));
|
||
|
|
Debug.Assert(cs != null, "IComponentChangeService not found");
|
||
|
|
if (cs != null) {
|
||
|
|
cs.ComponentAdded += new ComponentEventHandler(this.DesignerHost_ComponentAdd);
|
||
|
|
cs.ComponentRemoved += new ComponentEventHandler(this.DesignerHost_ComponentRemove);
|
||
|
|
cs.ComponentChanged += new ComponentChangedEventHandler(this.DesignerHost_ComponentChanged);
|
||
|
|
}
|
||
|
|
|
||
|
|
host.TransactionOpened += new EventHandler(this.DesignerHost_TransactionOpened);
|
||
|
|
host.TransactionClosed += new DesignerTransactionCloseEventHandler(this.DesignerHost_TransactionClosed);
|
||
|
|
|
||
|
|
// If our host is already in a transaction, we handle it.
|
||
|
|
if (host.InTransaction) {
|
||
|
|
DesignerHost_TransactionOpened(host, EventArgs.Empty);
|
||
|
|
}
|
||
|
|
|
||
|
|
host.LoadComplete += new EventHandler(this.DesignerHost_LoadComplete);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Properties
|
||
|
|
|
||
|
|
/// The primary selection is the last one to have been selected.
|
||
|
|
object ISelectionService.PrimarySelection {
|
||
|
|
get {
|
||
|
|
if (primarySelection == null) {
|
||
|
|
IDictionaryEnumerator selections = (IDictionaryEnumerator)selectionsByComponent.GetEnumerator();
|
||
|
|
bool valueFound = selections.MoveNext();
|
||
|
|
|
||
|
|
if (valueFound) {
|
||
|
|
primarySelection = (SampleSelectionItem)selections.Value;
|
||
|
|
primarySelection.Primary = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (primarySelection != null) {
|
||
|
|
return primarySelection.Component;
|
||
|
|
}
|
||
|
|
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Return the number of components we have selected.
|
||
|
|
int ISelectionService.SelectionCount {
|
||
|
|
get {
|
||
|
|
return selectionsByComponent.Count;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Events
|
||
|
|
public event EventHandler SelectionChanged;
|
||
|
|
public event EventHandler SelectionChanging;
|
||
|
|
|
||
|
|
// Methods
|
||
|
|
|
||
|
|
/// Check to see if the given component is in our selection group.
|
||
|
|
bool ISelectionService.GetComponentSelected(object component) {
|
||
|
|
return (component != null && null != selectionsByComponent[component]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Return our list of selected components.
|
||
|
|
ICollection ISelectionService.GetSelectedComponents() {
|
||
|
|
object[] sels = new object[selectionsByComponent.Values.Count];
|
||
|
|
selectionsByComponent.Values.CopyTo(sels, 0);
|
||
|
|
object[] objects = new object[sels.Length];
|
||
|
|
|
||
|
|
for (int i = 0; i < sels.Length; i++) {
|
||
|
|
objects[i] = ((SampleSelectionItem)sels[i]).Component;
|
||
|
|
}
|
||
|
|
|
||
|
|
return objects;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Select the given components.
|
||
|
|
void ISelectionService.SetSelectedComponents(ICollection components) {
|
||
|
|
((ISelectionService)this).SetSelectedComponents(components, SelectionTypes.Normal);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Select the given components with the given SelectionType.
|
||
|
|
void ISelectionService.SetSelectedComponents(ICollection components, SelectionTypes selectionType) {
|
||
|
|
bool fToggle = false;
|
||
|
|
bool fControl = false;
|
||
|
|
bool fClick = false;
|
||
|
|
bool fChanged = false; // did we actually change something?
|
||
|
|
|
||
|
|
// Catch a null input list
|
||
|
|
if (components == null){
|
||
|
|
components = new Component[0];
|
||
|
|
}
|
||
|
|
|
||
|
|
if ((selectionType & SelectionTypes.Normal) == SelectionTypes.Normal
|
||
|
|
|| (selectionType & SelectionTypes.Click) == SelectionTypes.Click) {
|
||
|
|
|
||
|
|
fControl = ((Control.ModifierKeys & Keys.Control) == Keys.Control);
|
||
|
|
|
||
|
|
// Only toggle when we are affecting a single control, and
|
||
|
|
// when we are handling the "mouse" state events (i.e. up/down
|
||
|
|
// used to show/hide the selection).
|
||
|
|
//
|
||
|
|
fToggle = ((Control.ModifierKeys & Keys.Control) != 0 || (Control.ModifierKeys & Keys.Shift) != 0)
|
||
|
|
&& components.Count == 1
|
||
|
|
&& (selectionType & SelectionTypes.MouseUp) != SelectionTypes.MouseUp;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ((selectionType & SelectionTypes.Click) == SelectionTypes.Click) {
|
||
|
|
fClick = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
// If we are replacing the selection, only remove the ones that are not in our new list.
|
||
|
|
// We also handle the special case here of having a singular component selected that's
|
||
|
|
// already selected. In this case we just move it to the primary selection.
|
||
|
|
//
|
||
|
|
if (!fToggle && !fControl) {
|
||
|
|
object firstSelection = null;
|
||
|
|
foreach(object o in components) {
|
||
|
|
firstSelection = o;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (fClick && 1 == components.Count && ((ISelectionService)this).GetComponentSelected(firstSelection)) {
|
||
|
|
SampleSelectionItem oldPrimary = primarySelection;
|
||
|
|
SetPrimarySelection((SampleSelectionItem)selectionsByComponent[firstSelection]);
|
||
|
|
if (oldPrimary != primarySelection) {
|
||
|
|
fChanged = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
SampleSelectionItem[] selections = new SampleSelectionItem[selectionsByComponent.Values.Count];
|
||
|
|
selectionsByComponent.Values.CopyTo(selections, 0);
|
||
|
|
|
||
|
|
// Even with several hundred components this should be fairly fast
|
||
|
|
foreach(SampleSelectionItem item in selections) {
|
||
|
|
bool remove = true;
|
||
|
|
|
||
|
|
foreach(object comp in components) {
|
||
|
|
if (comp == item.Component) {
|
||
|
|
remove = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (remove) {
|
||
|
|
RemoveSelection(item);
|
||
|
|
fChanged = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
SampleSelectionItem primarySel = null;
|
||
|
|
int selectedCount = selectionsByComponent.Count;
|
||
|
|
|
||
|
|
// Now do the selection.
|
||
|
|
foreach(Component comp in components) {
|
||
|
|
if (comp != null) {
|
||
|
|
SampleSelectionItem s = (SampleSelectionItem)selectionsByComponent[comp];
|
||
|
|
|
||
|
|
if (null == s) {
|
||
|
|
s = new SampleSelectionItem(this, comp);
|
||
|
|
AddSelection(s);
|
||
|
|
|
||
|
|
if (fControl || fToggle) {
|
||
|
|
primarySel = s;
|
||
|
|
}
|
||
|
|
|
||
|
|
fChanged = true;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
if (fToggle) {
|
||
|
|
// Remove the selection from our list.
|
||
|
|
RemoveSelection(s);
|
||
|
|
fChanged = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (primarySel != null) {
|
||
|
|
SetPrimarySelection(primarySel);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Now notify that our selection has changed
|
||
|
|
if (fChanged) {
|
||
|
|
OnSelectionChanged();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/// Adds the given selection to our selection list.
|
||
|
|
private void AddSelection(SampleSelectionItem sel) {
|
||
|
|
selectionsByComponent[sel.Component] = sel;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
private void DesignerHost_LoadComplete(object sender, EventArgs e) {
|
||
|
|
// Flush any pending changes
|
||
|
|
batchMode = false;
|
||
|
|
FlushSelectionChanges();
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/// Called by the designer host when it is entering or leaving a batch
|
||
|
|
/// operation. Here we queue up selection notification and we turn off
|
||
|
|
/// our UI.
|
||
|
|
private void DesignerHost_TransactionClosed(object sender, DesignerTransactionCloseEventArgs e) {
|
||
|
|
batchMode = false;
|
||
|
|
FlushSelectionChanges();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Called by the designer host when it is entering or leaving a batch
|
||
|
|
/// operation. Here we queue up selection notification and we turn off
|
||
|
|
/// our UI.
|
||
|
|
private void DesignerHost_TransactionOpened(object sender, EventArgs e) {
|
||
|
|
batchMode = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Called by the formcore when someone has added a component.
|
||
|
|
private void DesignerHost_ComponentAdd(object sender, ComponentEventArgs ce) {
|
||
|
|
OnSelectionContentsChanged();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Called when a component changes. Here we look to see if the component is
|
||
|
|
/// selected. If it is, we notify STrackSelection that the selection has changed.
|
||
|
|
private void DesignerHost_ComponentChanged(object sender, ComponentChangedEventArgs ce) {
|
||
|
|
if (selectionsByComponent[ce.Component] != null) {
|
||
|
|
OnSelectionContentsChanged();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Called by the formcore when someone has removed a component. This will
|
||
|
|
/// remove any selection on the component without disturbing the rest of
|
||
|
|
/// the selection.
|
||
|
|
private void DesignerHost_ComponentRemove(object sender, ComponentEventArgs ce) {
|
||
|
|
|
||
|
|
SampleSelectionItem sel = (SampleSelectionItem)selectionsByComponent[ce.Component];
|
||
|
|
|
||
|
|
if (sel != null) {
|
||
|
|
RemoveSelection(sel);
|
||
|
|
|
||
|
|
|
||
|
|
// If we removed the last selection and we have a designer host, then select the base
|
||
|
|
// component of the host. Otherwise, it is OK to have an empty selection.
|
||
|
|
if (selectionsByComponent.Count == 0 && host != null) {
|
||
|
|
|
||
|
|
// Now we have to run through all the components and pick
|
||
|
|
// the control with the highest z-order.
|
||
|
|
IComponent[] comps = new IComponent[host.Container.Components.Count];
|
||
|
|
host.Container.Components.CopyTo(comps, 0);
|
||
|
|
|
||
|
|
if (comps == null) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
int maxZOrder = -1;
|
||
|
|
int defaultIndex = -1;
|
||
|
|
object maxIndexComp = null;
|
||
|
|
object baseComp = host.RootComponent;
|
||
|
|
|
||
|
|
if (baseComp == null) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (int i = comps.Length - 1; i >= 0; i--) {
|
||
|
|
if (comps[i] == baseComp) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
else if (defaultIndex == -1) {
|
||
|
|
defaultIndex = i;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (comps[i] is Control) {
|
||
|
|
int zorder = ((Control)comps[i]).TabIndex;
|
||
|
|
if (zorder > maxZOrder) {
|
||
|
|
maxZOrder = zorder;
|
||
|
|
maxIndexComp = (object)comps[i];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (maxIndexComp == null) {
|
||
|
|
if (defaultIndex != -1) {
|
||
|
|
maxIndexComp = comps[defaultIndex];
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
maxIndexComp = baseComp;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
((ISelectionService)this).SetSelectedComponents(new object[]{maxIndexComp}, SelectionTypes.Replace);
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
OnSelectionChanged();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
// Component isn't selected, but our list of selectable components is
|
||
|
|
// different.
|
||
|
|
OnSelectionContentsChanged();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/// Disposes the entire selection manager.
|
||
|
|
internal void Dispose() {
|
||
|
|
// We've got to be careful here. We're one of the last things to go away when
|
||
|
|
// a form is being torn down, and we've got to be wary if things have pulled out
|
||
|
|
// already.
|
||
|
|
host.RemoveService(typeof(ISelectionService));
|
||
|
|
host.TransactionOpened -= new EventHandler(this.DesignerHost_TransactionOpened);
|
||
|
|
host.TransactionClosed -= new DesignerTransactionCloseEventHandler(this.DesignerHost_TransactionClosed);
|
||
|
|
if (host.InTransaction) {
|
||
|
|
DesignerHost_TransactionClosed(host, new DesignerTransactionCloseEventArgs(true));
|
||
|
|
}
|
||
|
|
host.LoadComplete -= new EventHandler(this.DesignerHost_LoadComplete);
|
||
|
|
|
||
|
|
IComponentChangeService cs = (IComponentChangeService)host.GetService(typeof(IComponentChangeService));
|
||
|
|
if (cs != null) {
|
||
|
|
cs.ComponentAdded -= new ComponentEventHandler(this.DesignerHost_ComponentAdd);
|
||
|
|
cs.ComponentRemoved -= new ComponentEventHandler(this.DesignerHost_ComponentRemove);
|
||
|
|
cs.ComponentChanged -= new ComponentChangedEventHandler(this.DesignerHost_ComponentChanged);
|
||
|
|
}
|
||
|
|
|
||
|
|
SampleSelectionItem[] sels = new SampleSelectionItem[selectionsByComponent.Values.Count];
|
||
|
|
selectionsByComponent.Values.CopyTo(sels, 0);
|
||
|
|
|
||
|
|
for (int i = 0; i < sels.Length; i++) {
|
||
|
|
sels[i].Dispose();
|
||
|
|
}
|
||
|
|
|
||
|
|
selectionsByComponent.Clear();
|
||
|
|
primarySelection = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/// Called when our visiblity or batch mode changes. Flushes
|
||
|
|
/// any pending notifications or updates if possible.
|
||
|
|
private void FlushSelectionChanges() {
|
||
|
|
if (!batchMode) {
|
||
|
|
if (selectionChanged) OnSelectionChanged();
|
||
|
|
if (selectionContentsChanged) OnSelectionContentsChanged();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/// Called anytime the selection has changed. We update our UI for the selection, and
|
||
|
|
/// then we fire a change event.
|
||
|
|
private void OnSelectionChanged() {
|
||
|
|
if (batchMode) {
|
||
|
|
selectionChanged = true;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
selectionChanged = false;
|
||
|
|
|
||
|
|
// Raise SelectionChanging event - this should be moved to OnSelectionChanging()
|
||
|
|
// if the implementation requires listening to changING events.
|
||
|
|
if (SelectionChanging != null) {
|
||
|
|
try {
|
||
|
|
SelectionChanging(this, EventArgs.Empty);;
|
||
|
|
}
|
||
|
|
catch(Exception) {
|
||
|
|
// It is an error to ever throw in this event.
|
||
|
|
Debug.Fail("Exception thrown in selectionChanging event");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (need_clear_selection)
|
||
|
|
{
|
||
|
|
need_clear_selection = false;
|
||
|
|
selectionsByComponent.Clear();
|
||
|
|
primarySelection = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (SelectionChanged != null) {
|
||
|
|
try {
|
||
|
|
SelectionChanged(this, EventArgs.Empty);;
|
||
|
|
}
|
||
|
|
catch(Exception) {
|
||
|
|
// It is an error to ever throw in this event.
|
||
|
|
Debug.Fail("Exception thrown in selectionChanging event");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
OnSelectionContentsChanged();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// This should be called when the selection has changed, or when just the contents of
|
||
|
|
/// the selection has changed. It updates the document manager's notion of selection.
|
||
|
|
/// the selection have changed.
|
||
|
|
private void OnSelectionContentsChanged() {
|
||
|
|
if (batchMode) {
|
||
|
|
selectionContentsChanged = true;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
selectionContentsChanged = false;
|
||
|
|
|
||
|
|
PropertyGrid grid = (PropertyGrid)host.GetService(typeof(PropertyGrid));
|
||
|
|
if (grid != null)
|
||
|
|
{
|
||
|
|
ICollection col = ((ISelectionService)this).GetSelectedComponents();
|
||
|
|
object[] selection = new Object[col.Count];
|
||
|
|
col.CopyTo(selection, 0);
|
||
|
|
grid.SelectedObjects = selection;
|
||
|
|
(host as SampleDesignerHost).SetSelectedComponents(selection);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Removes the given selection from our selection list
|
||
|
|
private void RemoveSelection(SampleSelectionItem s) {
|
||
|
|
selectionsByComponent.Remove(s.Component);
|
||
|
|
s.Dispose();
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/// Sets the given selection object to be the primary selection.
|
||
|
|
internal void SetPrimarySelection(SampleSelectionItem sel) {
|
||
|
|
if (sel != primarySelection) {
|
||
|
|
if (null != primarySelection) {
|
||
|
|
primarySelection.Primary = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
primarySelection = sel;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (null != primarySelection) {
|
||
|
|
primarySelection.Primary = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//internal void SetSelectedComponent(IComponent comp)
|
||
|
|
//{
|
||
|
|
// ((ISelectionService)this).SetSelectedComponents(new IComponent[] { comp });
|
||
|
|
//}
|
||
|
|
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|