Skip to content

Instantly share code, notes, and snippets.

@SchreinerK
Last active October 6, 2021 17:52
Show Gist options
  • Save SchreinerK/299093195aebf1d3ef0d413659255c0d to your computer and use it in GitHub Desktop.
Save SchreinerK/299093195aebf1d3ef0d413659255c0d to your computer and use it in GitHub Desktop.
CompareBinding
// 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