Last active
October 6, 2021 17:52
-
-
Save SchreinerK/299093195aebf1d3ef0d413659255c0d to your computer and use it in GitHub Desktop.
CompareBinding
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
// SOURCE: https://gist.github.com/SchreinerK/299093195aebf1d3ef0d413659255c0d | |
using System; | |
using System.Collections; | |
using System.Globalization; | |
using System.Windows; | |
using System.Windows.Data; | |
// ReSharper disable once CheckNamespace | |
namespace KsWare.Presentation { | |
public class CompareBinding : Binding { | |
// optional arguments not working in XAML, so we need more constructors | |
/// <inheritdoc /> | |
public CompareBinding(string path, CompareBindingOperator op) : base(path) { | |
Converter = new CompareBindingConverter(op); | |
} | |
/// <inheritdoc /> | |
public CompareBinding(string path, CompareBindingOperator op, object value) : base(path) { | |
Converter = new CompareBindingConverter(op, value); | |
} | |
} | |
public class CompareBindingConverter : IValueConverter { | |
public CompareBindingOperator Operator { get; } | |
public object CompareValue { get; } | |
public CompareBindingConverter(CompareBindingOperator op) { | |
Operator = op; | |
} | |
public CompareBindingConverter(CompareBindingOperator op, object compareValue = null) { | |
Operator = op; | |
CompareValue = compareValue; | |
} | |
/// <inheritdoc /> | |
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { | |
// RULE: null < !null | |
// RULE: null = null | |
switch (Operator) { | |
case CompareBindingOperator.IsFalse: return Equals(value, false); | |
case CompareBindingOperator.IsTrue: return Equals(value, true); | |
case CompareBindingOperator.IsNull: return Equals(value, null); | |
case CompareBindingOperator.IsNotNull: return !Equals(value, null); | |
case CompareBindingOperator.IsNullOrFalse: return Equals(value, null) || Equals(value, false); | |
case CompareBindingOperator.IsNullOrTrue: return Equals(value, null) || Equals(value, true); | |
case CompareBindingOperator.IsNullOr0: return Equals(value, null) || NumericEquals(value, 0); | |
case CompareBindingOperator.IsNullOrEmpty: | |
switch (value) { | |
case null: return true; | |
case string s: return string.IsNullOrEmpty(s); | |
case IList list: return list.Count == 0; | |
default: throw new NotSupportedException(); | |
} | |
case CompareBindingOperator.IsNotEmpty: | |
switch (value) { | |
case null: return false; | |
case string s: return !string.IsNullOrEmpty(s); | |
case IList list: return list.Count > 0; | |
default: throw new NotSupportedException(); | |
} | |
case CompareBindingOperator.IsEqualTo: | |
if (Equals(value, null) && Equals(CompareValue, null)) return true; | |
if (Equals(value, null) || Equals(CompareValue, null)) return false; | |
return Equals(value, System.Convert.ChangeType(CompareValue, value.GetType(), CultureInfo.InvariantCulture)); | |
case CompareBindingOperator.IsNotEqualTo: | |
if (Equals(value, null) && Equals(CompareValue, null)) return false; | |
if (Equals(value, null) || Equals(CompareValue, null)) return true; | |
return !Equals(value, System.Convert.ChangeType(CompareValue, value.GetType(), CultureInfo.InvariantCulture)); | |
case CompareBindingOperator.IsGreaterThen: | |
switch (value) { | |
case null: return !Equals(CompareValue, null); | |
case string s: return CompareToString(s) > 0; | |
default: return CompareToNumeric(value) > 0; | |
} | |
case CompareBindingOperator.IsGreaterOrEqual: | |
switch (value) { | |
case null: return true; | |
case string s: return CompareToString(s) >= 0; | |
default: return CompareToNumeric(value) >= 0; | |
} | |
case CompareBindingOperator.IsLessThen: | |
switch (value) { | |
case null: return !Equals(CompareValue, null); // null:null=false / null:!null=true | |
case string s: return CompareToString(s) < 0; | |
default: return CompareToNumeric(value) < 0; | |
} | |
case CompareBindingOperator.IsLessThenOrEqual: | |
switch (value) { | |
case null: return true; | |
case string s: return CompareToString(s) <= 0; | |
default: return CompareToNumeric(value) <= 0; | |
} | |
case CompareBindingOperator.TypeEqualTo: | |
switch (CompareValue) { | |
case null: throw new InvalidOperationException("CompareValue must not null"); | |
case string s when s.Contains("."): return Equals(value.GetType().FullName, s); | |
case string s when !s.Contains("."): return Equals(value.GetType().Name, s); | |
case Type type: return Equals(value.GetType(), type); | |
default: throw new InvalidOperationException("Invalid CompareValue."); | |
} | |
case CompareBindingOperator.TypeNotEqualTo: | |
switch (CompareValue) { | |
case null: throw new InvalidOperationException("CompareValue must not null"); | |
case string s when s.Contains("."): return !Equals(value.GetType().FullName, s); | |
case string s when !s.Contains("."): return !Equals(value.GetType().Name, s); | |
case Type type: return !Equals(value.GetType(), type); | |
default: throw new InvalidOperationException("Invalid CompareValue."); | |
} | |
case CompareBindingOperator.TypeIsAssignableFrom: | |
switch (CompareValue) { | |
case null: throw new InvalidOperationException("CompareValue must not null"); | |
case Type type: return value.GetType().IsAssignableFrom(type); | |
default: throw new InvalidOperationException("Invalid CompareValue."); | |
} | |
case CompareBindingOperator.TypeIsAssignableTo: | |
switch (CompareValue) { | |
case null: throw new InvalidOperationException("CompareValue must not null"); | |
case Type type: return type.IsAssignableFrom(value.GetType()); | |
default: throw new InvalidOperationException("Invalid CompareValue."); | |
} | |
default: return false; | |
} | |
} | |
private bool NumericEquals(object value, object compareValue) { | |
var a = (Decimal)System.Convert.ChangeType(value, typeof(Decimal), CultureInfo.InvariantCulture); | |
var b = (Decimal)System.Convert.ChangeType(compareValue, value.GetType(), CultureInfo.InvariantCulture); | |
return Math.Abs(a - b) < 0.0000001m; | |
} | |
private int CompareToString(string s) { | |
return s.CompareTo(System.Convert.ChangeType(CompareValue, typeof(string), CultureInfo.InvariantCulture)); | |
} | |
private int CompareToNumeric(object value) { | |
return CompareToNumeric(value, CompareValue); | |
} | |
private int CompareToNumeric(object value, object compareValue) { | |
var a = (Decimal)System.Convert.ChangeType(value, typeof(Decimal), CultureInfo.InvariantCulture); | |
var b = (Decimal)System.Convert.ChangeType(compareValue, value.GetType(), CultureInfo.InvariantCulture); | |
return a.CompareTo(b); | |
} | |
/// <inheritdoc /> | |
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { | |
throw new NotImplementedException(); | |
} | |
} | |
public class CompareDataTrigger : DataTrigger{ | |
// Constructor arguments are not possible in XAML, so this type is only a little helpful. | |
/// <inheritdoc /> | |
public CompareDataTrigger() { | |
Value = true; | |
} | |
/// <inheritdoc cref="DataTrigger.Binding"/> | |
public new CompareBinding Binding { | |
get => (CompareBinding)base.Binding; | |
set => base.Binding = value; | |
} | |
} | |
public enum CompareBindingOperator { | |
//unary operators | |
IsFalse, // bool/bool? | |
IsTrue, // bool/bool? | |
IsNullOrFalse, // bool? | |
IsNullOrTrue, // bool? | |
IsNull, //object | |
IsNotNull,//object | |
IsNullOrEmpty, // valid for collection and strings | |
IsNotEmpty, // valid for collection and strings | |
IsNullOr0, | |
// binary operators | |
IsEqualTo, | |
IsNotEqualTo, | |
IsGreaterThen, | |
IsGreaterOrEqual, | |
IsLessThen, | |
IsLessThenOrEqual, | |
TypeEqualTo, | |
TypeNotEqualTo, | |
TypeIsAssignableFrom, | |
TypeIsAssignableTo, | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment