I have a script accessing an instantiated game object of Vuforia from Hierarchy and adding a component to it:
IEnumerable<TrackableBehaviour> tbs = TrackerManager.Instance.GetStateManager().GetTrackableBehaviours();
foreach (TrackableBehaviour tb in tbs)
{
if (tb.TrackableName == "Fashion")
{
if (tb.name == "New Game Object")
{
// change generic name to include trackable name
tb.gameObject.name = ++counter + "DynamicImageTarget-" + tb.TrackableName;
// add additional script components for trackable
tb.gameObject.AddComponent<DefaultTrackableEventHandler>();
tb.gameObject.AddComponent<TurnOffBehaviour>();
Up to here it is working but at this point/line when i try to add
tb.gameObject.GetComponent<DefaultTrackableEventHandler>().OnTargetFound.AddListener(DressFound);
is throwing null exception in runtime
When I try
tb.gameObject.GetComponent<DefaultTrackableEventHandler>().OnTargetFound.RemoveAllListeners();
throwing null exception in runtime as well
However when i try
tb.gameObject.GetComponent<DefaultTrackableEventHandler>().enable = false;
it is working and disabling script component
void DressFound()
{
augmentation.SetActive(true);
}
Why i can't reach events on the script?
/*==============================================================================
Copyright (c) 2019 PTC Inc. All Rights Reserved.
Copyright (c) 2010-2014 Qualcomm Connected Experiences, Inc.
All Rights Reserved.
Confidential and Proprietary - Protected under copyright and other laws.
==============================================================================*/
using UnityEngine;
using UnityEngine.Events;
using Vuforia;
/// <summary>
/// A custom handler that implements the ITrackableEventHandler interface.
///
/// Changes made to this file could be overwritten when upgrading the Vuforia version.
/// When implementing custom event handler behavior, consider inheriting from this class instead.
/// </summary>
public class DefaultTrackableEventHandler : MonoBehaviour
{
public enum TrackingStatusFilter
{
Tracked,
Tracked_ExtendedTracked,
Tracked_ExtendedTracked_Limited
}
/// <summary>
/// A filter that can be set to either:
/// - Only consider a target if it's in view (TRACKED)
/// - Also consider the target if's outside of the view, but the environment is tracked (EXTENDED_TRACKED)
/// - Even consider the target if tracking is in LIMITED mode, e.g. the environment is just 3dof tracked.
/// </summary>
public TrackingStatusFilter StatusFilter = TrackingStatusFilter.Tracked_ExtendedTracked_Limited;
public UnityEvent OnTargetFound;
public UnityEvent OnTargetLost;
protected TrackableBehaviour mTrackableBehaviour;
protected TrackableBehaviour.Status m_PreviousStatus;
protected TrackableBehaviour.Status m_NewStatus;
protected bool m_CallbackReceivedOnce = false;
protected virtual void Start()
{
mTrackableBehaviour = GetComponent<TrackableBehaviour>();
if (mTrackableBehaviour)
{
mTrackableBehaviour.RegisterOnTrackableStatusChanged(OnTrackableStatusChanged);
}
}
protected virtual void OnDestroy()
{
if (mTrackableBehaviour)
{
mTrackableBehaviour.UnregisterOnTrackableStatusChanged(OnTrackableStatusChanged);
}
}
void OnTrackableStatusChanged(TrackableBehaviour.StatusChangeResult statusChangeResult)
{
m_PreviousStatus = statusChangeResult.PreviousStatus;
m_NewStatus = statusChangeResult.NewStatus;
Debug.LogFormat("Trackable {0} {1} -- {2}",
mTrackableBehaviour.TrackableName,
mTrackableBehaviour.CurrentStatus,
mTrackableBehaviour.CurrentStatusInfo);
HandleTrackableStatusChanged();
}
protected virtual void HandleTrackableStatusChanged()
{
if (!ShouldBeRendered(m_PreviousStatus) &&
ShouldBeRendered(m_NewStatus))
{
OnTrackingFound();
}
else if (ShouldBeRendered(m_PreviousStatus) &&
!ShouldBeRendered(m_NewStatus))
{
OnTrackingLost();
}
else
{
if (!m_CallbackReceivedOnce && !ShouldBeRendered(m_NewStatus))
{
// This is the first time we are receiving this callback, and the target is not visible yet.
// --> Hide the augmentation.
OnTrackingLost();
}
}
m_CallbackReceivedOnce = true;
}
protected bool ShouldBeRendered(TrackableBehaviour.Status status)
{
if (status == TrackableBehaviour.Status.DETECTED ||
status == TrackableBehaviour.Status.TRACKED)
{
// always render the augmentation when status is DETECTED or TRACKED, regardless of filter
return true;
}
if (StatusFilter == TrackingStatusFilter.Tracked_ExtendedTracked)
{
if (status == TrackableBehaviour.Status.EXTENDED_TRACKED)
{
// also return true if the target is extended tracked
return true;
}
}
if (StatusFilter == TrackingStatusFilter.Tracked_ExtendedTracked_Limited)
{
if (status == TrackableBehaviour.Status.EXTENDED_TRACKED ||
status == TrackableBehaviour.Status.LIMITED)
{
// in this mode, render the augmentation even if the target's tracking status is LIMITED.
// this is mainly recommended for Anchors.
return true;
}
}
return false;
}
protected virtual void OnTrackingFound()
{
if (mTrackableBehaviour)
{
var rendererComponents = mTrackableBehaviour.GetComponentsInChildren<Renderer>(true);
var colliderComponents = mTrackableBehaviour.GetComponentsInChildren<Collider>(true);
var canvasComponents = mTrackableBehaviour.GetComponentsInChildren<Canvas>(true);
// Enable rendering:
foreach (var component in rendererComponents)
component.enabled = true;
// Enable colliders:
foreach (var component in colliderComponents)
component.enabled = true;
// Enable canvas':
foreach (var component in canvasComponents)
component.enabled = true;
}
if (OnTargetFound != null)
OnTargetFound.Invoke();
}
protected virtual void OnTrackingLost()
{
if (mTrackableBehaviour)
{
var rendererComponents = mTrackableBehaviour.GetComponentsInChildren<Renderer>(true);
var colliderComponents = mTrackableBehaviour.GetComponentsInChildren<Collider>(true);
var canvasComponents = mTrackableBehaviour.GetComponentsInChildren<Canvas>(true);
// Disable rendering:
foreach (var component in rendererComponents)
component.enabled = false;
// Disable colliders:
foreach (var component in colliderComponents)
component.enabled = false;
// Disable canvas':
foreach (var component in canvasComponents)
component.enabled = false;
}
if (OnTargetLost != null)
OnTargetLost.Invoke();
}
}
The issue seems to be that both UnityEvents are not assigned in the case the component is attached on runtime via AddComponent.
Usually these are assigned to automatically by the Unity Inspector since they are serialized.
To be sure you could simply initialize them always using
public UnityEvent OnTargetFound = new UnityEvent();
public UnityEvent OnTargetLost = new UnityEvent();
Related
Checkboxes are currently unusable in the new toolkit, as you cannot bind functions to states.
There is a development commit on GitHub, but it's not usable yet, so I need a script to work around, without changing the toolkit.
Should be able to easily set starting state, and call functions on state change - which you currently can't.
Found a solution that works without changing the toolkit, and should work on newer versions.
using UnityEngine;
using Microsoft.MixedReality.Toolkit.UI;
using UnityEngine.Events;
[RequireComponent(typeof(Interactable))]
public class CheckBoxInteractableSwitch : MonoBehaviour
{
public bool startChecked = true;
public UnityEvent OnCheck;
public UnityEvent OnUncheck;
private Interactable interactable;
private int state = 1;
void Start()
{
interactable = GetComponent<Interactable>();
if (OnCheck == null)
OnCheck = new UnityEvent();
if (OnUncheck == null)
OnUncheck = new UnityEvent();
OnCheck.AddListener(Checked);
OnUncheck.AddListener(UnChecked);
//works with 2 dimensions only
if (startChecked)
{
if (interactable.GetDimensionIndex() == 0) interactable.IncreaseDimension();
}
else
{
if (interactable.GetDimensionIndex() == 1) interactable.IncreaseDimension();
}
}
void Update()
{
if (interactable == null) return;
//state check
if (state != interactable.GetDimensionIndex())
{
state = interactable.GetDimensionIndex();
if (state == 0) OnUncheck.Invoke();
if(state == 1) OnCheck.Invoke();
}
}
private void Checked()
{
}
private void UnChecked()
{
}
}
Works with checkboxes only (2 dimension), you can set the default state for the checkbox, and you can subscribe to states on state changes.
Now it's working fine in editor and in runtime for each door.
But I want to add a global public flag that will control all the doors at once in editor and in runtime. If I change the global flag to true all the doors will be locked and same if set to false.
The DoorsLockManager script:
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
[ExecuteInEditMode]
public class DoorsLockManager : MonoBehaviour
{
[HideInInspector]
public List<HoriDoorManager> Doors = new List<HoriDoorManager>();
private void Awake()
{
var doors = GameObject.FindGameObjectsWithTag("Door");
Doors = new HoriDoorManager[doors.Length].ToList();
for (int i = 0; i < doors.Length; i++)
{
Doors[i] = doors[i].GetComponent<HoriDoorManager>();
}
}
}
And the editor script:
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(DoorsLockManager))]
public class DoorsLockManagerEditor : Editor
{
private SerializedProperty _doors;
private void OnEnable()
{
_doors = serializedObject.FindProperty("Doors");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
for (int i = 0; i < _doors.arraySize; i++)
{
var door = _doors.GetArrayElementAtIndex(i);
// if door == null the script itself has an error since it can't even find the SerializedProperty
if (door == null)
{
EditorGUILayout.HelpBox("There was an error in the editor script!\nPlease check the log", MessageType.Error);
Debug.LogError("Couldn't get door property", target);
return;
}
if (door.objectReferenceValue == null) continue;
// FindPropertyRelative seems not to only work for MonoBehaviour classes
// so we have to use this hack around
var serializedDoor = new SerializedObject(door.objectReferenceValue);
// If it's public no worry anyway
// If it's private still works since we made it a SerializeField
var lockState = serializedDoor.FindProperty("doorLockState");
// Fetch current values into the serialized "copy"
serializedDoor.Update();
if (lockState == null)
{
EditorGUILayout.HelpBox("There was an error in the editor script!\nPlease check the log", MessageType.Error);
Debug.LogError("Couldn't get lockState property", target);
return;
}
// for the PropertyField there is
// no return value since it automatically uses
// the correct drawer for the according property
// and directly changes it's value
EditorGUILayout.PropertyField(lockState, new GUIContent("Door " + i + " Lockstate"));
// or alternatively
//lockState.boolValue = EditorGUILayout.Toggle("Door " + i + " Lockstate", lockState.boolValue);
// Write back changes, mark as dirty if changed
// and add a Undo history entry
serializedDoor.ApplyModifiedProperties();
}
}
}
Well, you could simply add one to the DoorsLockManager
[ExecuteInEditMode]
public class DoorsLockManager : MonoBehaviour
{
[HideInInspector]
public List<HoriDoorManager> Doors = new List<HoriDoorManager>();
// The global state
[SerializeField] private bool _globalLockState;
// During runtime use a property instead
public bool GlobalLockState
{
get { return _globalLockState; }
set
{
_globalLocakState = value;
// apply it to all doors
foreach(var door in Doors)
{
// now you would need it public again
// or use the public property you had there
Door.doorLockState = _globalLocakState;
}
}
}
private void Awake()
{
var doors = GameObject.FindGameObjectsWithTag("Door");
Doors = new HoriDoorManager[doors.Length].ToList();
for (int i = 0; i < doors.Length; i++)
{
Doors[i] = doors[i].GetComponent<HoriDoorManager>();
}
}
}
and in the editor make it "overwrite" all the other flags if changed:
[CustomEditor(typeof(DoorsLockManager))]
public class DoorsLockManagerEditor : Editor
{
private SerializedProperty _doors;
private SerializedProperty _globalLockState;
private bool shouldOverwrite;
private void OnEnable()
{
_doors = serializedObject.FindProperty("Doors");
_globalLockState = serializedObject.FindProperty("_globalLockState");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
shouldOverwrite = false;
// Begin a change check here
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(_globalLockState);
if(EditorGUI.EndChangeCheck())
{
// overwrite only once if changed
shouldOverwrite = true;
}
for (int i = 0; i < _doors.arraySize; i++)
{
var door = _doors.GetArrayElementAtIndex(i);
// if door == null the script itself has an error since it can't even find the SerializedProperty
if (door == null)
{
EditorGUILayout.HelpBox("There was an error in the editor script!\nPlease check the log", MessageType.Error);
Debug.LogError("Couldn't get door property", target);
return;
}
if (door.objectReferenceValue == null) continue;
var serializedDoor = new SerializedObject(door.objectReferenceValue);
var lockState = serializedDoor.FindProperty("doorLockState");
serializedDoor.Update();
if (lockState == null)
{
EditorGUILayout.HelpBox("There was an error in the editor script!\nPlease check the log", MessageType.Error);
Debug.LogError("Couldn't get lockState property", target);
return;
}
// HERE OVERWRITE
if(shouldOverwrite)
{
lockState.boolValue = _globalLockState.boolValue;
}
else
{
EditorGUILayout.PropertyField(lockState, new GUIContent("Door " + i + " Lockstate"));
}
serializedDoor.ApplyModifiedProperties();
}
serializedObject.ApplyModifiedProperties();
}
}
You could create a static class with the flag in it, and then have the door's "activated" state tied to it. Example:
public static class GlobalVariables()
{
public static bool DoorsLocked = true; //Doors would be locked dependent on logic
}
Then you just set the doors' state to the global variable in code. When you change the global bool, the doors change.
GlobalVariables.DoorsLocked = false; //unlocks all doors reading the global bool
Hope this helps!
I am working on an AR Android application. I finished building the application but whenever I open it, the video plays in the background before even scanning the target image but the video gets displayed only when I scan the image. Any help or suggestions about that? The code for DefaultTrackableEventHandler.cs is:
using UnityEngine;
using Vuforia;
public class DefaultTrackableEventHandler : MonoBehaviour, ITrackableEventHandler
{
protected TrackableBehaviour mTrackableBehaviour;
protected TrackableBehaviour.Status m_PreviousStatus;
protected TrackableBehaviour.Status m_NewStatus;
protected virtual void Start()
{
mTrackableBehaviour = GetComponent<TrackableBehaviour>();
if (mTrackableBehaviour)
mTrackableBehaviour.RegisterTrackableEventHandler(this);
}
protected virtual void OnDestroy()
{
if (mTrackableBehaviour)
mTrackableBehaviour.UnregisterTrackableEventHandler(this);
}
public void OnTrackableStateChanged(
TrackableBehaviour.Status previousStatus,
TrackableBehaviour.Status newStatus)
{
m_PreviousStatus = previousStatus;
m_NewStatus = newStatus;
if (newStatus == TrackableBehaviour.Status.DETECTED ||
newStatus == TrackableBehaviour.Status.TRACKED ||
newStatus == TrackableBehaviour.Status.EXTENDED_TRACKED)
{
Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " found");
OnTrackingFound();
}
else if (previousStatus == TrackableBehaviour.Status.TRACKED &&
newStatus == TrackableBehaviour.Status.NO_POSE)
{
Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " lost");
OnTrackingLost();
}
else
{
OnTrackingLost();
}
}
protected virtual void OnTrackingFound()
{
var rendererComponents = GetComponentsInChildren<Renderer>(true);
var colliderComponents = GetComponentsInChildren<Collider>(true);
var canvasComponents = GetComponentsInChildren<Canvas>(true);
foreach (var component in rendererComponents)
component.enabled = true;
foreach (var component in colliderComponents)
component.enabled = true;
foreach (var component in canvasComponents)
component.enabled = true;
}
protected virtual void OnTrackingLost()
{
var rendererComponents = GetComponentsInChildren<Renderer>(true);
var colliderComponents = GetComponentsInChildren<Collider>(true);
var canvasComponents = GetComponentsInChildren<Canvas>(true);
foreach (var component in rendererComponents)
component.enabled = false;
foreach (var component in colliderComponents)
component.enabled = false;
foreach (var component in canvasComponents)
component.enabled = false;
}
}
If you use the Unity Videoplayer check if you disabled the Loop option in Video Player Inspector view.
Second, the game object that include the VideoPlayer component is located as child of ImageTarget gameobject?
Third, in the OnTrackingFound() you must manage the Play and in OnTrackingLost() the Stop about VideoPlayer.
Hierarchy:
ARCamera
Image target
-Canvas
First start Canvas not show (it good)
Track Image target found, canvas show
but if track image target lost, canvas always show like stuck.
I was change script and see another tutorial
so, I am copy script from defaultTrackableEventHandler.cs to canvas.cs
this is script canvas.cs basic from DefaultTrackableEventHandler.cs
and my canvas name is "Information" (If Needed).
/*==============================================================================
Copyright (c) 2010-2014 Qualcomm Connected Experiences, Inc.
All Rights Reserved.
Confidential and Proprietary - Qualcomm Connected Experiences, Inc.
==============================================================================*/
using UnityEngine;
namespace Vuforia
{
/// <summary>
/// A custom handler that implements the ITrackableEventHandler interface.
/// </summary>
public class canvas : MonoBehaviour,
ITrackableEventHandler
{
#region PRIVATE_MEMBER_VARIABLES
private TrackableBehaviour mTrackableBehaviour;
#endregion // PRIVATE_MEMBER_VARIABLES
#region UNTIY_MONOBEHAVIOUR_METHODS
void Start()
{
mTrackableBehaviour = GetComponent<TrackableBehaviour>();
if (mTrackableBehaviour)
{
mTrackableBehaviour.RegisterTrackableEventHandler(this);
}
}
#endregion // UNTIY_MONOBEHAVIOUR_METHODS
#region PUBLIC_METHODS
/// <summary>
/// Implementation of the ITrackableEventHandler function called when the
/// tracking state changes.
/// </summary>
public void OnTrackableStateChanged(
TrackableBehaviour.Status previousStatus,
TrackableBehaviour.Status newStatus)
{
if (newStatus == TrackableBehaviour.Status.DETECTED ||
newStatus == TrackableBehaviour.Status.TRACKED ||
newStatus == TrackableBehaviour.Status.EXTENDED_TRACKED)
{
OnTrackingFound();
}
else
{
OnTrackingLost();
}
}
#endregion // PUBLIC_METHODS
#region PRIVATE_METHODS
private void OnTrackingFound()
{
Canvas[] canvasComponents = GetComponentsInChildren<Canvas>(true);
// Enable canvas objects
foreach (Canvas component in canvasComponents)
{
component.enabled = true;
}
}
private void OnTrackingLost()
{
Canvas[] canvasComponents = GetComponentsInChildren<Canvas>(true);
// Disable canvas objects
foreach (Canvas component in canvasComponents)
{
component.enabled = false;
}
}
#endregion // PRIVATE_METHODS
}
}
Go to DefaultTrackableEventHandler.cs under your Assests--> Vuforia--> Scripts folder.
You will find two methods namely, OnTrackingFound(),OnTrackingLost().
Under OnTrackingFound(), Add the below codes.
Canvas[] canvasComponents = GetComponentsInChildren<Canvas>(true);
//Enable canvas:
foreach(Canvas component in canvasComponents)
{
component.enabled=true;
}
Under OnTrackingLost(), add the below lines
Canvas[] canvasComponents = GetComponentsInChildren<Canvas>(true);
// Disable canvas:
foreach (Canvas component in canvasComponents)
{
component.enabled = false;
}
NOTE: Make sure your Canvas must be inside ImageTarget folder in project hierarchy!
That's it! You are set! Check with it!
I have got following problem:
There is a Ribbon-region in the Shell, let´s call it the "ShellRibbonRegion". There is also a task button region "ShellTaskButtonRegion" (similar to Creating View-Switching Applications with Prism 4).
There are e.g. three modules. Each module has a different number of RibbonTabItems. Module 1 has one RibbonTabItem, module 2 four and module 3 one.
The goal is now to add the RibbonTabItems to the "ShellRibbonRegion" after the "TaskButton" of a module is clicked. I already have written a custom RegionAdapter, but the problem is either only one RibbonTabItem (SingleActiveRegion) is shown or all (AllActiveRegion) RibbonTabItems from all modules.
public class RibbonRegionAdapter : RegionAdapterBase<Ribbon>
{
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="behaviorFactory">Allows the registration of the default set of RegionBehaviors.</param>
public RibbonRegionAdapter(IRegionBehaviorFactory behaviorFactory)
: base(behaviorFactory)
{
}
/// <summary>
/// Adapts a WPF control to serve as a Prism IRegion.
/// </summary>
/// <param name="region">The new region being used.</param>
/// <param name="regionTarget">The WPF control to adapt.</param>
protected override void Adapt(IRegion region, Ribbon regionTarget)
{
regionTarget.SelectedTabChanged += (sender, args) =>
{
if (regionTarget.SelectedTabItem == null)
return;
//region.Activate(regionTarget.SelectedTabItem);
};
region.Views.CollectionChanged += (sender, e) =>
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (UIElement element in e.NewItems)
{
if(element is Ribbon)
this.AddRibbon(element as Ribbon, regionTarget, region);
else if(element is RibbonTabItem)
this.AddRibbonTabItem(element as RibbonTabItem, regionTarget, region);
else if(element is Backstage)
this.AddBackstage(element as Backstage, regionTarget);
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (UIElement elementLoopVariable in e.OldItems)
{
var element = elementLoopVariable;
if (element is Ribbon)
this.RemoveRibbon(element as Ribbon, regionTarget);
else if (element is RibbonTabItem)
this.RemoveRibbonTabItem(element as RibbonTabItem, regionTarget);
else if (element is Backstage)
this.RemoveBackstage(element as Backstage, regionTarget);
}
break;
}
};
}
#region Add
private void AddRibbon(Ribbon ribbon, Ribbon targetRibbon, IRegion region)
{
//Add tabs
foreach (var ribbonTabItem in ribbon.Tabs)
{
this.AddRibbonTabItem(ribbonTabItem, targetRibbon, region);
}
}
private void AddRibbonTabItem(RibbonTabItem ribbonTabItem, Ribbon targetRibbon, IRegion region)
{
if (!targetRibbon.Tabs.Contains(ribbonTabItem))
targetRibbon.Tabs.Add(ribbonTabItem);
}
private void AddBackstage(Backstage backstage, Ribbon targetRibbon)
{
}
#endregion
#region Remove
private void RemoveRibbon(Ribbon ribbon, Ribbon targetRibbon)
{
var tmp = new List<RibbonTabItem>(ribbon.Tabs);
//Add tabs
foreach (var ribbonTabItem in tmp)
{
if (targetRibbon.Tabs.Contains(ribbonTabItem)) this.RemoveRibbonTabItem(ribbonTabItem, targetRibbon);
}
}
private void RemoveRibbonTabItem(RibbonTabItem ribbonTabItem, Ribbon targetRibbon)
{
if (ribbonTabItem is IRegionMemberLifetime)
{
var rml = (IRegionMemberLifetime)ribbonTabItem;
if (!rml.KeepAlive) targetRibbon.Tabs.Remove(ribbonTabItem);
return;
}
targetRibbon.Tabs.Remove(ribbonTabItem);
}
private void RemoveBackstage(Backstage backstage, Ribbon targetRibbon)
{
}
#endregion
protected override IRegion CreateRegion()
{
return new AllActiveRegion();
}
}
The desired behaviour is following:
The "TaskButton" of a module is clicked: All RibbonTabItems which don´t belong to this module are removed from the region and the tab items from "clicked" module are added.
How can I achieve this behaviour?
I had a similar problem and ended up keeping track of tabs added in a hashtable keyed by the module type name. The module tabs were developed in module views containing ribbon controls so the datacontext also needed to be transferred. Here is what I ended up with as my RibbonRegionAdapter (add your own namespace!):
// Based on
// http://www.codeproject.com/Articles/165370/Creating-View-Switching-Applications-with-Prism-4#AppendixA
// with my modifications.
/// <summary>
/// Enables use of a Ribbon control as a Prism region.
/// </summary>
/// <remarks> See Developer's Guide to Microsoft Prism (Ver. 4), p. 189.</remarks>
[Export]
public class RibbonRegionAdapter : RegionAdapterBase<Ribbon> {
private static readonly Hashtable RibbonTabs = new Hashtable();
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="behaviorFactory">Allows the registration
/// of the default set of RegionBehaviors.</param>
[ImportingConstructor]
public RibbonRegionAdapter(IRegionBehaviorFactory behaviorFactory)
: base(behaviorFactory) {}
/// <summary>
/// Adapts a WPF control to serve as a Prism IRegion.
/// </summary>
/// <param name="region">The new region being used.</param>
/// <param name="regionTarget">The WPF control to adapt.</param>
protected override void Adapt(IRegion region, Ribbon regionTarget) {
region.Views.CollectionChanged += (sender, e) => {
switch (e.Action) {
case NotifyCollectionChangedAction.Add:
foreach (FrameworkElement element in e.NewItems) {
if (element is Ribbon) {
Ribbon rb = element as Ribbon;
var tabList = new List<RibbonTab>();
var items = rb.Items;
for (int i = rb.Items.Count - 1; i >= 0; i--) {
if (!(rb.Items[i] is RibbonTab)) continue;
RibbonTab rt = (rb.Items[i] as RibbonTab);
rb.Items.Remove(rt); // remove from existing view ribbon
regionTarget.Items.Add(rt); // add to target region ribbon
tabList.Add(rt); // add to tracking list
// Without these next 3 lines the tabs datacontext would end up being inherited from the Ribbon to which
// it has been transferred.
// Not sure if this is the best place to do this but it works for my purposes at the moment
if (rt.DataContext.Equals(regionTarget.DataContext)) { // then it is inherited
rt.DataContext = rb.DataContext; // so set it explicitly to the original parent ribbons datacontext
}
}
// store tracking list in hashtable using string key (= the view type name)
var key = rb.GetType().Name;
RibbonTabs[key] = tabList;
} else if (element is RibbonTab) {
// the datacontext should already be set in these circumstances
regionTarget.Items.Add(element);
}
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (UIElement elementLoopVariable in e.OldItems) {
var element = elementLoopVariable;
if (element is Ribbon) {
Ribbon rb = element as Ribbon;
var key = rb.GetType().Name;
if (!RibbonTabs.ContainsKey(key)) continue; // no ribbon tabs have been tracked
var tabList = (RibbonTabs[key] as List<RibbonTab>) ?? new List<RibbonTab>();
foreach (RibbonTab rt in tabList)
{
if (!regionTarget.Items.Contains(rt)) continue; // this shouldn't happen
regionTarget.Items.Remove(rt); // remove from target region ribbon
rb.Items.Add(rt); // restore to view ribbon
}
RibbonTabs.Remove(key); // finished tracking so remove from hashtable
} else if (regionTarget.Items.Contains(element)) {
regionTarget.Items.Remove(element);
}
}
break;
}
};
}
protected override IRegion CreateRegion() {
return new SingleActiveRegion();
}
}