Tag Archives: c#

Mutually Exclusive WPF Checkboxes

using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Interactivity;

public class UncheckOnSignalBehavior : Behavior<ToggleButton>
{
    public static readonly DependencyProperty SignalProperty = DependencyProperty.Register(
        "Signal",
        typeof(bool),
        typeof(UncheckOnSignalBehavior),
        new PropertyMetadata(SignalProperty_PropertyChanged));

    private static void SignalProperty_PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        ((UncheckOnSignalBehavior)sender).Update();
    }

    public bool Signal
    {
        get { return (bool)GetValue(SignalProperty); }
        set { SetValue(SignalProperty, value); }
    }

    protected override void OnAttached()
    {
        Update();
    }

    private void Update()
    {
        if (AssociatedObject != null && Signal)
        {
            AssociatedObject.IsChecked = false;
        }
    }
}

Usage:

<CheckBox IsChecked="{Binding A}">
    <i:Interaction.Behaviors>
        <local:UncheckOnSignalBehavior Signal="{Binding None}" />
    </i:Interaction.Behaviors>
    Option A
</CheckBox>
<CheckBox IsChecked="{Binding B}">
    <i:Interaction.Behaviors>
        <local:UncheckOnSignalBehavior Signal="{Binding None}" />
    </i:Interaction.Behaviors>
    Option B
</CheckBox>
<CheckBox IsChecked="{Binding C}">
    <i:Interaction.Behaviors>
        <local:UncheckOnSignalBehavior Signal="{Binding None}" />
    </i:Interaction.Behaviors>
    Option C
</CheckBox>
<CheckBox IsChecked="{Binding None}">
    <i:Interaction.Behaviors>
        <local:UncheckOnSignalBehavior Signal="{Binding A}" />
        <local:UncheckOnSignalBehavior Signal="{Binding B}" />
        <local:UncheckOnSignalBehavior Signal="{Binding C}" />
    </i:Interaction.Behaviors>
    None of the above
</CheckBox>

Buffering a TraceListener

TraceEventArgs.cs:

using System;

public class TraceEventArgs : EventArgs
{
    public string Message { get; private set; }

    public TraceEventArgs(string message)
    {
        Message = message;
    }
}

BufferedTraceListener.cs:

using System;
using System.Diagnostics;
using System.Text;
using System.Threading;

public class BufferedTraceListener : TraceListener
{
    private SynchronizationContext context;
    private StringBuilder buffer;
    private object bufferLock;
    private Timer timer;
    private TimeSpan delay;

    public BufferedTraceListener(TimeSpan delay)
    {
        context = SynchronizationContext.Current;
        buffer = new StringBuilder();
        bufferLock = new object();
        timer = new Timer(Tick);
        this.delay = delay;
    }

    public BufferedTraceListener()
        : this(TimeSpan.FromSeconds(0.1)) { }

    public event EventHandler<TraceEventArgs> Written;
    private void OnWritten(TraceEventArgs e)
    {
        if (context == null)
        {
            Written?.Invoke(this, e);
        }
        else
        {
            context.Post(state => Written?.Invoke(this, e), null);
        }
    }
    private void OnWritten(string message)
    {
        OnWritten(new TraceEventArgs(message));
    }

    private void Tick(object state)
    {
        lock (bufferLock)
        {
            OnWritten(buffer.ToString());
            buffer.Clear();
        }
    }

    public override void Write(string message)
    {
        lock (bufferLock)
        {
            buffer.Append(message);
            timer.Change(delay, Timeout.InfiniteTimeSpan);
        }
    }

    public override void WriteLine(string message)
    {
        Write(message + Environment.NewLine);
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        if (disposing)
        {
            timer.Dispose();
        }
    }
}

Logging SQL Queries in ADO.NET

using System.Data;

public class LoggingCommand : IDbCommand
{
    private static void Log(string message)
    {
        // ...
    }

    private IDbCommand @base;

    public string CommandText
    {
        get { return @base.CommandText; }
        set { @base.CommandText = value; }
    }

    public int CommandTimeout
    {
        get { return @base.CommandTimeout; }
        set { @base.CommandTimeout = value; }
    }

    public CommandType CommandType
    {
        get { return @base.CommandType; }
        set { @base.CommandType = value; }
    }

    public IDbConnection Connection
    {
        get { return @base.Connection; }
        set { @base.Connection = value; }
    }

    public IDataParameterCollection Parameters
    {
        get { return @base.Parameters; }
    }

    public IDbTransaction Transaction
    {
        get { return @base.Transaction; }
        set { @base.Transaction = value; }
    }

