I am trying to get the text of a label to update on the front of end of my app.
At the moment Im using Message Centre to send a notification up to the view model and increment a number that should update on the label in the view.
Im using Xamarin Forms and PCL.
I can get the number to log out in the debug so I know the message centre is working. But its not updating the view.
the relevant Xaml:
<Label Text="{Binding counter}"
Grid.Row="0"/>
The code behind:
public partial class DriverDashboardView : ContentPage
{
private DriverDashboardViewModel driverdashboardviewmodel;
public DriverDashboardView()
{
InitializeComponent();
this.Title = "Driver's Dashboard";
BindingContext = driverdashboardviewmodel = new DriverDashboardViewModel();
dataList.ItemTapped += DataList_ItemTapped;
}
private void DataList_ItemTapped(object sender, ItemTappedEventArgs e)
{
DisplayAlert("Route Information","Various Data","OK");
}
protected async override void OnAppearing()
{
base.OnAppearing();
await driverdashboardviewmodel.GetLabelInfo();
}
}
The View Model:
public class DriverDashboardViewModel:BaseViewModel,INotifyPropertyChanged
{
private int messageCounter { get; set; }
public string counter { get { return messageCounter.ToString(); }
set {
if (Equals(value, messageCounter)) return;
messageCounter = Convert.ToInt32(value);
OnPropertyChanged(nameof(counter));
} }
public DriverDashboardViewModel()
{
MessagingCenter.Subscribe<App>((App)Application.Current, "Increase", (variable) => {
messageCounter++;
});
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
And the relevant section that implements the message centre:
Foregroundmessages.cs:
MessagingCenter.Send((App)Xamarin.Forms.Application.Current, "Increase");
As stated the messaging centre works fine. It gets as far as the view model but doesnt update the counter variable to the view. I have tried setting the counter as an int and a string hence the conversion in the get and set.
I also tried observable collection but that seemed redundant because its a single variable not a collection or list.
Any ideas?
your code is updating the private messageCounter property, not the public counter property that you are binding to. Updating messageCounter does not cause PropertyChanged to fire.
MessagingCenter.Subscribe<App>((App)Application.Current, "Increase", (variable) => {
messageCounter++;
});
Related
I want to show the stream data to textbox in real time. But the textbox doesn't updated even the stream data has updated. I don't know what is wrong.
Here is my XAML code.
<TextBox Text="{Binding Path = marketPrice}" HorizontalAlignment="Left"/>
And this is View Model code.
public class OrderTestViewModel : INotifyPropertyChanged
{
public QuotesDataSource DataSource;
public string _marketPrice => DataSource.SymbolPrice;
public string marketPrice
{
get { return _marketPrice; }
set
{
RaisePropertyChanged("marketPrice");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
I checked the marketPrice is updated real time.
And the last is hidden code.
public partial class OrderTest : UserControl
{
OrderTestViewModel model = new OrderTestViewModel();
public OrderTest()
{
InitializeComponent();
DataContext = model;
}
}
Please help me.
It seems your marketPrice setter never update the value of _marketPrice (which will always show the same value.
Would you want something like :
public string _marketPrice = DataSource.SymbolPrice;
public string marketPrice
{
get { return _marketPrice; }
set
{
if (_marketPrice != value)
{
_marketPrice = value;
RaisePropertyChanged("marketPrice");
}
}
}
I want to update new value in UI when DataSource.SymbolPrice is updated. DataSource.SymbolPrice is updated periodly
Then you should bind directly to the SymbolPrice property and implement INotifyPropertyChanged and raise the PropertyChanged event in the QuotesDataSource class:
public class OrderTestViewModel
{
public QuotesDataSource DataSource;
public string marketPrice => DataSource.SymbolPrice;
}
Obviously some object must tell the UI when there is a update and this is the responsibility of the source object.
The view model cannot be supposed to know when the price is changed in the QuotesDataSource unless the latter tells it somehow, for example by raising an event.
I know that this had been answered multiple times before, and I've followed every possible guide.
It doesn't work.
Here is my code:
XAML
<Label Text="{Binding Path=StatusMessage, Mode=TwoWay}"
Margin="10,0,10,5"
VerticalOptions="End"/>
C#
private string statusMessage;
public string StatusMessage {
get { return statusMessage; }
set
{
statusMessage = value;
OnPropertyChanged(nameof(StatusMessage));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
The class extends INotifyPropertyChanged and to modify the label text I tried both
StatusMessage = "Status: ...";
and
Device.BeginInvokeOnMainThread(() => { StatusMessage = "Status: ...";});.
Nothing works.
Any idea how to fix this mess?
EDIT
Adding BindingContext = this; as suggested in the main helped.
Now it won't update the label from code called from a different thread, as follows
private void OnEnableUser(bool authenticated)
{
if (SynchronizationContext.Current != null)
{
[...]
} else
{
Device.BeginInvokeOnMainThread(() =>
{
OnEnableUser(authenticated);
});
}
}
Have you set the DataContext in the code behind?
this.DataContext = classWithStatusMessage;
I have an application with a front end, and instead of deleting objects right away we have a flag on every object that says whether it is supposed to be deleted, so it can be handled later. So the problem is when I am using the object in front end with a DataGrid in WinForms. When I set the Deleted flag I would like the object to not be displayed in the DataGrid, with the BindingList<> as the DataSource of the DataGrid. Is there a way to force a filter every time the DataGrid is repainted? would this be a function of the DataGrid? Or a function of the BindingList<>? For those who are more visual here is a code example. (WARNING this is a code example for conceptual purposes)
test.cs
public class Person : INotifyProperyChanged
{
public string Name { get; set; }
public int Id { get; set; }
private bool _isForDelete;
public bool IsForDelete
{
get { return _isForDelete; }
set
{
_isForDelete = value;
OnPropertyChanged("IsForDelete")
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
MyForm.cs
private BindingList<Person> _persons;
private void MyForm_Load(object sender, EventArgs e)
{
_persons = GetPersonsFromServer();
//Obviously this doesn't work, but I can dream. This is the basic idea.
_myDataGrid.DataSource = _persons.Where(x=>!x.IsForDelete);
}
private void DeleteBtn_Click(object sender, EventArgs e)
{
foreach(var row in _myDataGrid.SelectedRows)
{
var person = (Person)row.DataBoundItem;
person.IsForDelete = true;
}
}
Any suggestions?
One solution to your problem would be to loop through each row of the datagrid, get the object bound to it, check the property, and then if it is set to true suspend the binding, set the row to invisible and then resume the binding. Something like:
CurrencyManager cur = (CurrencyManager)datagrid.BindingContext[datagrid.Datasource];
cur.SuspendBinding();
datagridviewrow.Visible = false;
cur.ResumeBinding();
I'm new to WPF, so there's probably something basic I'm missing here. I have an application that looks like this:
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test Application" Height="647" Width="723" Background="#88B0FF">
<DockPanel Name="MainDock">
<Button DockPanel.Dock="Top" Margin="5,0,5,0" x:Name="PingButton" Click="PingButton_OnClick">Ping</Button>
<TextBox Text="{Binding Path=Output}" />
</DockPanel>
</Window>
The code-behind is like this:
public partial class MainWindow : Window
{
private Model _applicationModel = new Model();
public Model ApplicationModel {
get { return _applicationModel; }
set { _applicationModel = value; }
}
public MainWindow()
{
InitializeComponent();
this.DataContext = ApplicationModel;
ApplicationModel.Output = "Not clicked";
}
private void PingButton_OnClick(object sender, RoutedEventArgs e)
{
ApplicationModel.Output = "Clicked";
}
}
I have a small class called Model that implements INotifyPropertyChanged.
public class Model : INotifyPropertyChanged
{
public string Output { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I run this application, and the text box displays the text "Not clicked". When I click the button, I would expect that the text would change. It does not. The "ApplicationModel" object is updated, and this is reflected in the DataContext; I have a breakpoint in the OnPropertyChanged() method, however, and it appears that it's never being called.
What am I doing wrong?
OnPropertyChanged() isn't being called because you're not calling it.
There is no special magic that wires up calls to OnPropertyChanged by itself, so you need to do it yourself.
Specifically, you should modify your Output property to call it when it changes (and it wouldn't hurt to do the same for your ApplicationModel property:
private string output;
public string Output
{
get { return output; }
set
{
if (output != value)
{
output = value;
OnPropertyChanged("Output");
}
}
}
If you're targeting .NET 4.5 you can utilize the CallerMemberName attribute to reduce boilerplate code; This article explains how to do so. Then you'll have something like this:
private string output;
public string Output
{
get { return output; }
set { SetProperty(ref output, value); }
}
If you're using .NET 4.0 or below, you can use expression trees, as described in this answer.
Title pretty much says it all. The score is being displayed as 0 (which is what I initialized it to). However, when updating the Score it's not propagating to the UI textBlock. Thought this would be pretty simple, but I'm always running into problems making the switch from Android :) Am I suppose to be running something on the UI thread??
I'm trying to bind to the "Score" property.
<TextBox x:Name="text_Score" Text="{Binding Score, Mode=OneWay}" HorizontalAlignment="Left" Margin="91,333,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top" Height="148" Width="155" FontSize="72"/>
Here is my holder class
public class GameInfo
{
public int Score { get; set; }
public int counter = 0;
}
**Note: Make sure you don't forget to add {get; set;} or else nothing will show up.
and this is where I'm trying to set it
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
info.counter = (int)e.Parameter;
text_Score.DataContext = info;
}
P.S. To reiterate, I'm going for OneWay. I only want to display the score and have it undated when the variable changes. I plan on disabling user input.
Here is the full working code example. The only thing that had to change was my holder class. Thanks Walt.
public class GameInfo : INotifyPropertyChanged
{
private int score;
public int Score {
get { return score; }
set
{
if (Score == value) return;
score = value;
NotifyPropertyChanged("Score");
}
}
public int counter = 0;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
In XAML binding your underlying class needs to inform the binding framework that the value has changed. I your example, you are setting the counter in the OnNavigatedTo event handler. But if you look at your GameInfo class, it's a simple data object.
The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed. So in your case, change the class as follows
public class GameInfo : INotifyPropertyChanged
{
private int _score;
public int Score
{
get
{
return this._score;
}
set
{
if (value != this._score)
{
this._score = value;
NotifyPropertyChanged("Score");
}
}
}
public int counter = 0; // if you use _score, then you don't need this variable.
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
See the MSDN article for more information INotifyPropertyChanged