/// <summary>
        /// Deletes a binding between a dependency property and a network table entry
        /// </summary>
        /// <param name="source">The object the binding is on</param>
        /// <param name="property">The property to unbind</param>
        public static void Delete(DependencyObject source, DependencyProperty property)
        {
            if (!IsRunning)
            {
                throw new InvalidOperationException("Can only delete bindings while the network table is running");
            }
            DependencyNotifyListener listener = new DependencyNotifyListener(source);

            if (propertyLookup.ContainsKey(listener))
            {
                if (propertyLookup[listener].TryRemoveByFirst(property.Name))
                {
                    //permanent delete rather than temporary unbind
                    listener.DeleteBinding(property);
                }
            }
        }
        /// <summary>
        /// Creates a binding between a dependency property and a network table entry
        /// </summary>
        /// <typeparam name="TLocal">The type of the entry in the dashboard</typeparam>
        /// <typeparam name="TNetwork">The type of the entry in the network table</typeparam>
        /// <param name="source">The object to bind to</param>
        /// <param name="property">The property to bind. Attached properties will throw an exception</param>
        /// <param name="networkPath">The full path of the entry in the network table</param>
        /// <param name="converter">The conversion mapping between the network values and local values</param>
        /// <param name="localOverride">Whether the local dashboard values should take precedence when the binding occurs</param>
        public static void Create <TLocal, TNetwork>(DependencyObject source, DependencyProperty property, string networkPath,
                                                     NTConverter <TLocal, TNetwork> converter, bool localOverride = false)
        {
            if (!IsRunning)
            {
                throw new InvalidOperationException("Can only create bindings while the network table is running");
            }
            Tuple <ITable, string> networkSource = NormalizeKey(networkPath);

            networkPath = networkSource.Item2;
            //because of additional work that needs to be done to bind the value, simpler to reimplement
            DependencyNotifyListener listener = new DependencyNotifyListener(source);

            if (!propertyLookup.ContainsKey(listener))
            {
                propertyLookup[listener] = new OneToOneConversionMap <string, string>();
                //this is a new item being bound, have it notify us of updates
                listener.PropertyChanged += OnLocalValueChange;
            }
            if (propertyLookup[listener].TryAdd(property.Name, networkPath))
            {
                customTables[listener] = networkSource.Item1;
                //this means there were no binding conflicts
                //bind the dependency property to be notified of changes to it
                listener.BindProperty(property);
                //map a conversion if needed; null is valid
                propertyLookup[listener].MapConversionByFirst(property.Name, converter);
                //make the values consistent by forcing an update.
                //first check if the dashboard has a value at all
                Value data = networkSource.Item1.GetValue(networkPath, null);
                if (data == null || localOverride)
                {
                    //send the local value to the network by "updating" the local value
                    PropertyChangedEventArgs e = new PropertyChangedEventArgs(property.Name);
                    OnLocalValueChange(listener, e);
                }
                else
                {
                    //pull the dashboard from the local value by "updating" the network value
                    OnNetworkTableChange(networkSource.Item1, networkPath, data, NotifyFlags.NotifyUpdate);
                }
            }
        }