Calling parents by name - razor

Is there a way/plugin to call $parents["ExamViewModel"] instead of $parents[2]?
That would be very helpful for shared views that may show in different parent views.

No there is no such thing.
You could create your own method using instanceof as a cleaner way.
Something like:
function findParentOfType(parents, targetType) {
for (var i = 0; i < parents.length; i++) {
if (parents[i] instanceof targetType) return parents[i];
}
return null;
}
Usage:
findParentOfType($parents, ExamViewModel)
Example:
function Class1() {
this.name = 'MyClass1';
}
function Class2() {
this.name = 'MyClass2';
}
var parents = [new Class1(), new Class2()];
function findParentOfType(parents, targetType) {
for (var i = 0; i < parents.length; i++) {
if (parents[i] instanceof targetType) return parents[i];
}
return null;
}
console.log(findParentOfType(parents, Class2).name);

Related

Serializing and deserializing objects

I am trying to serialize and deserialize my objects to save and load data.
I thought I was smart and introduced Attributes:
[ExposedProperty]
public float Width { get; set; }
[ExposedProperty]
public Color HoverColor { get; set; }
I have a PropertyData class:
[System.Serializable]
public class PropertyData
{
public string Name;
public string Type;
public object Value;
public override string ToString()
{
return "PropertyData ( Name = " + Name + ", Type = " + Type + ", Value = " + Value + ")";
}
}
So instead of writing an ObjectData class for every Object class I have that gets serialized into JSON, I though I'd write a Serializable class that does:
public List<PropertyData> SerializeProperties()
{
var list = new List<PropertyData>();
var type = this.GetType();
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty)
.Where(p => p.GetCustomAttributes(typeof(ExposedPropertyAttribute), false).Length > 0)
.ToArray();//
for (int i = 0; i < properties.Length; i++)
{
var property = properties[i];
var data = new PropertyData();
data.Name = property.Name;
data.Type = property.PropertyType.Name;
data.Value = property.GetValue(this);
list.Add(data);
}
return list;
}
and also to deserialize:
protected void DeserializePropertyData(PropertyData data)
{
var p = this.GetType().GetProperty(data.Name);
if (p == null)
{
Debug.LogError("Item " + this + " does not have a property with name '" + data.Name + "'");
return;
}
var type = p.PropertyType;
try
{
//TODO do some magic here to deserialize any of the values.
TypeConverter typeConverter = TypeDescriptor.GetConverter(type);
object propValue = typeConverter.ConvertFromString(data.Value);
p.SetValue(this, propValue);
}
catch(FormatException fe)
{
Debug.Log($"Serializable, there was a format exception on property {data.Name} and value {data.Value} for type {type}");
}
catch(NotSupportedException nse)
{
Debug.Log($"Serializable, there was a not supported exception on property {data.Name} and value {data.Value} for type {type}");
}
finally
{
}
}
But as it turns out, I can't serialize or deserialize Color, or Vector3, or Quaternion, or whatever. It only works for bool, float, string, int...
Any ideas how to serialize/deserialize other objects properly?
Thanks for all the answers. I looked into the ISerializationSurrogate Interface and noticed that it can't be used for UnityEngine.Vector3 or UnityEngine.Color classes.
Then I looked into TypeConverter and also saw that you can't directly use it because you have to add the attribute [TypeConverter(typeof(CustomTypeConverter))] on top of the very class or struct you want to convert.
And at the end of the day, both TypeConverter and ISerializationSurrogate are simply pushing and parsing chars around to get the result. For a Vector3, you have to trim the "(", ")", split the string with a "," and perform float.Parse on every element of the split string array. For a Color, you have to trim "RGBA(" and ")", and do the exact same thing.
Deadline is tight, so I took the same principle and created a static class that does my converting:
public static class MyCustomTypeConverter
{
public static object ConvertFromString(string value, Type destinationType)
{
if (destinationType == typeof(string))
{
return value;
}
else if (destinationType == typeof(int))
{
return int.Parse(value);
}
else if (destinationType == typeof(float))
{
return float.Parse(value);
}
else if (destinationType == typeof(double))
{
return double.Parse(value);
}
else if (destinationType == typeof(long))
{
return long.Parse(value);
}
else if (destinationType == typeof(bool))
{
return bool.Parse(value);
}
else if (destinationType == typeof(Vector3))
{
var mid = value.Substring(1, value.Length - 2);
var values = mid.Split(",");
return new Vector3(
float.Parse(values[0], CultureInfo.InvariantCulture),
float.Parse(values[1], CultureInfo.InvariantCulture),
float.Parse(values[2], CultureInfo.InvariantCulture)
);
}
else if (destinationType == typeof(Vector4))
{
var mid = value.Substring(1, value.Length - 2);
var values = mid.Split(",");
return new Vector4(
float.Parse(values[0], CultureInfo.InvariantCulture),
float.Parse(values[1], CultureInfo.InvariantCulture),
float.Parse(values[2], CultureInfo.InvariantCulture),
float.Parse(values[3], CultureInfo.InvariantCulture)
);
}
else if (destinationType == typeof(Color))
{
var mid = value.Substring(5, value.Length - 6);
var values = mid.Split(",");
return new Color(
float.Parse(values[0], CultureInfo.InvariantCulture),
float.Parse(values[1], CultureInfo.InvariantCulture),
float.Parse(values[2], CultureInfo.InvariantCulture),
float.Parse(values[3], CultureInfo.InvariantCulture)
);
}
else if (destinationType == typeof(PrimitiveType))
{
var e = Enum.Parse<PrimitiveType>(value);
return e;
}
else if (destinationType == typeof(SupportedLightType))
{
var e = Enum.Parse<SupportedLightType>(value);
return e;
}
return null;
}
public static string ConvertToString(object value)
{
return value.ToString();
}
}
It works, does what I want, and I can extend it for further classes or structs. Quaternion or Bounds perhaps, but I don't need much more than that.
My Serialization class now does this:
public List<PropertyData> SerializeProperties()
{
var list = new List<PropertyData>();
var type = this.GetType();
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty)
.Where(p => p.GetCustomAttributes(typeof(ExposedPropertyAttribute), false).Length > 0)
.ToArray();//
for (int i = 0; i < properties.Length; i++)
{
var property = properties[i];
var data = new PropertyData();
data.Name = property.Name;
data.Value = MyCustomTypeConverter.ConvertToString(property.GetValue(this));
list.Add(data);
}
return list;
}
and that:
protected void DeserializePropertyData(PropertyData data)
{
var p = this.GetType().GetProperty(data.Name);
if (p == null)
{
return;
}
var type = p.PropertyType;
object propValue = MyCustomTypeConverter.ConvertFromString(data.Value, type);
p.SetValue(this, propValue);
}