    public UpdateRowSource UpdatedRowSource
    {
        get { return @base.UpdatedRowSource; }
        set { @base.UpdatedRowSource = value; }
    }

    public LoggingCommand(IDbCommand @base)
    {
        this.@base = @base;
    }

    public void Cancel()
    {
        @base.Cancel();
    }

    public IDbDataParameter CreateParameter()
    {
        return @base.CreateParameter();
    }

    public void Dispose()
    {
        @base.Dispose();
    }

    private void OnExecuting()
    {
        Log(CommandText);
    }

    public int ExecuteNonQuery()
    {
        OnExecuting();
        return @base.ExecuteNonQuery();
    }

    public IDataReader ExecuteReader()
    {
        OnExecuting();
        return @base.ExecuteReader();
    }

    public IDataReader ExecuteReader(CommandBehavior behavior)
    {
        OnExecuting();
        return @base.ExecuteReader(behavior);
    }

    public object ExecuteScalar()
    {
        OnExecuting();
        return @base.ExecuteScalar();
    }

    public void Prepare()
    {
        @base.Prepare();
    }
}

public class LoggingConnection : IDbConnection
{
    private IDbConnection @base;

    public string ConnectionString
    {
        get { return @base.ConnectionString; }
        set { @base.ConnectionString = value; }
    }

    public int ConnectionTimeout
    {
        get { return @base.ConnectionTimeout; }
    }

    public string Database
    {
        get { return @base.Database; }
    }

    public ConnectionState State
    {
        get { return @base.State; }
    }

    public LoggingConnection(IDbConnection @base)
    {
        this.@base = @base;
    }

    public IDbTransaction BeginTransaction()
    {
        return @base.BeginTransaction();
    }

    public IDbTransaction BeginTransaction(IsolationLevel il)
    {
        return @base.BeginTransaction(il);
    }

    public void ChangeDatabase(string databaseName)
    {
        @base.ChangeDatabase(databaseName);
    }

    public void Close()
    {
        @base.Close();
    }

    public IDbCommand CreateCommand()
    {
        return new LoggingCommand(@base.CreateCommand());
    }

    public void Dispose()
    {
        @base.Dispose();
    }

    public void Open()
    {
        @base.Open();
    }
}

Two-Way Dictionaries

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

public class TwoWayDictionary<T1, T2> : ICollection<Tuple<T1, T2>>
{
    private static Tuple<T1, T2> ToTuple(T1 item1, T2 item2)
    {
        return Tuple.Create(item1, item2);
    }

    private static Tuple<T1, T2> ToTuple(KeyValuePair<T1, T2> pair)
    {
        return ToTuple(pair.Key, pair.Value);
    }

    private static KeyValuePair<T1, T2> ToForwardPair(Tuple<T1, T2> tuple)
    {
        return new KeyValuePair<T1, T2>(tuple.Item1, tuple.Item2);
    }

    private static KeyValuePair<T2, T1> ToReversePair(Tuple<T1, T2> tuple)
    {
        return new KeyValuePair<T2, T1>(tuple.Item2, tuple.Item1);
    }

    private IDictionary<T1, T2> forward;
    private IDictionary<T2, T1> reverse;

