C# MVVM How to update viewmodel string from the model - c#

I am really new to mvvm and wpf in c# and got stuck at some very basic stuff.In this example I am using Fody.PropertyChanged. I have a basic viewmodel that holds a string called Test which is binded to a textblock.
public class Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
public string Test { get; set; }
}
Then,in a separate file and class called Data,I have a simple function that increments an int and converts it to a string.
public class Data
{
public static int i = 0;
public static string IncTest { get; set; }
public static void Inc()
{
i++;
IncTest = i.ToString();
}
}
How do I update the Test variable inside the viewmodel when calling the Inc() function? For example, when clicking a button
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new Model();
Data.Inc();
}
private void Increment_Click(object sender, RoutedEventArgs e)
{
Data.Inc();
}

In MVVM, the model does not update the view model, its actually opposite, The view model updates the model properties.
Here is an example.
MODEL:
public class Model
{
public string Test
{
get;
set;
}
}
View Model:
public class ViewModel : INotifyPropertyChanged
{
private Model _model;
public string Test
{
get
{
return _model.Test;
}
set
{
if(string.Equals(value, _model.Test, StringComparison.CurrentCulture))
{
return;
}
_model.Test = value;
OnPropertyChanged();
}
}
public ViewModel(Model model)
{
_model = model;
}
}
Your views will bind to your view models.
UPDATE: In regards to your question
public class SomeClass
{
public static void Main(string [] args)
{
Model model = new Model();
ViewModel viewModel = new ViewModel(model);
//Now setting the viewmodel.Test will update the model property
viewModel.Test = "This is a test";
}
}

Related

In Winform why ALL bound properties are updated when I call PropertyChanged on ONE data source?

