I would like to display the name of the object currently built in a label (Windows Form)
My label doesn't refresh in real-time. When I put a DialogBox or something else after each iteration, the label takes the good value and is well updated, but when I let my code run without "breaks", the label seems to bug and his value never changes...
I have the same kind of code for a progress bar and everything is working well.
ModelBuilder.cs
public event PropertyChangedEventHandler PropertyChanged;
private Substation currentSubstation;
public Substation CurrentSubstation
{
get
{
return this.currentSubstation;
}
set
{
if (value != this.currentSubstation)
{
this.currentSubstation = value;
NotifyPropertyChanged();
}
}
}
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public void Build()
{
foreach (string substationUri in substationsUri)
{
Substation substation = new Substation(new Uri(substationUri));
this.CurrentSubstation = substation;
/* code */
}
}
View.cs
private void StartImportation_Click(object sender, EventArgs e)
{
this.model = new ModelBuilder(....);
this.myLabel.DataBindings.Add(new Binding("Text", this.model, "CurrentSubstation.name"));
this.model.Build();
}
Related
I have a view model that has several properties that are databound to several controls.
When I raise PropertyChanged on one of them, the controls unexpectedly all update. I would expect only the one I am raising the event on to update.
For my form, I have this:
public partial class MainForm : Form
{
AmountCalculatorVM amountCalculatorVM;
public MainForm()
{
InitializeComponent();
}
private void setBindings()
{
textBoxTotalAmount.DataBindings.Add("Text", amountCalculatorVM, "TotalAmount");
textBoxAverage.DataBindings.Add("Text", amountCalculatorVM, "Average",true, DataSourceUpdateMode.Never,null, "#.00");
textBoxCount.DataBindings.Add("Text", amountCalculatorVM, "Count");
listBoxLineAmounts.DataSource = amountCalculatorVM.Amounts;
}
private void MainForm_Load(object sender, EventArgs e)
{
amountCalculatorVM = new AmountCalculatorVM();
setBindings();
}
private void buttonAddAmount_Click(object sender, EventArgs e)
{
if (int.TryParse(textBoxLineAmount.Text.Replace(",", ""), out int amount))
{
amountCalculatorVM.Amounts.Add(amount);
textBoxLineAmount.Text = "";
textBoxLineAmount.Focus();
}
}
private void buttonClear_Click(object sender, EventArgs e)
{
textBoxLineAmount.Text = "";
amountCalculatorVM.Amounts.Clear();
textBoxLineAmount.Focus();
}
}
Then, for my view model, I have this:
class AmountCalculatorVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private readonly AmountList amounts;
public BindingSource Amounts { get; }
public int TotalAmount => amounts.TotalAmount;
public int Count => amounts.Count;
public decimal Average => amounts.Average;
public AmountCalculatorVM()
{
amounts = new AmountList();
Amounts = new BindingSource();
Amounts.DataSource = amounts;
Amounts.ListChanged += Amounts_ListChanged;
}
private void Amounts_ListChanged(object sender, ListChangedEventArgs e)
{
//Any one of these will cause all three textboxes to update in the form
//I would think that with Count and Average commented out, the Count and
//Average textboxes would not update.
OnPropertyChanged("TotalAmount");
//OnPropertyChanged("Count");
//OnPropertyChanged("Average");
//Using any other word will not
//OnPropertyChanged("SomeOtherRandomWord");
}
}
Here is the AmountList class for reference:
class AmountList : List<int>
{
public int TotalAmount
{
get
{
int total = 0;
foreach (int amount in this)
{
total += amount;
}
return total;
}
}
Now, unexpectedly, all three textboxes update if an item is added to the amounts list, which fires ListChanged, and then in turn, the PropertyChanged event.
It doesn't matter which of the three properties I fire PropertyChanged on, but it won't work if I use a different value - it needs to be either TotalAmount, Count, or Average.
I can't understand this behaviour. I would have expected only the text box bound to TotalAmount to be updated, and not the other two, since nothing seems to be notifying them that an update has occurred.
Any ideas?
Why don't you implement the propertychanged like this:
public class Data : INotifyPropertyChanged
{
// boiler-plate
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
// props
private string name;
public string Name
{
get { return name; }
set { SetField(ref name, value, "Name"); }
}
}
You can control now, in the setter, which property fires the event:
private string name;
public string Name
{
get { return name; }
set { SetField(ref name, value, "Name"); }
}
you know what I mean?
I am DataBinding to a readonly property to the Text property of a Label in Windows Forms.
lblSourceLoggingState.DataBindings.Add("Text", machine, "Status");
My problem with this is the text only gets updated at startup of the form.
I am implementing INotifyPropertyChanged in a base class like this:
protected void SetValue<T>(Expression<Func<T>> property, T value)
{
LambdaExpression lambdaExpression = property;
if (lambdaExpression == null)
{
throw new ArgumentException("Lambda expression return value can't be null", "property");
}
string propertyName = PropertyName.GetMemberName(lambdaExpression);
T storedValue = getValue<T>(propertyName);
if (Equals(storedValue, value))
return;
_propertyValueStorage[propertyName] = value;
OnPropertyChanged(propertyName);
}
public SynchronizationContext SyncContext;
protected ViewModelBase()
{
SyncContext = SynchronizationContext.Current;
_propertyValueStorage = new Dictionary<string, object>();
}
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
if (SyncContext != null)
SyncContext.Send(obj => handler(this, new PropertyChangedEventArgs(propertyName));
else
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
And here the Code in the machine object for Status Property:
public short Status
{
get { return GetValue(() => Status); }
private set { SetValue(() => Status, value); }
}
While debugging and stepping through the SetValue or OnPropertyChanged methods the Label text gets updated, but if i run the application without breakpoints it wont.
Also if i register to the PropertyChangedEvent like this:
machine.PropertyChanged += delegate(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == "Status")
lblSourceLoggingState.Text = machine.Status.ToString();
};
the label text also gets updated like it should so what could go wrong with the DataBindings?
I've got 2 projects, one containing my model, the other one containing my view (Windows Form).
I tried to refresh my view, specifically a Label in accordance with model changes during my build() method using bindings but that didn't work. I don't know if my code is wrong or if it is impossible.
Edit : Actually, it seems that a label needs an Update() or Refresh() call to be updated graphically in his window... That can explain my problem
there is my Model class :
// ModelBuilder : INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private Substation currentSubstation;
public Substation CurrentSubstation
{
get
{
return this.currentSubstation;
}
set
{
if (value != this.currentSubstation)
{
this.currentSubstation = value;
NotifyPropertyChanged("CurrentSubstation");
}
}
}
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public void Build()
{
foreach (Uri substationUri in substationsUri)
{
Substation substation = new Substation(substationUri); // long process
this.CurrentSubstation = substation;
}
}
There is my view
private void StartImportation_Click(object sender, EventArgs e)
{
this.model = new ModelBuilder();
// Old mistake:
//this.timeLabel.DataBindings.Add(new Binding("Text", this.model.CurrentSubstation,"name"));
this.timeLabel.DataBindings.Add(new Binding("Text", this.model, "CurrentSubstation.name"));
this.model.Build(); // I'd like to see the current substation created name
}
The reason is that you are binding to the initial this.model.CurrentSubstation but when you Build() the ModelBuilder a new Substation is assigned. However the old CurrentSubstation never changed during that process.
new Binding("Text", this.model.CurrentSubstation, "name")
Change the binding to
new Binding("Text", this.model, "CurrentSubstation.name")
So I am trying to implement the MVVM pattern in a simple sample app. Essentially my app allows a user to choose from a list of search providers in a SettingsPage, and then in the MainPage when the user clicks the 'search' button he or she will be navigated to the search provider's website. Everything seems to work ok, no errors, except when navigating directly back to MainPage from SettingsPage the search property does not seem to be updated. Everything is fine though when the application is completely exited and launched fresh. What I have is as follows
MainPage.xaml.cs
void search_Click(object sender, EventArgs e)
{
TheBrowser.Navigate(App.ViewModel.SearchProvider.Address);
}
App.xaml.cs
private static MainViewModel viewModel = null;
public static MainViewModel ViewModel
{
get
{
// Delay creation of the view model until necessary
if (viewModel == null)
viewModel = new MainViewModel();
return viewModel;
}
}
MainViewMode.cs
public ListItem SearchProvider { get; private set; }
public MainViewModel()
{
SearchProvider = Settings.SearchProvider.Value;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
and in my SettingsPage is where I am allowin ga user to select a search provider
SettingsPage.xaml.cs
private void PopulateSearchProviderList()
{
searchProviderList = new ObservableCollection<ListItem>();
searchProviderList.Add(new ListItem { Name = "Bing", Address = "http://www.bing.com" });
searchProviderList.Add(new ListItem { Name = "Google", Address = "http://www.google.com" });
SearchProviderListPicker.ItemsSource = searchProviderList;
}
private void stk_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
if (SearchProviderListPicker.SelectedIndex != -1)
{
var selectedItem = (sender as StackPanel).DataContext as TestApp.Classes.ListItem;
Settings.SearchProvider.Value = selectedItem; //Setting the search provider
}
}
and finally my ListItem class which is fairly straightforward
ListItem.cs
public string Name
{
get;
set;
}
public string Address
{
get;
set;
}
So essentially I am not updating the ViewModel correctly based on the SettingsPage, but I am unsure of how to go about this properly.
You have to call the OnNotifyPropertyChanged("propertyName") for the item to update in the UI.
For example (assuming the Name and Address properties are bound to your UI elements.)
private string name;
private string address;
public string Name
{
get { return name;}
set {
name = value;
OnNotifyPropertyChanged("Name");
}
}
public string Address
{
get { return address; }
set {
address = value ;
OnNotifyPropertyChanged("Address");
}
}
There are a few issues I can see. We'll start from there.
Your MainViewModel needs to implement INotifyPropertyChanged see here
Your SearchProvider setter needs to raise PropertyChanged
You need to set the value of the SearchProvider. Currently that is only performed in the constructor which is probably why you are seeing things working on app startup only.
You need to make sure you are correctly binding the value of SearchProvider in your xaml. If you post your xaml we can check that out too.
In your ViewModel, add:
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string caller = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
}
Update the SearchProvider property to something like:
private ListItem searchProvider;
public ListItem SearchProvider
{
get { return searchProvider; }
set
{
searchProvider = value;
OnPropertyChanged();
}
}
I have a silverlight application that simply plots points on a graph. The points that it plots come from a sql query.
The silverlight program will have to run the query and pull the relevant data on its own.
How do i achieve this kind of functionality??
thanks!
You don't say how many times it needs to plot the data per sec/min? Is it just to plot once. If so then when your app first loads write an asynchronous call and have the call query sql and return the results in the callback..
If the program needs to return data at set intervals then you'll need a dispatcher timer or something similar..
Ok something like this..
public class MyClass : INotifyPropertyChanged
{
public MyClass()
{
DispatcherTimer timer = new DispatcherTimer();
timer.Tick += OnTimerTick;
timer.Interval = TimeSpan.FromSeconds(300);
}
private void OnTimerTick(object sender, EventArgs e)
{
var result = await UpdateGraphPoints();
MyGraphPoints = this.PopulateTheGraph(result);
}
private async Task<List<MyGraphPoint>> UpdateGraphPoints()
{
var oper = await YourDatabaseQueryMethod();
return oper;
}
private ObservableCollection<MyGraphPoint> PopulateTheGraph(object result)
{
}
private ObservableCollection<MyGraphPoint> myGraphPoints;
public ObservableCollection<MyGraphPoint> MyGraphPoints
{
get { return this.myGraphPoints; }
set
{
myGraphPoints = value;
OnPropertyChanged("MyGraphPoints");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class MyGraphPoint : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int xValue;
public int XValue
{
get { return xValue; }
set
{
this.xValue = value;
this.OnPropertyChanged("XValue");
}
}
private int yValue;
public int YValue
{
get { return yValue; }
set
{
this.yValue = value;
this.OnPropertyChanged("YValue");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And then in your xaml - bind the MyGraphPoints observable collection to your graph control.