Unable to serialize multi-dimensional array to JSON in dotnet core 3.1

When I try to serialize a multi-dimensional array using the new built-in JSON serializer in dotnet core 3.1, I get an error:
int[,] foo = new int[5, 5];
var json = System.Text.Json.JsonSerializer.Serialize(foo);
System.NotSupportedException: 'The collection type 'System.Int32[,]' is not supported.'
Is there a way to use a custom JSON converter for multi-dimensional arrays?
Like this:
int[][] foo = new int[][] { new []{1}, new [] {3, 4} };
var json = System.Text.Json.JsonSerializer.Serialize(foo);
Console.WriteLine(json); // [[1],[3,4]]
I have had implemented a partial solution for this issue.
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
public class MatrixConverter<T> : JsonConverter<T[,]>
{
public override T[,] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray)
{
throw new JsonException();
}
reader.Read();
if (reader.TokenType != JsonTokenType.StartArray)
{
throw new JsonException();
}
reader.Read();
var elements = new List<List<T>>();
var innerElements = new List<T>();
int? qttOfElementsInFirstLine = null;
while (reader.TokenType != JsonTokenType.EndArray)
{
while (reader.TokenType != JsonTokenType.EndArray)
{
innerElements.Add(JsonSerializer.Deserialize<T>(ref reader, options));
reader.Read();
}
if (!qttOfElementsInFirstLine.HasValue)
{
qttOfElementsInFirstLine = innerElements.Count;
}
if (innerElements.Count != qttOfElementsInFirstLine)
{
throw new JsonException("the matrix must have teh same qauantity of elements in each line");
}
elements.Add(innerElements);
reader.Read();
if (reader.TokenType != JsonTokenType.StartArray && reader.TokenType != JsonTokenType.EndArray)
{
throw new JsonException();
}
innerElements = new List<T>();
reader.Read();
}
var ret = new T[elements.Count, qttOfElementsInFirstLine.Value];
for (int i = 0; i < elements.Count; i++)
{
for (int j = 0; j < elements[i].Count; j++)
{
ret[i, j] = elements[i][j];
}
}
return ret;
}
public override void Write(Utf8JsonWriter writer, T[,] value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
The usage of the converter
var serializeOptions = new JsonSerializerOptions
{
WriteIndented = true,
Converters =
{
new MatrixConverter<double>()
}
};
var s = "[[1,2,3],[1,2,3]]";
var v = JsonSerializer.Deserialize<double[,]>(s, serializeOptions);

Easy way to write and read some Transform to a text file in Unity3d?

This is strictly in Unity3D, I have an array of 100 Transform,
I want to write those to a file on the PC desktop, and later read them.
Consider ...
// write to desktop...
string s = "";
for ( int i=0; i< sendMe.Length; ++i )
{
Transform tt = sendMe[i].transform;
s = s +tt.position.x +"," +tt.position.x +"," ...etc... "\n";
}
string d = System.Environment.GetFolderPath(
System.Environment.SpecialFolder.DesktopDirectory);
string path = System.IO.Path.Combine(d, "happyData.txt" );
System.IO.File.Delete(path);
System.IO.File.WriteAllText(path,s);
return;
.. and then read it similarly, just manually parsing the text format. Something like...
public ReadTrackFromDesktopFile()
{
GetSplineGetReady("development");
string tt = File.ReadAllText( .. path as above);
List<string> ts = new List<string>(
tt.Split(new string[] { "\r","\n" },
StringSplitOptions.RemoveEmptyEntries) );
foreach (string t in ts)
{
string[] parts = t.Split(',');
Vector3 pos = new Vector3(parts[0],parts[1],parts[2]);
Quaternion rot = new Quaternion(parts[3],parts[4],parts[5],parts[6]);
GetSplineOneNode(pos, rot);
}
GetSplineFinalise();
}
But this seems naive. What's the "simple Unity way" to do this?
You can use Json and the Playerprefs to save the Transform. This is a TransformSaver extension class that can do that:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public static class TransformSaver
{
[System.Serializable]
public class TransformInfo
{
public Vector3 pos;
public Quaternion rot;
public Vector3 scale;
}
//Save Transform
public static void SaveTransform(this Transform trans, Transform[] tranformToSave)
{
TransformInfo[] trnfrm = new TransformInfo[tranformToSave.Length];
for (int i = 0; i < trnfrm.Length; i++)
{
trnfrm[i] = new TransformInfo();
trnfrm[i].pos = tranformToSave[i].position;
trnfrm[i].rot = tranformToSave[i].rotation;
trnfrm[i].scale = tranformToSave[i].localScale;
}
string jsonTransform = JsonHelper.ToJson(trnfrm, true);
PlayerPrefs.SetString("transform", jsonTransform);
}
//Load Transform
public static Transform[] LoadTransform(this Transform trans)
{
string jsonTransform = PlayerPrefs.GetString("transform");
if (jsonTransform == null)
{
return null;
}
//Debug.Log("Loaded: " + jsonTransform);
TransformInfo[] savedTransforms = JsonHelper.FromJson<TransformInfo>(jsonTransform);
GameObject[] gameObjects = new GameObject[savedTransforms.Length];
Transform[] loadedTransforms = new Transform[savedTransforms.Length];
for (int i = 0; i < gameObjects.Length; i++)
{
gameObjects[i] = new GameObject("_");
gameObjects[i].hideFlags = HideFlags.HideAndDontSave;
loadedTransforms[i] = gameObjects[i].transform;
loadedTransforms[i].position = savedTransforms[i].pos;
loadedTransforms[i].rotation = savedTransforms[i].rot;
loadedTransforms[i].localScale = savedTransforms[i].scale;
}
return loadedTransforms;
}
public static void CopyTransform(this Transform trans, Transform source, Transform target, bool createNewInstance = false)
{
if (source == null)
{
return;
}
if (target == null || createNewInstance)
{
GameObject obj = new GameObject("_");
obj.hideFlags = HideFlags.HideAndDontSave;
target = obj.transform;
}
target.position = source.position;
target.rotation = source.rotation;
target.localScale = source.localScale;
}
public static void CopyTransform(this Transform trans, Transform[] source, Transform[] target, bool createNewInstance = false)
{
if (source == null || source.Length <= 0)
{
return;
}
for (int i = 0; i < target.Length; i++)
{
CopyTransform(null, source[i], target[i], createNewInstance);
if (i >= target.Length - 1)
{
break;
}
}
}
}
JsonHelper script for converting arrays to json and vice versa:
using UnityEngine;
using System.Collections;
using System;
public class JsonHelper
{
public static T[] FromJson<T>(string json)
{
Wrapper<T> wrapper = UnityEngine.JsonUtility.FromJson<Wrapper<T>>(json);
return wrapper.Items;
}
public static string ToJson<T>(T[] array, bool prettyPrint)
{
Wrapper<T> wrapper = new Wrapper<T>();
wrapper.Items = array;
return UnityEngine.JsonUtility.ToJson(wrapper, prettyPrint);
}
[Serializable]
private class Wrapper<T>
{
public T[] Items;
}
}
Usage:
using UnityEngine;
using System.Collections;
public class TransformTest : MonoBehaviour
{
public Transform[] objectToSave;
// Use this for initialization
void Start()
{
//Save Transform
transform.SaveTransform(objectToSave);
//Load Transform
Transform[] loadedTransform = transform.LoadTransform();
transform.CopyTransform(loadedTransform, objectToSave);
}
}
It's not perfect and can be improved. Improvement includes making the LoadTransform function take transform array as parameter instead of returning an array.
You can use Binary Serialization.
Create following structs:
[Serializable]
public struct SerializebleVector
{
public float x, y, z, w;
public SerializebleVector(float x, float y, float z, float w = 0f)
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
public static explicit operator SerializebleVector(Quaternion a)
{
return new SerializebleVector(a.x, a.y, a.z, a.w);
}
public static implicit operator SerializebleVector(Vector3 a)
{
return new SerializebleVector(a.x, a.y, a.z);
}
}
[Serializable]
public struct SerializebleTransform
{
SerializebleVector position;
SerializebleVector rotation;
SerializebleVector scale;
public SerializebleTransform(Transform tr)
{
position = tr.position;
rotation = (SerializebleVector) tr.rotation;
scale = tr.lossyScale;
}
public static implicit operator SerializebleTransform(Transform tr)
{
return new SerializebleTransform(tr);
}
}
Save method:
public static void Save(Transform[] ts )
{
var data = new List<SerializebleTransform>();
foreach (var t in ts)
{
data.Add(t);
}
BinaryFormatter bf = new BinaryFormatter();
using (FileStream file = File.Create (Application.persistentDataPath + "/savedTransforms.dat"))
{
bf.Serialize(file, data);
}
}
Load Method:
public static void Load(out List<SerializebleTransform> ts)
{
if(File.Exists(Application.persistentDataPath + "/savedTransforms.dat"))
{
BinaryFormatter bf = new BinaryFormatter();
using (FileStream file = File.Open(Application.persistentDataPath + "/savedTransforms.dat", FileMode.Open))
{
var data = (List<SerializebleTransform>)bf.Deserialize(file);
return data;
}
}
else
ts = null;
}
PS: As far as I know you can't create Transform without GameObject, so read data from SerializebleTransform into Transform kind of manually.

Metro App CollectionViewSource ObservableCollection Filter

It appears that filtering an ObservableCollection with CollectionViewSource is not possible in WinRT:
See here!
I can filter using LINQ, but how do I get the UI to update if changes that affect the filtered data are made?
I ended up writing my own class to achieve the desired effect:
public class ObservableCollectionView<T> : ObservableCollection<T>
{
private ObservableCollection<T> _view;
private Predicate<T> _filter;
public ObservableCollectionView(IComparer<T> comparer)
: base(comparer)
{
}
public ObservableCollectionView(IComparer<T> comparer, IEnumerable<T> collection)
: base(comparer, collection)
{
}
public ObservableCollectionView(IComparer<T> comparer, IEnumerable<T> collection, Predicate<T> filter)
: base(comparer, collection == null ? new T[] { } : collection)
{
if (filter != null)
{
_filter = filter;
if (collection == null)
_view = new ObservableCollection<T>(comparer);
else
_view = new ObservableCollection<T>(comparer, collection);
}
}
public ObservableCollection<T> View
{
get
{
return (_filter == null ? this : _view);
}
}
public Predicate<T> Filter
{
get
{
return _filter;
}
set
{
if (value == null)
{
_filter = null;
_view = new ObservableCollection<T>(Comparer);
}
else
{
_filter = value;
Fill();
}
}
}
private void Fill()
{
_view = new ObservableCollection<T>(Comparer);
foreach (T item in this)
{
if (Filter(item))
View.Add(item);
}
}
private int this[T item]
{
get
{
int foundIndex = -1;
for (int index = 0; index < View.Count; index++)
{
if (View[index].Equals(item))
{
foundIndex = index;
break;
}
}
return foundIndex;
}
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
if (_filter != null)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (T item in e.NewItems)
if (Filter(item))
View.Add(item);
break;
case NotifyCollectionChangedAction.Move:
break;
case NotifyCollectionChangedAction.Remove:
foreach (T item in e.OldItems)
if (Filter(item))
View.Remove(item);
break;
case NotifyCollectionChangedAction.Replace:
for (int index = 0; index < e.OldItems.Count; index++)
{
T item = (T)e.OldItems[index];
if (Filter(item))
{
int foundIndex = this[item];
if (foundIndex != -1)
View[foundIndex] = (T)e.NewItems[index];
}
}
break;
case NotifyCollectionChangedAction.Reset:
Fill();
break;
}
}
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (_filter != null)
{
// TODO: Implement code for property changes
}
}
}
Not yet perfect. So improvements/suggestions welcomed.
You can now bind this object, directly to a control using the View property.
You need to make sure the filtering changes are observable, so you can set the source of the CollectionViewSource to an ObservableCollection and make the changes on that collection or assign a new Source of the CVS to a new, filtered collection.

