private static void OnNetworkTableChange(ITable table, string key, Value v, NotifyFlags flags) { try { //multiple objects could be bound to this key foreach (INotifyPropertyChanged source in propertyLookup.Keys) { OneToOneConversionMap <string, string> conversionMap = propertyLookup[source]; ITable boundTable = customTables[source]; object bindingSource = (source is DependencyNotifyListener) ? (object)(source as DependencyNotifyListener).source : source; if (table.ToString() != boundTable.ToString()) { continue; } if (conversionMap.TryGetBySecond(key, out string property)) { //the property that changed is bound to this object //grab the converter and use it if needed IValueConverter converter = conversionMap.GetConverterByFirst(property); PropertyInfo inf = bindingSource.GetType().GetProperty(property); //issue using v for some reason object value = NetworkUtil.ReadValue(boundTable.GetValue(key, null)); if (converter != null) { //in an NTConverter (required in API) the null values are never used so we don't need to set them object attemptedVal = converter.ConvertBack(value, null, null, null); //in case the conversion was invalid if (attemptedVal != DependencyProperty.UnsetValue) { value = attemptedVal; } } //correct any type inconsistencies (eg if we want to display an integer from the network, which only stores doubles) if (value != null && value.GetType() != inf.PropertyType) { Type targetType = inf.PropertyType; if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Nullable <>)) { targetType = targetType.GetGenericArguments()[0]; } //anything still here can make an invalid cast to let them know to use a converter value = Convert.ChangeType(value, targetType); } //write to the object assignmentDispatch.Invoke(() => inf.SetValue(bindingSource, value)); } } } catch (InvalidOperationException e) { if (!e.Message.StartsWith("Collection was modified")) { throw e; } } }
private static void OnLocalValueChange(object sender, PropertyChangedEventArgs e) { INotifyPropertyChanged obj = sender as INotifyPropertyChanged; OneToOneConversionMap <string, string> map = propertyLookup[obj]; ITable boundTable = customTables[obj]; object bindingSource = (obj is DependencyNotifyListener) ? (object)(obj as DependencyNotifyListener).source : obj; //first, verify that the property that changed is bound to something if (map.TryGetByFirst(e.PropertyName, out string networkPath)) { //grab the converter and use it if needed IValueConverter converter = map.GetConverterByFirst(e.PropertyName); PropertyInfo inf = bindingSource.GetType().GetProperty(e.PropertyName); object value = inf.GetValue(bindingSource); if (converter != null) { //in an NTConverter (required in API) the null values are never used so we don't need to set them object attemptedVal = converter.Convert(value, null, null, null); //in case the conversion was invalid if (attemptedVal != DependencyProperty.UnsetValue) { value = attemptedVal; } } //apparently network table STRONGLY dislikes null values if (value != null) { //write to the dashboard Value data = Value.MakeValue(value); boundTable.PutValue(networkPath, data); //the network table doesn't know any better and won't try to notify us of this change //therefore, bindings won't be updated again, so we should initiate a network table update //this is ok, because we only write to network table on effective value changes OnNetworkTableChange(boundTable, networkPath, data, NotifyFlags.NotifyUpdate); } } }