Created
April 26, 2017 18:03
-
-
Save gro-ove/3de0d9fe36891e8463e8197f56cd0274 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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