    public int Count
    {
        get { return forward.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public TwoWayDictionary()
    {
        forward = new Dictionary<T1, T2>();
        reverse = new Dictionary<T2, T1>();
    }

    public void Add(T1 item1, T2 item2)
    {
        forward.Add(item1, item2);
        try
        {
            reverse.Add(item2, item1);
        }
        catch
        {
            forward.Remove(item1);
            throw;
        }
    }

    public void Add(Tuple<T1, T2> tuple)
    {
        Add(tuple.Item1, tuple.Item2);
    }

    public void Clear()
    {
        forward.Clear();
        reverse.Clear();
    }

    public bool Contains(Tuple<T1, T2> tuple)
    {
        return forward.Contains(ToForwardPair(tuple));
    }

    public void CopyTo(Tuple<T1, T2>[] array, int arrayIndex)
    {
        foreach (Iterator<KeyValuePair<T1, T2>> pair in forward.Iterate())
        {
            array[arrayIndex + pair.Index] = ToTuple(pair.Value);
        }
    }

    public bool Remove(Tuple<T1, T2> tuple)
    {
        if (forward.Remove(ToForwardPair(tuple)))
        {
            reverse.Remove(ToReversePair(tuple));
            return true;
        }
        else
        {
            return false;
        }
    }

    public T2 Forward(T1 item1)
    {
        return forward[item1];
    }

    public T1 Reverse(T2 item2)
    {
        return reverse[item2];
    }

    public IEnumerator<Tuple<T1, T2>> GetEnumerator()
    {
        return forward.Select(ToTuple).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

Interlocked Booleans

using System;
using System.Threading;

public class InterlockedBoolean
{
    private int value;

    public bool Value
    {
        get { return Convert.ToBoolean(value); }
    }

    public InterlockedBoolean(bool value)
    {
        this.value = Convert.ToInt32(value);
    }

    public bool Exchange(bool value)
    {
        return Convert.ToBoolean(Interlocked.Exchange(ref this.value, Convert.ToInt32(value)));
    }

    public bool CompareExchange(bool value, bool comparand)
    {
        return Convert.ToBoolean(Interlocked.CompareExchange(ref this.value, Convert.ToInt32(value), Convert.ToInt32(comparand)));
    }
}

Base64 Serialization and Deserialization

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

public static class Base64Extensions
{
    private static IFormatter GetFormatter()
    {
        return new BinaryFormatter();
    }

    public static string ToBase64String(object value)
    {
        byte[] data;
        if (value == null)
        {
            data = new byte[] { };
        }
        else
        {
            using (MemoryStream stream = new MemoryStream())
            {
                GetFormatter().Serialize(stream, value);
                data = stream.ToArray();
            }
        }
        return Convert.ToBase64String(data);
    }

    public static object FromBase64String(string value)
    {
        byte[] data = Convert.FromBase64String(value);
        if (data.Length == 0)
        {
            return null;
        }
        else
        {
            using (MemoryStream stream = new MemoryStream(data))
            {
                return GetFormatter().Deserialize(stream);
            }
        }
    }
}

Conditional Required Validation in ASP.NET MVC

using System.ComponentModel.DataAnnotations;
using System.Reflection;

public sealed class ConditionalRequiredAttribute : RequiredAttribute
{
    public override bool RequiresValidationContext
    {
        get { return true; }
    }

    public string ConditionalPropertyName { get; private set; }

    public ConditionalRequiredAttribute(string conditionalPropertyName)
    {
        ConditionalPropertyName = conditionalPropertyName;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        PropertyInfo conditionalProperty = validationContext.ObjectType.GetProperty(ConditionalPropertyName);
        bool conditionalValue = (bool)conditionalProperty.GetValue(validationContext.ObjectInstance);
        if (conditionalValue)
        {
            return base.IsValid(value, validationContext);
        }
        else
        {
            return ValidationResult.Success;
        }
    }
}

Automatic WPF Grids

using System.Windows;
using System.Windows.Controls;

public class AutoGrid : Grid
{
    protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
    {
        base.OnVisualChildrenChanged(visualAdded, visualRemoved);
        SetCells();
    }

    private void SetCells()
    {
        int columnCount = ColumnDefinitions.Count;
        for (int index = 0; index < Children.Count; index++)
        {
            UIElement child = Children[index];
            SetRow(child, index / columnCount);
            SetColumn(child, index % columnCount);
        }
    }
}

WPF Application Boilerplate

using System;
using System.Text;
using System.Windows;

public partial class App : Application
{
    [STAThread]
    private static void Main(string[] args)
    {
        try
        {
            App app = new App();
            app.Run();
        }
        catch (Exception ex)
        {
            HandleError(ex);
        }
    }

    private static void HandleError(Exception ex)
    {
        Clipboard.SetText(ex.ToString());
        StringBuilder message = new StringBuilder();
        message.AppendLine("The application has encountered an error and must shut down.");
        message.AppendLine();
        message.AppendLine(string.Format("{0}: {1}", ex.GetType(), ex.Message));
        message.AppendLine();
        message.Append("Full details have been copied to the clipboard.");
        MessageBox.Show(message.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
    }

    public App()
    {
        DispatcherUnhandledException += (sender, e) =>
        {
            HandleError(e.Exception);
            e.Handled = true;
            Shutdown(1);
        };
        InitializeComponent();
    }

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        Window window/* = ... */;
        window.Show();
    }
}

Update: April 5, 2018

For something a little less user-hostile, save the stack trace to a temporary file instead of the clipboard:

private static void HandleError(Exception ex)
{
    string path = Path.GetTempFileName();
    File.WriteAllText(path, ex.ToString());
    StringBuilder message = new StringBuilder();
    message.AppendLine("The application has encountered an error and must shut down.");
    message.AppendLine();
    message.AppendLine(string.Format("{0}: {1}", ex.GetType(), ex.Message));
    message.AppendLine();
    message.AppendLine("Full details have been saved to the following file:");
    message.AppendLine();
    message.Append(path);
    MessageBox.Show(message.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}