Save/ load QuickAccessToolBar

I 'm using Microsoft.Windows.Controls.Ribbon, and i would like save and load QuickAccessToolBar.
How I Can find QuickAccessToolBar items, in ribbon control?
This is my idea to save ribbon QuickAccessToolBar
OnClosing application : Get QuickAccessToolBar items, and locaize on Ribbon items. And then serialize
OnLoad : Read serilize object, and add insert items QuickAccessToolBar
Finally i did it!
I'm based on WPF Ribbon Sample to save/load. It used datacontext to localize control on RibbonControl, and i didn't like, so i isued QuickAccessToolBarId to localize the control.
This is the solution:
- OnClosing Application:
I save QuickAccessToolBar, search items on ribbon control by QuickAccessToolBar, and save path. Note: When item is in application menufirst path is -1.A
- OnLoadApplication:
Load items , and seerch on ribbon control by path.
And this is the code:
RibbonHelper:
class RibbonHelper
{
Ribbon ribbon;
public RibbonHelper(Ribbon ribbon)
{
this.ribbon = ribbon;
}
string _qatFileName = "qat.xml";
internal void Save()
{
try
{
SaveQatItems();
}
catch { }
}
internal void Load()
{
try
{
LoadQatItems();
}
catch
{
}
}
#region Save Qat
private void SaveQatItems()
{
QatItemCollection qatItems = GetQatItemsToSave(ribbon.QuickAccessToolBar);
if (qatItems != null)
{
QatItemCollection remainingItems = new QatItemCollection();
remainingItems.AddRange(qatItems);
SaveQatItemsOfApplicationMenu(remainingItems);
SaveQatItemsOfTabs(remainingItems);
SaveQatItems(qatItems);
}
}
private void SaveQatItems(QatItemCollection qatItems)
{
XmlWriter xmlWriter = XmlWriter.Create(_qatFileName);
XamlWriter.Save(qatItems, xmlWriter);
xmlWriter.Close();
}
private void SaveQatItemsOfApplicationMenu(QatItemCollection remainingItems)
{
if (ribbon.ApplicationMenu != null)
{
if (remainingItems != null)
{
remainingItems.ForEach(qat =>
{
qat.ControlIndices.Add(-1);
});
for (int index = 0; index < ribbon.ApplicationMenu.Items.Count && remainingItems.Count > 0; index++)
{
SaveQatItemsAmongChildren(remainingItems, ribbon.ApplicationMenu.Items[index],index);
}
remainingItems.ForEach(qat =>
{
qat.ControlIndices.Clear();
});
}
}
}
private void SaveQatItemsOfTabs(QatItemCollection remainingItems)
{
if (ribbon.Items != null)
{
if (remainingItems != null)
{
for (int tabIndex = 0; tabIndex < ribbon.Items.Count && remainingItems.Count > 0; tabIndex++)
{
RibbonTab tab = ribbon.Items[tabIndex] as RibbonTab;
SaveQatItemsAmongChildren(remainingItems, tab, tabIndex);
}
}
}
}
private void SaveQatItemsAmongChildren(QatItemCollection remainingItems, object control, int controlIndex)
{
if (control == null)
{
return;
}
//AƱaidmos el control index a los pendientes
remainingItems.ForEach(qat =>
{
qat.ControlIndices.Add(controlIndex);
});
SaveQatItemsAmongChildrenInner(remainingItems, control);
//Eliminamos el control index de los pendientes ya que no estan dentro de ese control
remainingItems.ForEach(qat =>
{
int last = qat.ControlIndices.Count - 1;
qat.ControlIndices.RemoveAt(last);
});
}
private void SaveQatItemsAmongChildrenInner(QatItemCollection remainingItems, object parent)
{
SaveQatItemsIfMatchesControl(remainingItems, parent);
if (remainingItems.Count == 0 || IsLeaf(parent))
{
return;
}
int childIndex = 0;
DependencyObject dependencyObject = parent as DependencyObject;
if (dependencyObject != null)
{
IEnumerable children = LogicalTreeHelper.GetChildren(dependencyObject);
foreach (object child in children)
{
SaveQatItemsAmongChildren(remainingItems, child, childIndex);
childIndex++;
}
}
if (childIndex != 0)
{
return;
}
// if we failed to get any logical children, enumerate the visual ones
Visual visual = parent as Visual;
if (visual == null)
{
return;
}
for (childIndex = 0; childIndex < VisualTreeHelper.GetChildrenCount(visual); childIndex++)
{
Visual child = VisualTreeHelper.GetChild(visual, childIndex) as Visual;
SaveQatItemsAmongChildren(remainingItems, child, childIndex);
}
}
private bool IsLeaf(object element)
{
if ((element is RibbonButton) ||
(element is RibbonToggleButton) ||
(element is RibbonRadioButton) ||
(element is RibbonCheckBox) ||
(element is RibbonTextBox) ||
(element is RibbonSeparator))
{
return true;
}
RibbonMenuItem menuItem = element as RibbonMenuItem;
if (menuItem != null &&
menuItem.Items.Count == 0)
{
return true;
}
return false;
}
private bool SaveQatItemsIfMatchesControl(QatItemCollection remainingItems, object control)
{
bool matched = false;
FrameworkElement element = control as FrameworkElement;
if (element != null)
{
object getQuickAccessToolBarId = RibbonControlService.GetQuickAccessToolBarId(element);
if (getQuickAccessToolBarId != null)
{
int remove = remainingItems.RemoveAll(qat => qat.QuickAccessToolBarIdHashCode == getQuickAccessToolBarId.GetHashCode());
matched = remove > 0;
}
}
return matched;
}
private QatItemCollection GetQatItemsToSave(RibbonQuickAccessToolBar qat)
{
QatItemCollection qatItems = new QatItemCollection();
if (qat != null && qat.Items != null && qat.Items.Count > 0)
{
foreach (var item in qat.Items)
{
FrameworkElement element = item as FrameworkElement;
if (element != null)
{
object getQuickAccessToolBarId = RibbonControlService.GetQuickAccessToolBarId(element);
if (getQuickAccessToolBarId != null)
{
QatItem qatItem = new QatItem(getQuickAccessToolBarId.GetHashCode());
qatItems.Add(qatItem);
}
}
}
}
return qatItems;
}
#endregion
#region load qat
private void LoadQatItems()
{
if (ribbon.QuickAccessToolBar == null)
{
ribbon.QuickAccessToolBar = new RibbonQuickAccessToolBar();
}
QatItemCollection qatItems = GetQatItemsToLoad();
if ((qatItems != null) && (qatItems.Count > 0))
{
SearchInApplicationMenu(qatItems);
SearchInTabs(qatItems);
qatItems.Where(qat => qat.Owner != null).ToList().ForEach(qat =>
{
if (RibbonCommands.AddToQuickAccessToolBarCommand.CanExecute(null, qat.Owner))
{
RibbonCommands.AddToQuickAccessToolBarCommand.Execute(null, qat.Owner);
}
});
}
}
private void SearchInApplicationMenu(QatItemCollection qatItems)
{
if (qatItems != null)
{
int remainingItemsCount = qatItems.Count(qat=>qat.Owner==null);
QatItemCollection matchedItems = new QatItemCollection();
for (int index = 0; index < ribbon.ApplicationMenu.Items.Count && remainingItemsCount > 0; index++)
{
matchedItems.Clear();
matchedItems.AddRange(qatItems.Where(qat => qat.ControlIndices[0] == -1)); //-1 is applicationMenu
//remove -1
matchedItems.ForEach(qat =>
{
qat.ControlIndices.RemoveAt(0);
}
);
object item = ribbon.ApplicationMenu.Items[index];
if (item != null)
{
if (!IsLeaf(item))
{
LoadQatItemsAmongChildren(matchedItems, 0, index, item, ref remainingItemsCount);
}
else
{
LoadQatItemIfMatchesControl(matchedItems, new QatItemCollection(), 0, index, item, ref remainingItemsCount);
}
}
//Add -1
matchedItems.ForEach(qat =>
{
qat.ControlIndices.Insert(0, -1);
});
}
}
}
private void SearchInTabs(QatItemCollection qatItems)
{
int remainingItemsCount = qatItems.Count(qat => qat.Owner == null);
QatItemCollection matchedItems = new QatItemCollection();
for (int tabIndex = 0; tabIndex < ribbon.Items.Count && remainingItemsCount > 0; tabIndex++)
{
matchedItems.Clear();
matchedItems.AddRange(qatItems.Where(qat => qat.ControlIndices[0] == tabIndex));
RibbonTab tab = ribbon.Items[tabIndex] as RibbonTab;
if (tab != null)
{
LoadQatItemsAmongChildren(matchedItems, 0, tabIndex, tab, ref remainingItemsCount);
}
}
}
private void LoadQatItemsAmongChildren(
QatItemCollection previouslyMatchedItems,
int matchLevel,
int controlIndex,
object parent,
ref int remainingItemsCount)
{
if (previouslyMatchedItems.Count == 0)
{
return;
}
if (IsLeaf(parent))
{
return;
}
int childIndex = 0;
DependencyObject dependencyObject = parent as DependencyObject;
if (dependencyObject != null)
{
IEnumerable children = LogicalTreeHelper.GetChildren(dependencyObject);
foreach (object child in children)
{
if (remainingItemsCount == 0)
{
break;
}
QatItemCollection matchedItems = new QatItemCollection();
LoadQatItemIfMatchesControl(previouslyMatchedItems, matchedItems, matchLevel + 1, childIndex, child, ref remainingItemsCount);
LoadQatItemsAmongChildren(matchedItems, matchLevel + 1, childIndex, child, ref remainingItemsCount);
childIndex++;
}
}
if (childIndex != 0)
{
return;
}
// if we failed to get any logical children, enumerate the visual ones
Visual visual = parent as Visual;
if (visual == null)
{
return;
}
for (childIndex = 0; childIndex < VisualTreeHelper.GetChildrenCount(visual); childIndex++)
{
if (remainingItemsCount == 0)
{
break;
}
Visual child = VisualTreeHelper.GetChild(visual, childIndex) as Visual;
QatItemCollection matchedItems = new QatItemCollection();
LoadQatItemIfMatchesControl(previouslyMatchedItems, matchedItems, matchLevel + 1, childIndex, child, ref remainingItemsCount);
LoadQatItemsAmongChildren(matchedItems, matchLevel + 1, childIndex, child, ref remainingItemsCount);
}
}
private void LoadQatItemIfMatchesControl(
QatItemCollection previouslyMatchedItems,
QatItemCollection matchedItems,
int matchLevel,
int controlIndex,
object control,
ref int remainingItemsCount)
{
for (int qatIndex = 0; qatIndex < previouslyMatchedItems.Count; qatIndex++)
{
QatItem qatItem = previouslyMatchedItems[qatIndex];
if (qatItem.ControlIndices[matchLevel] == controlIndex)
{
if (qatItem.ControlIndices.Count == matchLevel + 1)
{
qatItem.Owner = control as Control; ;
remainingItemsCount--;
}
else
{
matchedItems.Add(qatItem);
}
}
}
}
private QatItemCollection GetQatItemsToLoad()
{
try
{
if (File.Exists(_qatFileName))
{
using (XmlReader xmlReader = XmlReader.Create(_qatFileName))
{
QatItemCollection qatItems = (QatItemCollection)XamlReader.Load(xmlReader);
xmlReader.Close();
return qatItems;
}
}
}
catch
{
//Do nothing, return null;
}
return null;
}
#endregion
}
Classes to serialize:
public class QatItem {
public QatItem()
{
}
public QatItem(int qatbIdHashCode)
{
QuickAccessToolBarIdHashCode = qatbIdHashCode;
}
public Int32Collection ControlIndices
{
get
{
if (_controlIndices == null)
{
_controlIndices = new Int32Collection();
}
return _controlIndices;
}
set
{
_controlIndices = value;
}
}
Int32Collection _controlIndices;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int QuickAccessToolBarIdHashCode { get; set; }
//Is only for load
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Control Owner { get; set; }
}
public class QatItemCollection : List<QatItem>
{
}
And this is the code to save/load from window
RibbonHelper helper;
protected override void OnClosing(CancelEventArgs e)
{
helper.Save();
base.OnClosing(e);
}
private void RibbonWindow_Loaded(object sender, RoutedEventArgs e)
{
helper = new RibbonHelper(ribbon);
helper.Load();
}