Skip to content

Instantly share code, notes, and snippets.

@gro-ove
Created April 26, 2017 18:03
Show Gist options
  • Save gro-ove/3de0d9fe36891e8463e8197f56cd0274 to your computer and use it in GitHub Desktop.
Save gro-ove/3de0d9fe36891e8463e8197f56cd0274 to your computer and use it in GitHub Desktop.
public abstract class BaseSwitch : FrameworkElement {
public static readonly DependencyProperty ResetElementNameBindingsProperty = DependencyProperty.Register(nameof(ResetElementNameBindings), typeof(bool),
typeof(BaseSwitch), new FrameworkPropertyMetadata(false));
public bool ResetElementNameBindings {
get { return (bool)GetValue(ResetElementNameBindingsProperty); }
set { SetValue(ResetElementNameBindingsProperty, value); }
}
protected abstract UIElement GetChild();
private UIElement _child;
private void SetActiveChild(UIElement child) {
if (ReferenceEquals(_child, child)) return;
/* sometimes, different errors — such as “Cannot modify the logical children
for this node at this time because a tree walk is in progress.” — happen here */
RemoveVisualChild(_child);
RemoveLogicalChild(_child);
_child = child;
AddLogicalChild(_child);
AddVisualChild(_child);
if (ResetElementNameBindings) {
child.ResetElementNameBindings();
}
}
protected override IEnumerator LogicalChildren => _child == null ? EmptyEnumerator.Instance :
new SingleChildEnumerator(_child);
protected void UpdateActiveChild() {
SetActiveChild(GetChild());
}
protected static void OnChildDefiningPropertyChanged(object sender, DependencyPropertyChangedEventArgs e) {
var b = sender as BaseSwitch;
if (b == null) return;
b.UpdateActiveChild();
b.InvalidateMeasure();
b.InvalidateVisual();
}
protected override Size MeasureOverride(Size constraint) {
UpdateActiveChild();
var e = _child;
if (e == null) return Size.Empty;
e.Measure(constraint);
return e.DesiredSize;
}
protected override Size ArrangeOverride(Size arrangeBounds) {
_child?.Arrange(new Rect(arrangeBounds));
return arrangeBounds;
}
protected override int VisualChildrenCount => _child != null ? 1 : 0;
protected override Visual GetVisualChild(int index) {
var child = _child;
if (child == null || index != 0) throw new ArgumentOutOfRangeException(nameof(index));
return child;
}
protected static void OnWhenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var element = d as UIElement;
if (element != null) {
(VisualTreeHelper.GetParent(element) as BaseSwitch)?.UpdateActiveChild();
}
}
}
internal class EmptyEnumerator : IEnumerator {
private EmptyEnumerator() { }
public static IEnumerator Instance => _instance ?? (_instance = new EmptyEnumerator());
public void Reset() { }
public bool MoveNext() { return false; }
public object Current {
get { throw new InvalidOperationException(); }
}
private static IEnumerator _instance;
}
internal class SingleChildEnumerator : IEnumerator {
internal SingleChildEnumerator(object child) {
_child = child;
_count = child == null ? 0 : 1;
}
object IEnumerator.Current => _index == 0 ? _child : null;
bool IEnumerator.MoveNext() {
return ++_index < _count;
}
void IEnumerator.Reset() {
_index = -1;
}
private int _index = -1;
private readonly int _count;
private readonly object _child;
}
[ContentProperty(nameof(Children))]
public abstract class ListSwitch : BaseSwitch, IAddChild, IList {
protected readonly List<UIElement> UiElements = new List<UIElement>(2);
public IList Children => this;
public void CopyTo(Array array, int index) {
((IList)UiElements).CopyTo(array, index);
}
public virtual int Count => UiElements.Count;
public object SyncRoot => ((IList)UiElements).SyncRoot;
public bool IsSynchronized => ((IList)UiElements).IsSynchronized;
public void RemoveAt(int index) {
UiElements.RemoveAt(index);
}
public UIElement this[int index] {
get { return UiElements[index]; }
set {
var vc = UiElements;
if (!ReferenceEquals(vc[index], value)) {
vc[index] = value;
InvalidateMeasure();
}
}
}
public bool Contains(UIElement element) {
return UiElements.Contains(element);
}
public virtual void Clear() {
UiElements.Clear();
}
public int Add(UIElement element) {
InvalidateMeasure();
UiElements.Add(element);
return UiElements.Count;
}
public int IndexOf(UIElement element) {
return UiElements.IndexOf(element);
}
public void Insert(int index, UIElement element) {
InvalidateMeasure();
UiElements.Insert(index, element);
}
public void Remove(UIElement element) {
UiElements.Remove(element);
}
int IList.Add(object value) {
return Add((UIElement)value);
}
bool IList.Contains(object value) {
return Contains(value as UIElement);
}
int IList.IndexOf(object value) {
return IndexOf(value as UIElement);
}
void IList.Insert(int index, object value) {
Insert(index, (UIElement)value);
}
bool IList.IsFixedSize => false;
bool IList.IsReadOnly => false;
void IList.Remove(object value) {
Remove(value as UIElement);
}
object IList.this[int index] {
get { return this[index]; }
set { this[index] = (UIElement)value; }
}
IEnumerator IEnumerable.GetEnumerator() {
return UiElements.GetEnumerator();
}
void IAddChild.AddChild(object value) {
if (value == null) throw new ArgumentNullException(nameof(value));
var element = value as UIElement;
if (element == null) throw new ArgumentException("Only UIElement supported", nameof(value));
Add(element);
}
void IAddChild.AddText(string text) {
if (!string.IsNullOrWhiteSpace(text)) throw new NotSupportedException();
}
protected abstract bool TestChild(UIElement child);
protected override UIElement GetChild() {
return UiElements?.FirstOrDefault(TestChild);
}
}
public class Switch : ListSwitch {
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(object),
typeof(Switch), new FrameworkPropertyMetadata(null, OnChildDefiningPropertyChanged));
public object Value {
get { return GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static object GetWhen(DependencyObject obj) {
return obj.GetValue(WhenProperty);
}
public static void SetWhen(DependencyObject obj, object value) {
obj.SetValue(WhenProperty, value);
}
public static readonly DependencyProperty WhenProperty = DependencyProperty.RegisterAttached("When", typeof(object),
typeof(Switch), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsParentMeasure, OnWhenChanged));
protected override bool TestChild(UIElement child) {
return Value.XamlEquals(GetWhen(child));
}
protected override UIElement GetChild() {
return UiElements == null ? null : (UiElements.FirstOrDefault(TestChild) ??
UiElements.FirstOrDefault(x => x.GetValue(WhenProperty) == null));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment