Last active
December 16, 2019 16:56
-
-
Save StevePotter/b17f8d4b2657a2d2610390a11fb57e03 to your computer and use it in GitHub Desktop.
A WPF control that creates its content based on the value of some property in DataContext. This is great when you have a property like "Status" and want to display a particular control for each status.
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
using System; | |
using System.Collections.Generic; | |
using System.Windows; | |
using System.Windows.Controls; | |
using System.Windows.Data; | |
using System.Windows.Markup; | |
namespace Wpf | |
{ | |
/// <summary> | |
/// Depending on the value of some property of DataContext, this will create the appropriate child view. Views are only created on demand. | |
/// </summary> | |
/// <remarks> | |
/// Todo: use data trigger to activate the proper view, which will also handle type conversion. | |
/// </remarks> | |
[ContentProperty("Possibilities")] | |
public class ConditionalView: Decorator | |
{ | |
public ConditionalView() | |
{ | |
this.DataContextChanged += ConditionalView_DataContextChanged; | |
Possibilities.CollectionChanged += Possibilities_CollectionChanged; | |
} | |
private void Possibilities_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) | |
{ | |
UpdateChild(); | |
} | |
private void ConditionalView_DataContextChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e) | |
{ | |
if (e.NewValue != null) | |
{ | |
CreateBindingIfNecessary(); | |
} | |
} | |
private void CreateBindingIfNecessary() | |
{ | |
var binding = BindingOperations.GetBinding(this, PropertyValueProperty); | |
if (binding != null && (binding.Source != DataContext || binding.Path.Path != Property)) | |
{ | |
BindingOperations.ClearBinding(this, PropertyValueProperty); | |
} | |
if (DataContext != null && Property.HasChars()) | |
{ | |
binding = new Binding(); | |
binding.Source = DataContext; | |
binding.Path = new PropertyPath(Property); | |
binding.Mode = BindingMode.OneWay; | |
BindingOperations.SetBinding(this, PropertyValueProperty, binding); | |
} | |
} | |
/// <summary> | |
/// The name of the property on the DataContext to monitor and create the proper view for. | |
/// </summary> | |
public string Property | |
{ | |
get { return (string)GetValue(PropertyProperty); } | |
set { SetValue(PropertyProperty, value); } | |
} | |
// Using a DependencyProperty as the backing store for Property. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty PropertyProperty = | |
DependencyProperty.Register("Property", typeof(string), typeof(ConditionalView), new PropertyMetadata(OnPropertyNameValueChanged)); | |
public static void OnPropertyNameValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) | |
{ | |
var control = (ConditionalView)d; | |
control.CreateBindingIfNecessary(); | |
control.UpdateChild(); | |
} | |
protected object PropertyValue | |
{ | |
get { return (object)GetValue(PropertyValueProperty); } | |
set { SetValue(PropertyValueProperty, value); } | |
} | |
// Using a DependencyProperty as the backing store for PropertyValue. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty PropertyValueProperty = | |
DependencyProperty.Register("PropertyValue", typeof(object), typeof(ConditionalView), new PropertyMetadata(OnPropertyValueChanged)); | |
public static void OnPropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) | |
{ | |
var control = (ConditionalView)d; | |
control.UpdateChild(); | |
} | |
protected void UpdateChild() | |
{ | |
foreach(var possiblity in Possibilities) | |
{ | |
if (DoesPossiblityEqualValue(PropertyValue, possiblity.Value)) | |
{ | |
if (possiblity.Template == null) | |
throw new Exception("Possibility missing its Template"); | |
var o = possiblity.LoadContent() as UIElement; | |
if (o == null) | |
throw new Exception("Possibility Template must return UIElement as its root."); | |
Child = o; | |
return; | |
} | |
} | |
Child = null; | |
} | |
public ObservableCollection<PossibleView> Possibilities | |
{ | |
get | |
{ | |
return _Possibilities.Value; | |
} | |
} | |
private Lazy<ObservableCollection<PossibleView>> _Possibilities = new Lazy<ObservableCollection<PossibleView>>(); | |
public static bool DoesPossiblityEqualValue(object value, object possiblity) | |
{ | |
bool xIsNull = value == null; | |
bool yIsNull = possiblity == null; | |
if (xIsNull || yIsNull) | |
return (xIsNull && yIsNull); | |
if (Object.Equals(value, possiblity)) | |
{ | |
return true; | |
} | |
Type valueType = value.GetType(); | |
Type possiblityType = possiblity.GetType(); | |
try | |
{ | |
if (valueType.IsEnum) | |
{ | |
return value.Equals(Enum.Parse(valueType, possiblity.ToString()));//for some reason without the int cast it wouldn't work | |
} | |
if (value is int) | |
{ | |
return Convert.ToInt32(possiblity) == (int)value; | |
} | |
if (value is double) | |
{ | |
return Convert.ToDouble(possiblity) == (double)value; | |
} | |
if (value is bool) | |
{ | |
return Convert.ToBoolean(possiblity) == (bool)value; | |
} | |
return string.Equals(value.ToString(), possiblity.ToString(), StringComparison.Ordinal); | |
} | |
catch (Exception) | |
{ | |
return string.Equals(value.ToString(), possiblity.ToString(), StringComparison.Ordinal); | |
} | |
} | |
} | |
public class PossibleView: ControlTemplate | |
{ | |
public object Value { get; set; } | |
} | |
} |
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
<myWpf:ConditionalView Property="Status"> | |
<myWpf:PossibleView Value="Initializing"> | |
<TextBlock Text="Initializing"></TextBlock> | |
</myWpf:PossibleView> | |
<myWpf:PossibleView Value="ReadyToTest"> | |
<TextBlock Text="Ready to go!"></TextBlock> | |
</myWpf:PossibleView> | |
</myWpf:ConditionalView> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment