Bound target not updating when button pressed to change value - c#

I have some code which uses a form. The form is bound to my class, FormData. I have binding working well and updating my formData (local instance), but when I try to change the value of one of the variables in formData on button click/LostFocus trigger, it doesn't update.
Here's my relevant XAML:
<TextBox x:Name="friendly_name_textBox"
Style="{StaticResource TextErrorStyle}"
Text="{Binding
PrimaryUserName,
Mode=TwoWay,
ValidatesOnExceptions=True,
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged,
NotifyOnValidationError=True}"
HorizontalAlignment="Left"
Margin="0,75,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="120"/>`
The button trigger (which does get run):
private void Button_Click(object sender, RoutedEventArgs e)
{
formData.PrimaryUserName = "TEST";
}
And my FormData code:
public string PrimaryUserName
{
get
{
return primaryUserNameValue;
}
set
{
if(primaryUserNameValue != value)
{
primaryUserNameValue = value;
}
}
}

You need to implement the INotifyPropertyChanged interface and raise the PropertyChanged event in your formData class:
public class formData : INotifyPropertyChanged
{
private string primaryUserNameValue;
public string PrimaryUserName
{
get
{
return primaryUserNameValue;
}
set
{
if (primaryUserNameValue != value)
{
primaryUserNameValue = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

Your Class needs to implement INotifyPropertyChanged, so that the target knows if the source property changes:
https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-implement-property-change-notification
It's really easy, please have a look at the documentation and adjust your code accordingly. Your Property would have to look like this:
public string PrimaryUserName
{
get
{
return primaryUserNameValue;
}
set
{
if(primaryUserNameValue != value)
{
primaryUserNameValue = value;
OnPropertyChanged("PrimaryUserName");
}
}
}
But you also need the event and onPropertyChanged function to make it work.
Happy Coding!

Related

WPF Application Data Binding works on initialization, but not updating

I am working on a WPF application and I am trying to have text on the UI update after a button has been pressed. The data binding works for the initialization but when I try to change the text in the code after the button has been pressed, it does not update.
public class UpdateWindow : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _textStatus;
private readonly DeviceConnection deviceConnection = new DeviceConnection();
private readonly GetFile getFile = new GetFile();
public UpdateWindow()
{
TextStatus = "This will update the text";
}
//The button calls this WriteCommand function
public void WriteCommand()
{
TextStatus="The text on the UI should update here but it doesn't";
if (getFile.FindFile())
{
if (deviceConnection.CheckDeviceConnection())
{
//do something
}
else
{
//do something else
}
}
}
protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
{
if (!Equals(field, newValue))
{
field = newValue;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
return true;
}
return false;
}
public string TextStatus { get => _textStatus; set => SetProperty(ref _textStatus, value); }
}
And here is my XAML
<TextBlock x:Name="StatusText" Text="{Binding Path=TextStatus, UpdateSourceTrigger=PropertyChanged}" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" HorizontalAlignment="Center" />
I'm just not sure why it can initialize but not update.

Unable to Update XAML TextBlock Text Binding

I have a TextBlock in XAML that's bound to a property called EditsWarning:
<TextBlock DockPanel.Dock="Top" Text="{Binding EditsWarning, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Style="{DynamicResource Esri_TextBlockRegular}" HorizontalAlignment="Left" FontSize="14" FontWeight="DemiBold" VerticalAlignment="Center" Margin="10,0,10,5" TextWrapping="WrapWithOverflow"/>
The Definition for the EditsWarning Property is here:
public string EditsWarning
{
get { return editsWarningMessage; }
set
{
SetProperty(ref editsWarningMessage, value, () => this.EditsWarning);
}
}
The EditsWarning Property is set to an instance of a class like this:
editsWarning = new OutstandingEditsTextBlock();
editsWarningMessage = editsWarning.EditsWarningMessage.ToString();
And the OutstandingEditsTextBlock class is here, and implements INotifyPropertyChanged
internal class OutstandingEditsTextBlock : INotifyPropertyChanged
{
private string editsWarning;
public OutstandingEditsTextBlock()
{
if (Project.Current.HasEdits)
{
this.editsWarning = "This session/version has outstanding edits.";
}
else
{
this.editsWarning = string.Empty;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public string EditsWarningMessage
{
get { return this.editsWarning; }
set
{
this.editsWarning = value;
this.OnPropertyChanged("EditsWarningMessage");
}
}
public void OnPropertyChanged(string propertyName)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
I noticed that I can get it to display either value, however, I can never get it to update in the same debugging session. In fact, it looks like the setter for the public property is never hit.
Can someone please help me figure out what I'm doing wrong?
Thank you.

Calculated field not updating until edited in UI

I'm trying to test out data binding with XAML and C# as a novice programmer. I have two sliders that are bound to properties and I want to update a TextBox with the sum of the two values of the properties set by the sliders.
I'm using INotifyPropertyChanged and tried changing every property I could find but I can't get the textbox to update until I edit the textbox, at which point, the textbox updates to the correct value. Using UpdateSourceTrigger=PropertyChanged only updates the textbox as soon as I edit the textbox instead of when I select another element. I've tried writing a separate event handler that doesn't use [CallerNameMember] and uses a specified property but it didn't seem to change anything.
<Grid>
<Grid.RowDefinitions>
</Grid.RowDefinitions>
<TextBox Grid.Row="0"
Text="{Binding BoundNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
FontSize="20"
FontWeight="Bold"
AllowDrop="False" />
<Slider Grid.Row="1"
Value="{Binding BoundNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Maximum="100"
Minimum="10"
IsSnapToTickEnabled="True"
TickFrequency="10" />
<TextBox Grid.Row="2"
Text="{Binding BoundNumber2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
AllowDrop="False" />
<Slider Grid.Row="3"
Value="{Binding BoundNumber2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Maximum="100"
Minimum="10"
IsSnapToTickEnabled="True"
TickFrequency="10" />
<TextBox Grid.Row="4"
Name="MathBox"
Text="{Binding QuickMath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}">
</TextBox>
</Grid>
public partial class OrderScreen : INotifyPropertyChanged
{
public OrderScreen()
{
DataContext = this;
InitializeComponent();
}
private int quickMath;
public int QuickMath
{
get { return _boundNumber + _boundNumber2; }
set
{
if (value != quickMath)
{
quickMath = value;
OnPropertyChanged();
}
}
}
private int _boundNumber;
public int BoundNumber
{
get { return _boundNumber; }
set
{
if (_boundNumber != value)
{
_boundNumber = value;
// MathBox.Text = quickMath.ToString();
OnPropertyChanged();
}
}
}
private int _boundNumber2;
public int BoundNumber2
{
get { return _boundNumber2; }
set
{
if (_boundNumber2 != value)
{
_boundNumber2 = value;
MathBox.Text = quickMath.ToString();
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
I can get it to work with the commented out MathBox.Text = quickMath.ToString(); but I was hoping there was a better way to do this with data binding. Thanks in anticipation!
Binding mechanism subscribes to the PropertyChanged event of DataSource object, so there is no need to "initialize" the event along with the INPC implementation, but as you might have noticed, PropertyChanged event for the QuickMath property is indeed never triggered when BoundNumber or BoundNumber2 are changed.
You can fix it in different ways, e.g. explicitly call OnPropertyChanged for all affected properties:
private int _boundNumber;
public int BoundNumber
{
get { return _boundNumber; }
set
{
if (_boundNumber != value)
{
_boundNumber = value;
OnPropertyChanged();
OnPropertyChanged(nameof(QuickMath));
}
}
}
Note that this way you can keep QuickMath property a read-only. This approach works nicely in other situations, like with time-related properties, say if your data source property formats a string like "Edited 2 minutes ago" based on a recorded timestamp and current time and you call PropertyChanged as a timed task.
public int QuickMath => _boundNumber + _boundNumber2;
Alternatively, you can update QuickMath along with modifying BoundNumber and BoundNumber2 to trigger OnPropertyChanged() call inside QuickMath setter:
private int _boundNumber2;
public int BoundNumber2
{
get { return _boundNumber2; }
set
{
if (_boundNumber2 != value)
{
_boundNumber2 = value;
OnPropertyChanged();
QuickMath = BoundNumber + BoundNumber2;
}
}
}
This makes sense if the logic in QuickMath wouldn't allow making it a read-only property. In this case you have to adjust the getter accordingly and use private or protected setter there to avoid data inconsistency and unexpected behavior.
private int _quickMath;
public int QuickMath
{
get { return _quickMath; }
private set
{
if (value != _quickMath)
{
_quickMath = value;
OnPropertyChanged();
}
}
}
In both cases there is no need for two-way binding to QuickMath:
<TextBlock Grid.Row="4" Text="{Binding QuickMath, Mode=OneWay}"/>
On a side-note and looking at the rest of the code, it really worth mentioning that binding mechanism is expected to segregate UI from the data, where XAML knows about data source object properties (names and types) but not about it's internal implementation, while data source object can have no knowledge about XAML at all. So
there should be no calls from data object to FrameworkElements like MathBox.Text
it's considered a good design to have data object class completely separate from the page or control class.
Hope this helps.
You haven't initialized your PropertyChanged event anywhere, so it will never be called. Declare and initialize it like so:
public event PropertyChangedEventHandler PropertyChanged = delegate { };
A TextBox bound to the calculated property QuickMath should receive PropertyChanged event from it in order to update the text in the field.
Despite your OrderScreen implementing the INotifyPropertyChanged interface, it will not raise the event when QuickMath is changed because its setter (where the raising of the event is located) is never called. You can fix it, for example, by calling the QuickMath setter from the independent properties setters as suggested in other answers or delegate that work to DependenciesTracking lib:
public class OrderScreen : INotifyPropertyChanged
{
private readonly IDependenciesMap<OrderScreen> _dependenciesMap =
new DependenciesMap<OrderScreen>()
.AddDependency(i => i.QuickMath, i => i.BoundNumber + i.BoundNumber2, i => i.BoundNumber, i => i.BoundNumber2);
public OrderScreen() => _dependenciesMap.StartTracking(this);
private int _boundNumber2;
private int _boundNumber;
private int _quickMath;
public int QuickMath
{
get => _quickMath;
private set
{
if (value != _quickMath)
{
_quickMath = value;
OnPropertyChanged();
}
}
}
public int BoundNumber
{
get => _boundNumber;
set
{
if (_boundNumber != value)
{
_boundNumber = value;
OnPropertyChanged();
}
}
}
public int BoundNumber2
{
get => _boundNumber2;
set
{
if (_boundNumber2 != value)
{
_boundNumber2 = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string? propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public class Tests_SO_56623403
{
[Test]
public void Test_SO_56623403()
{
var sut = new OrderScreen();
var raisedEventsCount = 0;
sut.PropertyChanged += (_, args) =>
{
if (args.PropertyName == nameof(OrderScreen.QuickMath))
++raisedEventsCount;
};
Assert.Multiple(() =>
{
Assert.That(sut.QuickMath, Is.EqualTo(0));
Assert.That(raisedEventsCount, Is.EqualTo(0));
});
sut.BoundNumber = 12;
Assert.Multiple(() =>
{
Assert.That(sut.QuickMath, Is.EqualTo(12));
Assert.That(raisedEventsCount, Is.EqualTo(1));
});
sut.BoundNumber2 = 40;
Assert.Multiple(() =>
{
Assert.That(sut.QuickMath, Is.EqualTo(52));
Assert.That(raisedEventsCount, Is.EqualTo(2));
});
}
}

wpf bind textbox to List - MainWindow.List.Item[0]

I have a static List in my MainWindow. If changes occur, CurrValue is set immediately.
public static List<varVisu> varVisuListDll = new List<varVisu>();
In my class, there is a INotifyPropertyChanged implementation
public string m_CurrValue;
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string propertyName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public string CurrValue
{
get { return m_CurrValue; }
set
{
if (value != m_CurrValue)
{
//set value
m_CurrValue = value;
//notify anyone who cares about it
Notify("CurrValue");
}
}
}
This works fine, but now, I want to bind a Textbox (Text) in Window#2 to the first item (varVisuListDll[0].CurrValue) in this List.
How can I bind the TextBox.Text to this value (Text={Path, UpdateSourceTrigger ...}??
<TextBox x:Name="txtManualMode" Text="{Binding ElementName=????, Path=CurrValue, UpdateSourceTrigger=PropertyChanged}"
I have tested with (dtgrVariables.ItemSource=MainWindow.varVisuListDll). This work's.
Please help me ..
I've solved the problem.
I set a binding in code behind. That work's fine.
varVisu v1 = MainWindow.varVisuListDll[1];
txtManualMode.DataContext = v1;
Binding binding = new Binding() { Path = new PropertyPath("CurrValue") };
txtManualMode.SetBinding(TextBox.TextProperty, binding);
varVisuListDll must be a property, not a field:
private static List<varVisu> varVisuListDll = new List<varVisu>();
public static List<varVisu> VarVisuListDll
{
get { return varVisuListDll; }
}
Then the binding should look like this:
<TextBox Text="{Binding Path=(local:MainWindow.VarVisuListDll)[0].CurrValue}"/>
Or, if you're using an older framework than .NET 4:
<TextBox Text="{Binding Path=[0].CurrValue,
Source={x:Static local:MainWindow.VarVisuListDll}}"/>

DataBinding over a property in viewModel

surely this question has beed debated thousand of time, but I do not find any suitable solution to my need. I am new to SilverLIght and I intended to start well using MVVM.
therefore I did the following view model :
public class MyViewModel
{
private IRepository _Repository;
public string CountText { get; set; }
public MyViewModel (IRepository repository)
{
_Repository = repository;
CountText = "test ctor";
}
public void MyButtonCommand()
{
_Repository.GetResult((Result r) => MyActionAsync(r), (Exception e) => ManageException(e));
}
public void MyActionAsync(SchedeConsunitiviResult result)
{
CountText = string.Format("{0} items", result.Count);
}
public void ManageException(Exception e)
{
//to log the exception here and display some alert message
}
}
and here my xaml :
<sdk:Label Content="{Binding Path=CountText, Mode=TwoWay}" Grid.Row="3" Height="28" HorizontalAlignment="Left" Margin="12,142,0,0" Name="label1" VerticalAlignment="Top" Width="120" Grid.ColumnSpan="2" />
The first instanciation of CountText is visible in the Label. But the second one after the async method does not change the content of the LAbel. Should I add some mechanism like PropertyChanged in order to tell the view this property has changed? if so, how can I do that using xaml only?
thx for your help
Implement the INotifyPropertyChanged and notify that your property has changed with the EventHandler.
public class MyViewModel : INotifyPropertyChanged
{
private string countText;
public string CountText
{
get { return this.countText; }
set { this.countText = value; NotifyPropertyChanged("CountText"); }
}
.....snip.....
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(params string[] properties)
{
if (PropertyChanged != null)
{
foreach (string property in properties)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
As far as I know you do need a mechanism like PropertyChanged in your viewmodel

Categories