I had successfully implemented a databind before but now it has stop working... After putting in some breakpoints I have noticed that the PropertyChange event remains null. I looked up several "solutions" involving using DataContext (not sure where to put it) but still didnt work...
Thanks for any help!
It works for the first initial bind but after it does not wok (when the property changes)
My Code:
My Binding:
//databinding
Binding(newProjectile.CurrentVelocity, lbl_angleoftraveloutput, "AngleOfTravel");
private void Binding(velocity Object, Label Output, string Field)
{
Binding newBinding = new Binding();
newBinding.Source = Object;
newBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
newBinding.Path = new PropertyPath(Field);
newBinding.Mode = BindingMode.TwoWay;
Output.SetBinding(Label.ContentProperty, newBinding);
}
My Object (Part of it):
public class velocity : INotifyPropertyChanged, ICloneable
{
public double AngleOfTravel
{
get { return _AngleOfTravel; }
set
{
_AngleOfTravel = value;
OnPropertyChanged("AngleOfTravel");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string PropertyName)
{
PropertyChangedEventHandler Handler = PropertyChanged;
if (Handler != null)
{
Handler(this, new PropertyChangedEventArgs(PropertyName));
}
}
}
I have analyzed your code. In the button click event - LoadStaticsAndStart you have a statement newParticle.CurrentVelocity = newParticle.InitialVelocity; whic his causing the issue. newParticle.CurrentVelocity is bound to the UI Labels so it will have INotifyPropertyChanged Implementation. But newParticle.InitialVelocity is not bound with UI so it will not have INotifyPropertyChanged so I have commented the line and the UI update for you output labels are working now. Replace the function with the below function.
private void LoadStaticsAndStart(object sender, RoutedEventArgs e)
{
GraphCanvas.Visibility = Visibility.Visible;
DrawAxis();
List<velocity> velocityList = new List<velocity>();
double[,] DisplacementArray = new double[59, 1];
btn_Calculate.Click += OnLoaded;
particle newParticle;
velocity InitialVelocity = new velocity();
ObjectsAndDataStuctures.Environment newEnvironment;
if (FormatCheck(txtb_AngleOLaunch) == false || FormatCheck(txtbox_InitialVelocity) == false || FormatCheck(txtbox_TimeOfFlight) == false)
{
MessageBox.Show(" Input is not valid");
}
else
{
newEnvironment = new ObjectsAndDataStuctures.Environment();
newEnvironment.gravity = -9.8; // gravity remove when needed or changed
InitialVelocity.Magnitude = Convert.ToDouble(txtbox_InitialVelocity.Text); // collecting variables.
InitialVelocity.AngleOfTravel = Convert.ToDouble(txtb_AngleOLaunch.Text); // collecting variables.
newParticle = new particle(InitialVelocity, Convert.ToDouble(txtbox_TimeOfFlight.Text));
stk_pnl_inputvar.Visibility = Visibility.Collapsed;
CreateLabels(newParticle);
newParticle.CalculateHComponent();
newParticle.CalculateVComponent();
MPP(newParticle, newEnvironment);
OnLoaded(this, e);
if (Convert.ToDouble(txtbox_TimeOfFlight.Text) > 60)
{
TimeConstant = Convert.ToDouble(txtbox_TimeOfFlight.Text) / 60;
}
else
{
TimeConstant = 1;
}
double HVelTemp = newParticle.InitialVelocity.HorizontalVelocity * PixelPerMeter;
double VVelTemp = newParticle.InitialVelocity.VerticalVelocity * PixelPerMeter * -1;
Velocity = new Vector(HVelTemp, VVelTemp); // y direction is downwards
acceleration = new Vector(0, -1 * newEnvironment.gravity * PixelPerMeter); // y direction is downwards
aTimer = new System.Windows.Threading.DispatcherTimer();
aTimer.Interval = new TimeSpan(0, 0, 0, 0, 200);
//newParticle.CurrentVelocity = newParticle.InitialVelocity;
//this.DataContext = this;
TimerEvent = (s, t) => onTimedEvent(sender, t, newParticle, newEnvironment);
aTimer.Tick += TimerEvent;
ListOfVelocities.Add((velocity)newParticle.CurrentVelocity.Clone());
InSimulation = true;
aTimer.Start();
}
}
if you want to assign you InitialVelocity with CurrentVelocity Assign property by property. Like this below
newParticle.CurrentVelocity.AngleOfTravel = newParticle.InitialVelocity.AngleOfTravel;
newParticle.CurrentVelocity.HorizontalVelocity = newParticle.InitialVelocity.HorizontalVelocity;
newParticle.CurrentVelocity.Magnitude = newParticle.InitialVelocity.Magnitude;
newParticle.CurrentVelocity.VerticalVelocity = newParticle.InitialVelocity.VerticalVelocity;
Related
I am using WPF application with Helix Toolkit, where I dynamically add multiple 3D objects into the Viewport. The Visuals of the objects are added successfully, but some of the recently added objects (in this case tube objects) are not firing events, neither show their appropriate tooltip message. After some interaction with the GUI of the app and adding new additional 3D objects to the Viewport, the old already added objects are firing events and show their tooltip message...
Also I have noticed that if I add some other new 3D objects and if I rotate the camera with right button pressed of the mouse, they are coming responsive!
So what Is causing this problem? Is it there some rendering or camera problem or something else?
I am using this code about the Viewport with SortingVisual3D where all of the objects are added behind the transparent surface:
<helix:HelixViewport3D Grid.Column="0" Grid.Row="1" Grid.RowSpan="10" Background="GhostWhite" Name="_viewport" ShowFrameRate="True" ShowTriangleCountInfo="True" ShowViewCube="True" IsHitTestVisible="True" CalculateCursorPosition="True" ShowCoordinateSystem="True" >
<helix:DefaultLights/>
<helix:SortingVisual3D x:Name="sortingVisual1" Method="BoundingBoxCorners" IsSorting="True" SortingFrequency="4" >
<helix:MeshGeometryVisual3D x:Name="_cubeObjects">
</helix:MeshGeometryVisual3D>
<helix:MeshGeometryVisual3D x:Name="_tubeObjects">
</helix:MeshGeometryVisual3D>
//Transparent surface
<helix:MeshGeometryVisual3D x:Name="_transparentSurface" >
</helix:MeshGeometryVisual3D>
</helix:SortingVisual3D>
</helix:HelixViewport3D>
</strike>
This is how I dynamically add the objects:
AddObject tubeObject = new AddObject(points3dCollection, "Tube Object", tubeDiameter, 36, objectMaterial);
tubeObject.MouseLeftButtonDown += new MouseButtonEventHandler(tubeObjectClick);
tubeObject.IsHitTestVisible = true;
tubeObject.SetName("Tube Object");
ContainerUIElement3D cui = new ContainerUIElement3D();
cui.Children.Add(tubeObject);
_tubeObjects.Children.Add(cui);
'-------------------This is the class AddObject definition:-------------------------------'
class AddObject : UIElement3D,INotifyCollectionChanged
{
private readonly Timer _timer;
private readonly ToolTip _toolTip;
public AddObject DataContext { get; }
public AddObject(Point3DCollection path, string objectName, double diametar_1, int thetaDiv, Material material)
{
MeshBuilder builder = new MeshBuilder();
List<Point3D> list = new List<Point3D>();
for (int i = 0; i < path.Count; i++)
{
list.Add(path[i]);
}
list = CanonicalSplineHelper.CreateSpline(list, 0.5, null, false, 0.9);
builder.AddTube(list, diametar_1, thetaDiv, false, true, true);
GeometryModel3D model = new GeometryModel3D(builder.ToMesh(), material);
model.SetName(objectName);
Visual3DModel = model;
_toolTip = new ToolTip();
_timer = new Timer { AutoReset = false };
_timer.Elapsed += ShowToolTip;
DataContext = this;
}
public event NotifyCollectionChangedEventHandler CollectionChanged
{
add
{
((INotifyCollectionChanged)DataContext).CollectionChanged += value;
}
remove
{
((INotifyCollectionChanged)DataContext).CollectionChanged -= value;
}
}
public object ToolTipContent { get { return _toolTip.Content; } set { _toolTip.Content = value; } }
private void ShowToolTip(object sender, ElapsedEventArgs e)
{
_timer.Stop();
if (_toolTip != null)
_toolTip.Dispatcher.Invoke(new Action(() => { _toolTip.IsOpen = true; }));
}
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
var gm = Visual3DModel as GeometryModel3D;
gm.Material = gm.Material == materialtype ? Materials.Yellow : materialtype;
if (_toolTip != null)
{
_toolTip.IsOpen = true;
_toolTip.Content = gm.GetName().ToString().Trim() + " vein "; }
// _timer.Interval = ToolTipService.GetInitialShowDelay(Application.Current.MainWindow);
_timer.Interval = 50;
_timer.Start();
e.Handled = true;
}
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
var gm = Visual3DModel as GeometryModel3D;
gm.Material = gm.Material == materialtype ? Materials.Yellow : materialtype;
_timer.Stop();
if (_toolTip != null)
{ _toolTip.IsOpen = false;
_toolTip.Content = "";
}
e.Handled = true;
}
}
What should I check at first? Any idea is welcomed ! Please help !
The immediate response problem of the recently inserted objects is solved with this code:
<helix:MeshGeometryVisual3D >
<ContainerUIElement3D x:Name="_cubeObjects">
</ContainerUIElement3D>
</helix:MeshGeometryVisual3D>
<helix:MeshGeometryVisual3D >
<ContainerUIElement3D x:Name="_ballClickPoints">
</ContainerUIElement3D>
</helix:MeshGeometryVisual3D>
<helix:MeshGeometryVisual3D >
<ContainerUIElement3D x:Name="_tubeObjects">
</ContainerUIElement3D>
</helix:MeshGeometryVisual3D>
<helix:MeshGeometryVisual3D x:Name="_transparentSurface">
</helix:MeshGeometryVisual3D>
So the change is in using ContainerUIElement3D into MeshGeometryVisual3D.. The objects are responding immediately on mouse events .
This question already has answers here:
Error: Must create DependencySource on same Thread as the DependencyObject even by using Dispatcher
(2 answers)
Closed 3 years ago.
I am using a label on multiple forms which display the weather data which is called from a WCF service. I wish to have this update every minute to display the updated weather data without interfering with user interaction.
I get the following error:
"Must create DependencySource on same Thread as the DependencyObject. "
I have a View Model for getting the weather data asynchronously which inherits from ViewModelBase to handle property changed events. The properties from the ViewModel are bound to the label
ViewModel for weather
public class WeatherDataVM : ViewModelBase
{
private string _windString;
private SolidColorBrush _windState;
private DispatcherTimer _timer;
public WeatherDataVM()
{
_timer = new DispatcherTimer(DispatcherPriority.Render);
_timer.Interval = TimeSpan.FromSeconds(10);
_timer.Tick += async (sender, args) => {await Task.Run(() => GetWindAsync()); };
//_timer.Tick += _timer_Tick;
_timer.Start();
GetWind();
}
private void GetWind()
{
var weatherFromService = Services.Instance.EmptyStackService.GetWeather();
var windSpeed = Convert.ToDouble(weatherFromService.Windspeed);
var maxGust = Convert.ToDouble(weatherFromService.Max_Gust_In_Last_Min);
var windSpeedMPH = Math.Round(windSpeed * 1.15078, 1);
var maxGustMPH = Math.Round(maxGust * 1.15078, 1);
var windString = $"W/S: {windSpeedMPH}({maxGustMPH})";
var windState = new Color();
if (windSpeed >= 40)
windState = Color.FromRgb(255, 64, 64);
else if (windSpeed >= 24)
windState = Color.FromRgb(255, 212, 128);
else
windState = Color.FromRgb(0, 255, 0);
_windState = new SolidColorBrush(windState);
_windString = windString;
}
private async Task GetWindAsync()
{
var weatherFromService = Services.Instance.EmptyStackService.GetWeather();
var windSpeed = Convert.ToDouble(weatherFromService.Windspeed);
var maxGust = Convert.ToDouble(weatherFromService.Max_Gust_In_Last_Min);
var windSpeedMPH = Math.Round(windSpeed * 1.15078, 1);
var maxGustMPH = Math.Round(maxGust * 1.15078, 1);
var windString = $"W/S: {windSpeedMPH}({maxGustMPH})";
var windState = new Color();
if (windSpeed >= 40)
windState = Color.FromRgb(255, 64, 64);
else if (windSpeed >= 24)
windState = Color.FromRgb(255, 212, 128);
else
windState = Color.FromRgb(0, 255, 0);
WindState = new SolidColorBrush(windState);
WindString = windString;
}
public string WindString
{
get { return _windString; }
set
{
if (_windString == value)
return;
_windString = value;
OnPropertyChanged("WindString");
}
}
public SolidColorBrush WindState
{
get { return _windState; }
set
{
if (_windState == value)
return;
_windState = value;
OnPropertyChanged("WindState");
}
}
}
ViewModelBase
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Xaml on view for label
<Label x:Name="lblWeather" Content="{Binding WindString}" Foreground="black" Background="{Binding WindState}" Style="{DynamicResource SmallLabel}" />
code behind view in constructor
lblWeather.DataContext = new WeatherDataVM();
the weather label should change each time the timer ticks. Instead it throws an error.
You can create a brush on a background thread if you freeze it:
var brush = new SolidColorBrush(windState);
brush.Freeze();
WindState = brush;
But it doesn't make much sense to use a DispatcherTimer if you just call Task.Run in the Tick event handler.
Provided that your event handler only creates brushes and don't manipulate any UI elements directly (it certainly shouldn't since it's implemented in a view model), you could use a System.Timer.Timer. Its Elapsed event is queued for execution on a thread pool thread where you can query the service without blocking the UI.
I'm trying to provide communication between two objects that can be best described as turn - based. What I mean by that is that the first object(a Game class) has multiple goals which are specified in a database, and the second object represents a class that is used to play animations from sprite sheets. My goal currently is to show an introductory animation in the Game class(that I have done), show the player his first goal. Then the class expects user input in the form of animation parameters which are then passed as a list to be animated.
Once the animation is complete, I check if the goal is met. If it is met, and it is not the last one, I need to go back to the Game class, show the next goal, play a sort off explanation, then expect user input and this repeats until all goals are satisfied.
This is Windows Phone 8.1 Silverlight.
Here is the code of the Game class(most relevant snippets):
public Game(GamePage gp, int chapterNum)
{
currentClickedCommand = null;
currentClickedSlot = null;
gamePage = gp;
chapterNumber = chapterNum;
isAnimationComplete = false;
//chosenCommands = new string[5];
gamePage.characterRectangle.Visibility = System.Windows.Visibility.Collapsed;
Thread AnimationThread = new Thread(playIntroAnimation);
AnimationThread.Start();
Thread loadingThread = new Thread(loadControls);
loadingThread.Start();
}
These two threads are used to play the animation(animation thread), and the other one waits for a static variable to be set to true to load user input controls. The code:
private void loadControls()
{
while (!InformationObject.isSequenceComplete) { }
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
// loadGoals();
showCurrentGoal();
gamePage.CommandGrid.Visibility = System.Windows.Visibility.Visible;
gamePage.SlotGrid.Visibility = System.Windows.Visibility.Visible;
gamePage.mainCanvas.Background = null;
gamePage.characterRectangle.Visibility = Visibility.Collapsed;
loadCommands();
loadSlots();
});
}
This is the method which plays the animation sequence that the user selected:
public void AnimateCurrentAnimationInSequence(Rectangle container) {
AnimationParams currentParams = sequenceToAnimate[currentAnimationInSequence];
DoubleAnimation fadeInAnimation = new DoubleAnimation();
fadeOutAnimation = new DoubleAnimation();
if (sequencePlayerInteractions.Count != 0 && sequencePlayerInteractions != null)
{
PlayerInteraction currentInteraction = sequencePlayerInteractions[CurrentInteraction];
TalkBubble interactionBubble = new TalkBubble(currentInteraction.Value);
interactionBubble.setWidthHeight((int)container.Width, (int)container.Height);
talkBubble = interactionBubble;
onCanvas.Children.Add(talkBubble);
Canvas.SetLeft(talkBubble, Canvas.GetLeft(container) + 30);
Canvas.SetTop(talkBubble, Canvas.GetTop(container) + 30);
}
// ovdje razdvojiti po tipu
if (!currentParams.IsMoveType)
{
ImageBrush imageBrush = new ImageBrush() { Stretch = Stretch.None, AlignmentX = AlignmentX.Left, AlignmentY = AlignmentY.Top };
imageBrush.Transform = new CompositeTransform();
imageBrush.ImageSource = currentParams.Sheet;
container = setRectangleProperties(container, currentParams.Width, currentParams.Height, imageBrush);
// Storyboard za animaciju
Storyboard sb = new Storyboard();
if (currentParams.Loop) sb.RepeatBehavior = RepeatBehavior.Forever; // odredjujem da li je na repeat
// Animacija za talk bubble
if (sequencePlayerInteractions.Count != 0 && sequencePlayerInteractions != null)
{
fadeInAnimation.From = 0.0;
fadeInAnimation.To = 1.0;
fadeInAnimation.Completed += new EventHandler(fadeIn_complete);
fadeInAnimation.FillBehavior = FillBehavior.HoldEnd;
Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath(UIElement.OpacityProperty));
Storyboard.SetTarget(fadeInAnimation, talkBubble);
}
// Animacija sheeta preko frameova
int time = 0; // vrijeme kojim odredjujem trajanja frameova
ObjectAnimationUsingKeyFrames frm = createFrames(currentParams.Repetitions, time, currentParams.Width, currentParams.NumberOfFrames); // inicijaliziram frameove animacije sheeta
frm.BeginTime = new TimeSpan(0, 0, 0);
fadeInAnimation.Duration = TimeSpan.FromMilliseconds(400);
Storyboard.SetTarget(frm, container.Fill);
Storyboard.SetTargetProperty(frm, new PropertyPath("(ImageBrush.Transform).(CompositeTransform.TranslateX)"));
sb.Children.Add(frm);
if (sequencePlayerInteractions.Count != 0 && sequencePlayerInteractions != null) sb.Children.Add(fadeInAnimation);
sb.Completed += new EventHandler(animationInSequence_Complete);
sb.Begin();
}
else {
cameFromSequenceAnimate = true;
AnimateMove(container, currentParams.Direction, currentParams.Steps);
}
}
private void animationInSequence_Complete(object sender, EventArgs e)
{
if (!(currentAnimationInSequence + 1 == animationCount))
{
if (sequencePlayerInteractions.Count != 0 && sequencePlayerInteractions != null) { if (!(CurrentInteraction + 1 == interactionCount)) CurrentInteraction++; }
currentAnimationInSequence++;
AnimateCurrentAnimationInSequence(currentContainer);
}
else
{
// Goal satisfaction
if (CurrentCommandIDs != null && CurrentCommandIDs.Count != 0)
{
bool has = true;
List<string> sequenceID = new List<string>();
foreach (AnimationParams ap in sequenceToAnimate) sequenceID.Add(ap.AnimationID);
for (int i = 0; i < CurrentCommandIDs.Count; i++)
{
if (!sequenceID.Contains(CurrentCommandIDs[i])) has = false;
}
if (has) { InformationObject.isCriteriaMet = true;
InformationObject.setCurrentGoal(InformationObject.currentGoal + 1);
}
else InformationObject.isCriteriaMet = false;
}
KidCode.Game.isSequenceComplete = true;
InformationObject.isSequenceComplete = true;
currentAnimationInSequence = 0;
CurrentInteraction = 0;
}
}
Now, after the animation is done, I need to somehow return back to the game class to basically repeat this. I tried using a static class(InformationObject)
to store information about current goals but I wasn't able to utilize it
Thanks for any help!
I have a circular gauge(progress bar) with a polygon needle that when i select a number in a list box the needle goes up to that degree to resemble a speed in a car(works just fine)
What i want to do is automatically run through the indexes of the list items(0,1,2,3,....60) so the needle slowly rises till it gets to say 60 speed. Then, hold that for as long as i want so i can move a tack needle and run an odometer accordingly. i have tried to use a timer in a MVC class and use a stop watch. I can select the index 0 -6 but it will only just jump to the last one. I am trying to simulate a car dashboard the best i can...What are your thought?
public partial class MainWindow : Window
{
//DispatcherTimer timer;
List<Double> _items = new List<Double>();
DispatcherTimer timer;
public MainWindow()
{
this.InitializeComponent();
listBox1.ItemsSource = new List<double>() { 0,1,2,3,4,5,10,11,12,13,14, 15, 20, 25, 30, 35, 40 };
checkBox1.IsChecked = true;
park.Foreground = Brushes.Red;
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(2500);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
int second = DateTime.Now.Second;
firstDigitsec = second / 10;
int secondDigitsec = second % 10;
checkBox1.IsChecked = false;
first.Foreground = Brushes.Green;
park.Foreground = Brushes.White;
checkBox2.IsChecked = true;
Stopwatch stopwatch = Stopwatch.StartNew();
listBox1.SelectedIndex = 0;
listBox1.SelectedIndex = 1;
listBox1.SelectedIndex = 2;
listBox1.SelectedIndex = 3;
}
private int _stopw;
public int sw
{
get { return _stopw; }
set
{
_stopw = value;
OnPropertyChanged("");
}
}
private int _firstDigitsec;
public int firstDigitsec
{
get { return _firstDigitsec; }
set
{
_firstDigitsec = value;
OnPropertyChanged("");
/*
if (firstDigitsec < 1)
{
listBox1.SelectedIndex = 0;
}
if (firstDigitsec < 2)
{
*/
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
Since you are using WPF this sounds like an excellent candidate for Animation
Use a Binding to bind your Gauge's Progress property to the Progress property in your viewmodel and apply a DoubleAnimation to animate the Progress property from the previous value to the new value
With the code below, I get both the Firing C Binding message and the Firing F Binding message. Why is that? I'm expecting to only receive the Firing C Binding message.
class Program : Form
{
private Label lblC;
private Label lblF;
private Button btnCUp;
private Temp t;
public Program()
{
lblC = new Label();
lblF = new Label();
btnCUp = new Button();
t = new Temp();
lblC.Location = new Point(22, 21);
lblC.Size = new Size(35, 13);
Binding b = new Binding("Text", t, "C");
b.Format += new ConvertEventHandler(CLabelFormat);
lblC.DataBindings.Add(b);
lblF.Location = new Point(108, 21);
lblF.Size = new Size(35, 13);
Binding b2 = new Binding("Text", t, "F");
b2.Format += new ConvertEventHandler(FLabelFormat);
lblF.DataBindings.Add(b2);
btnCUp.Location = new Point(45, 55);
btnCUp.Text = "C Up";
btnCUp.Click += new EventHandler(btnCUp_Click);
this.ClientSize = new Size(165, 113);
this.Controls.Add(lblC);
this.Controls.Add(lblF);
this.Controls.Add(btnCUp);
}
private void CLabelFormat(object sender, ConvertEventArgs cevent)
{
MessageBox.Show("Firing C Binding");
}
private void FLabelFormat(object sender, ConvertEventArgs cevent)
{
MessageBox.Show("Firing F Binding");
}
[STAThread]
public static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Program());
}
private void btnCUp_Click(object sender, EventArgs e)
{
t.incC();
}
}
class Temp : INotifyPropertyChanged
{
private double tempC;
private double tempF;
public event PropertyChangedEventHandler PropertyChanged;
public Temp()
{
tempC = 0;
tempF = ctof(tempC);
}
private void NotifyPropertyChanged(String field)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(field));
}
}
private double ctof(double c)
{
return c * 9.0 / 5.0 + 32;
}
public void incC()
{
++tempC;
tempF = ctof(tempC);
NotifyPropertyChanged("C");
//NotifyPropertyChanged("F");
}
public double C
{
get { return tempC; }
}
public double F
{
get { return tempF; }
}
}
If I remove the comment in front of NotifyPropertyChanged("F"), then I get four message boxes when I press the button. Two for "Firing C Binding" and two for "Firing F Binding".
How can I modify the code to only get one of each?
EDIT: I've tried to look at the source for Binding (using Reflector) to see what it does when a PropertyChanged event is fired, but couln't find anything. Can anyone provide some insight? I want to confirm that it cares about what field is changed.
EDIT: Replaced my code with a full compilable example implementation that demonstrates the issue.
The PropertyChanged event is only firing once, which you can test yourself:
t.PropertyChanged += t_PropertyChanged;
void t_PropertyChanged(object sender, PropertyChangedEventArgs e) {
MessageBox.Show("Property Changed: " + e.PropertyName);
}
The Binding Format event is something else:
The Format event occurs whenever the Current value of the BindingManagerBase changes, which includes:
The first time the property is bound.
Any time the Position changes.
Whenever the data-bound list is sorted or filtered, which is accomplished when a DataView supplies the list.
So it would seem the Format event fires whenever a property changes in the current value.
I would presume that when one property changes it is indicating that it may have had an effect on another property, thereby resulting in that property firing an update as well. Quite often, property change events fire for an object even when it hasn't changed because the developer doesn't want to actually check for a change, and so just fires the event in scenarios where it might have changed. My gut instinct suggests this may be what you are experiencing.
However, it could also be that whatever is listening the the PropertyChanged event is not actually checking the name of the property that has changed. This would also cause the same issue.