I have two buttons and bind their property to two properties of a data object.
But every property is updated when I call PropertyChanged of the data object.
public partial class Form1 : Form
{
private DataClass data = new DataClass();
public Form1()
{
InitializeComponent();
ButtonA.DataBindings.Add("Text", data, "DataA");
ButtonB.DataBindings.Add("Text", data, "DataB");
ButtonB.Click += new EventHandler(OnButtonBClicked);
}
private void OnButtonBClicked(object sender, EventArgs e)
{
data.DataA += "1";
data.DataB += "1";
data.Notify("DataB");
}
}
public class DataClass : INotifyPropertyChanged
{
public string DataA { get; set; }
public string DataB { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public DataClass() {}
public void Notify(string property_name)
{
PropertyChanged(this, new PropertyChangedEventArgs(property_name));
}
}
When I press ButtonB (which means I call PropertyChanged(this, new PropertyChangedEventArgs("DataB"))), both ButtonA and ButtonB show new text.
If I call PropertyChanged(this, new PropertyChangedEventArgs("DataA")), both buttons are updated.
If I don't change value of DataA / DataB and just call PropertyChanged(this, new PropertyChangedEventArgs("DataB")), still both buttons are updated (can be noticed by breakpoint debugging).
If I call PropertyChanged(this, new PropertyChangedEventArgs("QQQ")), then no button is updated.
PropertyChangedEventArgs has a property named propertyName, I thought it's used to specify one property to notify but it doesn't.
In my real code, DataB changes much more frequently than DataA. I don't want to update ButtonA each time DataB is changed, it takes too much time.
Question: why would this happen? When a data source property is changed, how can I only update properties really connected to it?
(All code is .Net Framework 4.7.1 on Windows.)
#Jimi's method works.Simple and effective.I put each property in a shell class and bind data to the shell:
public class MyProperty<T>: INotifyPropertyChanged
{
public T Content { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public MyProperty(T _content)
{
Content = _content;
}
public void Notify()
{
PropertyChanged(this, new PropertyChangedEventArgs("Content"));
}
}
public class DataClass
{
public MyProperty<string> DataA = new MyProperty<string>("");
public MyProperty<string> DataB = new MyProperty<string>("");
public DataClass() {}
}
But in this way I must use DataA.Content+="1" instead of DataA+="1" every where.
I decide to use a base class to create all shells.But my real DataClass must inherit from other class and C# don't support multi-inherit.So I have to use a extension class.
public class BindHandle<T> : INotifyPropertyChanged
{
public T Content { get { return (T)parent.GetType().GetProperty(prop_name).GetValue(parent); } }
private object parent;
private string prop_name;
public event PropertyChangedEventHandler PropertyChanged;
public BindHandle(object _parent, string _prop_name)
{
parent = _parent;
prop_name = _prop_name;
}
public void NotifyChange()
{
PropertyChanged(this, new PropertyChangedEventArgs("Content"));
}
}
public interface IBindHandleProvider
{
BindHandleProvider provider { get; set; }
}
public class BindHandleProvider
{
private Dictionary<string, object> handle_map = new Dictionary<string, object>();
public BindHandle<T> GetBindHandle<T>(object obj,string property_name)
{
if (!handle_map.ContainsKey(property_name))
handle_map.Add(property_name, new BindHandle<T>(obj, property_name));
return (BindHandle<T>)handle_map[property_name];
}
public void NotifyChange<T>(string property_name)
{
if (handle_map.ContainsKey(property_name))
((BindHandle<T>)handle_map[property_name]).NotifyChange();
}
}
public static class BindHandleProviderExtension
{
public static void NotifyChange<T>(this IBindHandleProvider obj, string property_name)
{
obj.provider.NotifyChange<T>(property_name);
}
public static BindHandle<T> GetBindHandle<T>(this IBindHandleProvider obj, string property_name)
{
return obj.provider.GetBindHandle<T>(obj,property_name);
}
}
public class DataClass:IBindHandleProvider
{
public BindHandleProvider provider { get; set; } = new BindHandleProvider();
public string DataA { get; set; } = "";
public string DataB { get; set; } = "";
public DataClass(){ }
}
Then bind it like
ButtonA.DataBindings.Add("Text", data.GetBindHandle<string>("DataA"), "Content");
And notify like
data.NotifyChange<string>("DataB");
It's kinda complex but works well.

How to setup properties in a ViewModel that gets and sets values from a list of models?

I am still fairly new to MVVM and need some guidance.
I have setup the below MVVM and this works fine when using:
private Model model = new Model();
However, I actually want to have a collection of the models.
public ObservableCollection<Model> models = new ObservableCollection<Model>();
What I can't quite figure out right now is how to get the properties to return for instance the title of the bound / selected model.
I can't specify an index because I don't know the indexes yet. I'm thinking in the View, I'll add a hidden combobox and when the window is loaded, create a new model, and select the highest index. But how do I ensure the right property is returned and how to code that?
CURRENT SETUP
View
The view is binded to the ViewModel as follows.
<Window.DataContext>
<this:ViewModel />
</Window.DataContext>
ViewModel
The ViewModel is composed of two classes. A parent class which implements INotifyPropertyChanged and the ViewModel for the current View.
using System.ComponentModel;
namespace Framework
{
public abstract class ObservableObject : INotifyPropertyChanged
{
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Implementation
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}
using Framework;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using System.Windows;
namespace ESL_Master_Suite.Components.Core.Courses
{
/// <summary>
/// Interaction logic for Courses.xaml
/// </summary>
public partial class Courses : Window
{
public Courses()
{
InitializeComponent();
}
}
class ViewModel : ObservableObject
{
public ObservableCollection<Model> models = new ObservableCollection<Model>();
//private Model model = new Model();
#region Fields
bool skipUpdating = false;
#endregion
#region Properties
public string Title
{
get
{
return model.title;
}
set
{
model.title = value;
NotifyPropertyChanged("Title");
}
}
#endregion
}
Model / Model_Weeks
The Model and sub-models are shown below.
class Model
{
public string title;
public ObservableCollection<Model_Weeks> weeks = new ObservableCollection<Model_Weeks>();
}
class Model_Weeks
{
[DataMember(Name = "title")]
public string title { get; set; }
[DataMember(Name = "topic")]
public int topic { get; set; }
}
}
After a lot of playing I've come up with the following solution. I'm not sure if this is the correct way to do it, but it seems to work.
I have two ViewModels and also two Models.
Courses_ViewModel
class Courses_ViewModel : ObservableObject
{
public Courses_Model courses_Model;
public ObservableCollection<Course_ViewModel> course_ViewModels { get; set; }
public Courses_ViewModel()
{
//Deserialize and assign Parent Model to Local Variable
courses_Model = new Courses_Model();
//Initialise ChildModel List
course_ViewModels = new ObservableCollection<Course_ViewModel>();
//Create ChildViewModels and Add to List
foreach (Course_Model course in courses_Model.course_Models)
{
course_ViewModels.Add(new Course_ViewModel(course));
}
}
}
Course_ViewModel
class Course_ViewModel : ObservableObject
{
public Course_Model course_Model { get; set; }
public Course_ViewModel(Course_Model model)
{
course_Model = model;
}
public string Title
{
get
{
return course_Model.title;
}
set
{
course_Model.title = value;
NotifyPropertyChanged("Title");
}
}
}
Models
class Courses_Model
{
//List of courses
public List<Course_Model> course_Models { get; set; }
public Courses_Model()
{
//Initialise course_Models List
course_Models = new List<Course_Model>();
//Create courses and add to list
course_Models.Add(new Course_Model { title = "L1" });
course_Models.Add(new Course_Model { title = "L2" });
course_Models.Add(new Course_Model { title = "L3" });
course_Models.Add(new Course_Model { title = "L4" });
course_Models.Add(new Course_Model { title = "L5" });
}
}
class Course_Model
{
public string title;
}

How to Populate Combobox Based on User Input Using MVP?

How To Populate a ComboBox Based on User Input Using C# and WinForms?
I have a combobox which I need to populate based on user input (2 different options), while also using the MVP pattern (Model View Presenter). This is in a C# WinForms project.
For example purposes, let's say I have 2 different categories: Fruits and Shoes. If the user chooses Fruits, then my combobox should populate with fruits, if my user chooses Shoes, then my combobox should populate with shoes.
My category data (Fruits and Shoes) are both stored in separate tables in my database.
Currently I have a Model, View, and Presenter for both categories, which works great, but seems very repetitive (especially when dealing with more than 2 categories). Is there an additional piece/design pattern I can implement to cut down on the repetitiveness?
I have provided some example code below, in the following order:
1. Model, View, and Presenter for Fruits category
2. Model, View, and Presenter for Shoes category
3. WinForms Code Behind Page
////////FRUITS MVP////////
//Model
public class FruitsComboBoxModel
{
public List<Fruits> Fruits { get; set; }
}
//View
public interface IFruitsComboBoxModel
{
void ShowFruitsComboBox(FruitsComboBoxModel fruitsComboBoxModel);
}
//Presenter
public class FruitsComboBoxPresenter
{
IFruitsComboBoxView fruitsComboBoxView;
public FruitsComboBoxPresenter(IFruitsComboBoxView view)
{
fruitsComboBoxView = view;
}
public void Init()
{
var model = GetModel();
fruitsComboBoxView.ShowFruitsComboBox(model);
}
private FruitsComboBoxModel GetModel()
{
var dbFruits = GetFruitsFromDataBase(); //fake call to DB for fruits
var fruitsComboBoxModel = new FruitsComboBoxModel
{
Fruits = dbFruits;
}
return fruitsComboBoxModel;
}
}
////////SHOES MVP////////
//Model
public class ShoesComboBoxModel
{
public List<Shoes> Shoes { get; set; }
}
//View
public interface IShoesComboBoxModel
{
void ShowShoesComboBox(ShoesComboBoxModel shoesComboBoxModel);
}
//Presenter
public class ShoesComboBoxPresenter
{
IShoesComboBoxView shoesComboBoxView;
public ShoesComboBoxPresenter(IShoesComboBoxView view)
{
shoesComboBoxView = view;
}
public void Init()
{
var model = GetModel();
shoesComboBoxView.ShowShoesComboBox(model);
}
private ShoesComboBoxModel GetModel()
{
var dbShoes = GetShoesFromDataBase(); //fake call to DB for shoes
var shoesComboBoxModel = new ShoesComboBoxModel
{
Shoes = dbShoes;
}
return shoesComboBoxModel;
}
}
////////Code Behind Page////////
public partial class ExampleForm : Form, IFruitsComboBoxView, IShoesComboBoxView
{
public CategoryType categoryType { get; set; }
FruitsComboBoxPresenter fruitsComboBoxPresenter;
ShoesComboBoxPresenter shoesComboBoxPresenter;
public ExampleForm(CategoryType type)
{
categoryType = type; //user category selection
}
private void ExampleForm_Load(object sender, EventArgs e)
{
if (categoryType == CategoryType.Fruits)
{
fruitsComboBoxPresenter = new FruitsComboBoxPresenter(this);
fruitsComboBoxPresenter.Init();
}
else if (categoryType == CategoryType.Shoes)
{
shoesComboBoxPresenter = new ShoesComboBoxPresenter(this);
shoesComboBoxPresenter.Init();
}
else
{
throw new Exception("Invalid type detected");
}
}
public void ShowFruitsComboBox(FruitsComboBoxModel fruitsComboBoxModel)
{
comboBox.DataSource = fruitsComboBoxModel.Fruits.Select(x => x.Name).ToList();
}
public void ShowShoesComboBox(ShoesComboBoxModel shoesComboBoxModel)
{
comboBox.DataSource = shoesComboBoxModel.Shoes.Select(x => x.Name).ToList();
}
}
I have debated using only one Model, View, and Presenter for the combobox and on my model having a fruits property (list) and having a shoes property (shoes). But this means I will have a lot of if/else logic inside my model, and have to pass down the user selection.
I think you can make use of some interfaces and abstract classes to reduce some of your duplicate code.
This will require some tweaking, but if you have some compile-able code I can take another look.
public interface IComboItem {
string Name {get; set;}
object Value {get; set;}
}
public class Fruit : IComboItem {
//fruit stuff
}
public class Shoe : IComboItem {
//shoe stuff
}
//View
public interface IComboBoxModel
{
void ShowComboBox(List<IComboItem> comboItems);
}
//Presenter
public abstract class ComboBoxPresenter {
IComboBoxView comboBoxView;
public ComboBoxPresenter(IComboBoxView view){
comboBoxView = view;
}
public void Init(){
var model = GetModel();
comboBoxView.ShowComboBox(model);
}
//force implementors to get the model
private abstract List<IComboItem> GetModel();
}
public class FruitsComboBoxPresenter : ComboBoxPresenter
{
private override List<Fruit> GetModel()
{
return GetFruitsFromDataBase(); //fake call to DB for fruits
}
}
public class ShoeComboBoxPresenter : ComboBoxPresenter
{
private override List<Shoe> GetModel()
{
return GetShoesFromDataBase(); //fake call to DB for fruits
}
}
////////Code Behind Page////////
public partial class ExampleForm : Form, IComboBoxView
{
public CategoryType categoryType { get; set; }
IComboBoxPresenter comboBoxPresenter;
public ExampleForm(CategoryType type)
{
categoryType = type; //user category selection
}
private void ExampleForm_Load(object sender, EventArgs e)
{
if (categoryType == CategoryType.Fruits)
{
comboBoxPresenter = new FruitsComboBoxPresenter(this);
}
else if (categoryType == CategoryType.Shoes)
{
comboBoxPresenter = new ShoesComboBoxPresenter(this);
}
else
{
throw new Exception("Invalid type detected");
}
comboBoxPresenter.Init();
}
public void ShowComboBox(List<IComboItem> comboItems)
{
comboBox.DataSource = comboItems.Select(x => x.Name).ToList();
}
}
I will suggest one view for one presenter. If you need use the multi-model write the code into presenter.
Create the Contract class then define the View and Presenter Interface
public class ExampleFormContract
{
public interface IView
{
// Display the item to the view
void DisplayComboBoxItems(IEnumerable<string> items);
// set the presneter
IPresenter Presenter { set; }
}
public interface IPresenter
{
// init form load
void Init();
}
}
Create ExampleFormPresenter and implement the ExampleFormContract.IPresenter
public class ExampleFormPresenter : ExampleFormContract.IPresenter
{
private ExampleFormContract.IView View { get; set; }
private CategoryType Type { get; set; }
public ExampleFormPresenter(ExampleFormContract.IView view, CategoryType type)
{
// set the view and use the Dependency Injection (IoC)
View = view;
View.Presenter = this;
Type = type;
}
public void Init()
{
// you can write your logic code to here.
if (Type == CategoryType.Fruits)
{
var dbFruits = GetFruitsFromDataBase(); //fake call to DB for fruits
var fruitsLists = dbFruits.Select(x => x.Name).ToList();
// update the item to the view
View.DisplayComboBoxItems(fruitsLists);
}
else if (Type == CategoryType.Shoes)
{
var dbShoes = GetShoesFromDataBase(); //fake call to DB for shoes
var shoesLists = dbShoes.Select(x => x.Name).ToList();
// update the item to the view
View.DisplayComboBoxItems(shoesLists);
}
else
{
throw new Exception("Invalid type detected");
}
}
}
Modify the ExampleForm.cs implement ExampleFormContract.IView
public partial class ExampleForm : Form, ExampleFormContract.IView
{
// set the presenter
public ExampleFormContract.IPresenter Presenter { private get; set; }
public ExampleForm()
{
InitializeComponent();
}
private void ExampleForm_Load(object sender, EventArgs e)
{
Presenter.Init();
}
public void DisplayComboBoxItems(IEnumerable<string> items)
{
// update the view
comboBox.DataSource = items;
}
}
Demo
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// create the form
var form = new ExampleForm();
// use the IoC and Inject the CategoryType what you want
var presneter = new ExampleFormPresenter(form, CategoryType.Fruits);
Application.Run(form);
}
This MVP architecture work fine for me into C# winform. I think it will help you.

In Xamarin.Forms, how to notify the changes of the same viewmodel back to the previous page? (can pass to the second page, but not back)

I got two pages, "HomePage", "SettingPage", including the same "MyView" (some Pickers there).
When I click "Go Setting"(or show more settings) Button from Homepage, the values syncs to the setting page. But When I click "Apply" on the setting page, the values did not come back.
I am new in c# and Xamarin and tried to search online and Microsoft docs. But I couldn't find a way to fix this issue.
Also I was following this link: How to set BindingContext of multiple pages to the same ViewModel in Xamarin.Forms?
and did the same global value in my code.
MyView (ContentView)
public MyView()
{
InitializeComponent();
BindingContext = GlobalVar.MyViewModel;
Setting1.SetBinding(Picker.ItemsSourceProperty, "ObList1");
Setting1.ItemDisplayBinding = new Binding("obj_text");
Setting1.SetBinding(Picker.SelectedItemProperty, "SelectedItem1");
//also other pickers
}
HomePage (including the MyView)
public SearchPage ()
{
InitializeComponent ();
BindingContext = GlobalVar.MyViewModel;
}
private async void Click_GoSetting(object sender, EventArgs e)
{
await Navigation.PushAsync(new SettingPage());
}
SettingPage (including the same MyView)
public partial class SettingPage : ContentPage
{
MyViewModel viewModel { get; set; } = GlobalVar.MyViewModel;
public SettingPage ()
{
BindingContext = viewModel;
}
private async void Click_ApplySetting(object sender, EventArgs e)
{
await Navigation.PopAsync(true);
}
//some other method deal with viewModel
}
GLobalVar.cs
private static MyViewModel _myViewModel = new MyrViewModel();
public static MyViewModel MyViewModel
{
get
{
return _myViewModel;
}
}
ViewModel
public class MyViewModel : BaseViewModel
{
public ObservableCollection<obj> ObList1 { get; set; }
public ObservableCollection<obj> ObList2 { get; set; }
public ObservableCollection<obj> ObList3 { get; set; }
public obj SelectedItem1 { get; set; }
public obj SelectedItem2 { get; set; }
public obj SelectedItem3 { get; set; }
public MyViewModel()
{
ObList1 = new ObservableCollection<obj>();
ObList2 = new ObservableCollection<obj>();
ObList3 = new ObservableCollection<obj>();
}
}
Maybe I should notify the changes on my SettingPage to viewmodel? or do something in the "set" in viewmodel?
The confusing point is that two pages embed the same view using the same viewmodel, but notify the change from Page1 to Page2 only, not Page2 to Page1.
Any ideas, thx in advance.
Solution One:
Using Event can pass value back to Previous Page.
Define Event in SecondPage :
public delegate void EventHandler(string status);
public event EventHandler EventPass;
Invoke Event when Page disappear:
protected override void OnDisappearing()
{
base.OnDisappearing();
EventPass("Back Code");
}
In FirstPage, when Naviagtion place need to add the Event here:
string title = "PageSecondParamater";
PageSecond pageSecond = new PageSecond(title);
pageSecond.EventPass += PageSecond_EventPass; ;
Navigation.PushAsync(pageSecond);
Now value will be passed here:
private void PageSecond_EventPass(string status)
{
Title = status;
Console.WriteLine("---" + status);
}
Solution Two:
Using Properties Dictionary to store easy and small size data in Application, when enter in page will invoke it to get data from which has been stored.
In Second Page Where you want to store data, writing as bellow:
Application.Current.Properties ["value"] = valuedata;
When back to First Page, override OnAppearing method to update UI:
protected override void OnAppearing()
{
base.OnAppearing();
if (Application.Current.Properties.ContainsKey("value"))
{
var ValueGet = Application.Current.Properties ["value"] as DataType;
// do something with other things
}
}
Note: ViewModel if want to dynamic update data , need to use INotifyPropertyChanged .
Sample Implementation:
public class ObservableProperty : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
ViewModelBase suggest implementing ICommand as a Dictionary structure like:
public abstract class ViewModelBase : ObservableProperty
{
public Dictionary<string,ICommand> Commands { get; protected set; }
public ViewModelBase()
{
Commands = new Dictionary<string,ICommand>();
}
}
So all todo in your ViewModel is just inherit the ViewModelBase class and use it:
class LoginViewModel : ViewModelBase
{
string userName;
string password;
public string UserName
{
get {return userName;}
set
{
userName = value;
OnPropertyChanged("UserName");
}
}
public string Password
{
get{return password;}
set
{
password = value;
OnPropertyChanged("Password");
}
}
#endregion
#region ctor
public LoginViewModel()
{
//Add Commands
Commands.Add("Login", new Command(CmdLogin));
}
#endregion
#region UI methods
private void CmdLogin()
{
// do your login jobs here
}
#endregion
}
Solved.
MyViewModel (updated)
public class MyViewModel : BaseViewModel
{
public ObservableCollection<obj> ObList1 { get; set; }
public ObservableCollection<obj> ObList2 { get; set; }
public ObservableCollection<obj> ObList3 { get; set; }
private obj _selectedItem1 = new obj();
public obj SelectedItem1
{
get { return _selectedItem1; }
//this is the line solved the problem
//but still not understood thoroughly
set { SetProperty(ref _selectedItem1, value); }
}
//same for _selectedItem2 _selectedItem3
}
ps: BaseViewModel codes here (not changed, from template codes)
public class BaseViewModel : INotifyPropertyChanged
{
//some other attributes
//...
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName]string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}
It seems that by calling SetProperty, OnPropertyChanged will also be revoked.
But still a little bit confusing about why the previous codes go like kind of "one-way" binding.

Cross-Thread Call using MVP WIndows Forms

I would like to use MVP Design pattern for a WinForm App but i'm facing the problem of calling a View Update from another thread.
Here's my code
MODEL
public class Model : IModel
{
public string Status { get; set; }
public async void LongOperation(IHomeView View)
{
for (int i = 0; i < 1000; i++)
{
View.StatusListView = i.ToString();
}
}
}
PRESENTER
public class HomePresenter
{
IHomeView _IView;
IModel _IModel;
Model _Model = new Model();
public HomePresenter(IHomeView IView)
{
_IView = IView;
}
public async void LaunchLongOperation()
{
await Task.Run(() => _Model.LongOperation(_IView));
}
}
INTERFACE VIEW-PRESENTER
public interface IHomeView
{
string StatusListView { get; set; }
}
INTERFACE PRESENTER-MODEL
public interface IModel
{
string Status { get; set; }
}
FORM:
public partial class frmMain : Form, IHomeView
{
HomePresenter _Presenter;
public frmMain()
{
InitializeComponent();
_Presenter = new HomePresenter(this);
}
public string StatusListView
{
get
{
return lstActivityLog.Text;
}
set
{
lstActivityLog.Items.Add(value);
}
}
private void btnAvvia_Click(object sender, EventArgs e)
{
_Presenter.launchLongOperation();
}
}
i would like to update a list view in the Main form during the long operations of the Model class.
Which is the best way to do that?
Try this code without debugging, you'll be surprised about it works!
The quick and dirty way to make it work in debugging mode as well is to add Control.CheckForIllegalCrossThreadCalls = false; into the constructor of your form.
public partial class MainForm : Form, IHomeView
{
HomePresenter _Presenter;
public MainForm()
{
InitializeComponent();
Control.CheckForIllegalCrossThreadCalls = false; //<-- add this
_Presenter = new HomePresenter(this);
}
public string StatusListView
{
get
{
return lstActivityLog.Text;
}
set
{
lstActivityLog.Items.Add(value);
}
}
private void button1_Click(object sender, EventArgs e)
{
_Presenter.LaunchLongOperation();
}
}

Categories