Update a combobox from a presenter (MVP) - c#

I am using MVP in my project, but i am new in MVP.
I have two comboboxes. When I select an option in a combobox, the another combobox should be filled with new data.
This action will be in Presenter. I get my view 'view1' in Presenter, and introduced Combobox1 and Combobox2 as properties in 'view1', because I need 'DataSource', 'DisplayMember', 'ValueMember' and 'Refresh()' in the method below.
But, when using a pattern, it is enough to send a property like
public string Combobox2
{
get { return comboBox1.SelectedValue.ToSstring(); }
}
into Presenter not the whole Combobox. How can I solve this problem?
public void OnSelectedIndexChangedCombobox1()
{
if (view1.Combobox1.SelectedIndex == -1)
{
return;
}
DataTable dt = Tools.GetDataTable("A Path");
var query =
(from o in dt.AsEnumerable()
where o.Field<string>("afield") ==
farmerView.Combobox1.SelectedValue.ToString()
orderby o.Field<string>("anotherfield")
select new KeyValuePair<string, string>(o.Field<string>("field1"),
o.Field<string>("field2"))).ToList();
farmerView.Combobox2SelectedIndexChanged -= OnSelectedIndexChangedCombobox2;
farmerView.Combobox2.DataSource = new BindingSource(query, null);
farmerView.Combobox2.DisplayMember = "Value";
farmerView.Combobox2.ValueMember = "Key";
farmerView.Combobox2.Refresh();
farmerView.Combobox2SelectedIndexChanged +=
OnSelectedIndexChangedCombobox2;
farmerView.Combobox2.SelectedIndex = -1;
}
Thank you

You should not pass any Android objects to presenter, just get the event in view (for instance your Activity) then call a method from presenter that provides data for second ComboBox (we call it Spinner in Android!) by passing selected item from first one, and then presenter will call a method of View which fill the second one and View knows how to do it.
You can take a look at this sample project http://github.com/mmirhoseini/marvel and this article https://hackernoon.com/yet-another-mvp-article-part-1-lets-get-to-know-the-project-d3fd553b3e21 to get more familiar with MVP.

Related

Binding data MVVM after Tap on ListBox

This is my situation:
- I have a WebApi that sends me a json data.
- My app reads this data and binds all information in a list box
- When I tap on Item of the list box I want to show all information about that item
The problem is: How can I bind data on the new view?
This is the code after tap (MainViewModel):
this.ProfessorDetail = new RelayCommand(() =>
{
if (SelectedIndexProfessors != -1)
{
//This variable contain all detail information
Professor x = Professors.ElementAt(SelectedIndexProfessors);
//Open new page
App.RootFrame.Navigate(new Uri("/Pages/ProfessorDetailPage.xaml", UriKind.RelativeOrAbsolute));
}
});
You can use Uri parameter to pass simple string information between pages. For example, in your RelayCommand pass unique information about selected professor :
.........
//pass selected professor Id to ProfessorDetailPage
App.RootFrame.Navigate(new Uri("/Pages/ProfessorDetailPage.xaml?professorId=" + x.ProfessorId, UriKind.RelativeOrAbsolute));
.........
Then in the ProfessorDetailPage's Loaded or NavigatedTo event handler get the uri parameter and display information accordingly :
.........
string professorId;
if(NavigationContext.QueryString.TryGetValue("professorId", out professorId))
{
//load information based on professorId parameter value
}
.........
I never made a page based application, but I was looking at this link and from basic knowledge of WPF, I do know you need to set the DataContext of that page just like you asked.
Take a look at the following code in the link:
this.Frame.Navigate(typeof(BasicPage2), tb1.Text);
That is sending the text entered in tb1 to the object you're navigating to. Then look at how the receiving object is utilizing that information:
private void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
string name = e.NavigationParameter as string;
....
}
You're going to want to follow the same idea, except you're going to want to do something like:
App.RootFrame.Navigate(new Uri(".....", ...), x);
And then in the page itself, you're going to want to set up a LoadState event in ProfessorDetailPage control and inside it do:
Professor prof = x as Professor;
if( prof != null)
{
this.DataContext = prof;
}
That should set the DataContext of ProfessorDetailPage and your data should be populated.
Let me know how it works out, hope this helps!

MVVM - UI related code in View model - true separation of concerns

I started implementing a MVVM design pattern in an existing WPF c# application. I am completely new and have never used design patterns or dependency injection before. I was looking at the frameworks already available and have adopted MVVM light. I moved the logic from the view to the viewmodel. I have lot of code in the PopulateTestMenu which is related to UI in the view model. It also has calls to the event handlers. How do I take care of this?
In the XAML I have:
<Window DataContext="{Binding Main, Source={StaticResource Locator}}">
<Menu>
<MenuItem Header="Load All History..." Command="{Binding LoadAllHistory}">
In the MainViewModel class I have:
public ICommand LoadAllHistory { get; private set; }
public MainViewModel()
{
LoadAllHistory = new RelayCommand(() => LoadHistoryExecute(), () => true);
}
The code that I moved from my view to the viewmodel:
private void LoadHistoryExecute()
{
try
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "Test History File (*.xml)|*.xml";
ofd.Title = "Open Test History";
ofd.Multiselect = true;
if (ofd.ShowDialog() == true)
{
ThreadPool.QueueUserWorkItem(LoadTestHistoryCallback, ofd.FileNames);
}
}
catch
{
//some code
}
}
private void LoadTestHistoryCallback(object state)
{
try
{
string[] fileNames = (string[])state;
foreach (string fileName in fileNames)
{
bool success = MyApp.Instance.ParseTestHistory(fileName);
string status = success
? String.Format("'{0}' loaded successfully.",
System.IO.Path.GetFileName(fileName))
: String.Format("Failed to load history from '{0}'.",
System.IO.Path.GetFileName(fileName));
Dispatcher.CurrentDispatcher.DynamicInvoke(delegate()
{
Status = status;
});
PopulateTestMenu(new SortedList<int, int>());
}
}
catch
{
//some code
}
}
private void PopulateTestMenu(SortedList<int, int> indexes)
{
try
{
_testMenuMutex.WaitOne();
//Populate the Tests menu with the list of tests.
Dispatcher.CurrentDispatcher.DynamicInvoke(delegate()
{
menuTests.Items.Clear();
var checkEventHandler = new RoutedEventHandler(testMenuItem_Checked);
bool added = false;
if (MyApp.Instance.TestHistory != null &&
MyApp.Instance.TestHistory.Count > 0)
{
List<ushort> subIds = new
List<ushort>MyApp.Instance.TestHistory.Keys);
foreach (ushort subId in subIds)
{
MenuItem menuItem = null;
menuItem = new MenuItem();
menuItem.Header = subId.ToString().PadLeft(5, '0');**
MenuItem none = new MenuItem();
none.Header = "None";
none.IsCheckable = true;
none.IsChecked = true;
none.Checked += checkEventHandler;
none.Unchecked += checkEventHandler;
menuItem.Items.Add(none);
if (MyApp.Instance.TestHistory != null &&
MyApp.Instance.TestHistory.ContainsKey(subId))
{
var tests = MyApp.Instance.TestHistory[subId];
if (tests != null)
{
foreach (Test t in tests)
{
MenuItem item = new MenuItem();
item.IsCheckable = true;
string description = t.Description.Replace("\n",
"\n".PadRight(34, ' '));
string header = abc;
item.Header = header;
item.DataContext = t;
item.Checked += checkEventHandler;
item.Unchecked += checkEventHandler;
menuItem.Items.Add(item);
}
if (tests.Count > 0)
{
menuTests.Items.Add(menuItem);
added = true;
}
}
}
// Carry over the previous selection.
if (indexes.ContainsKey(subId) && indexes[subId] > -1)
{ ((MenuItem)menuItem.Items[indexes[subId]]).IsChecked =
true;
}
}
}
I am still trying to figure out what you are asking =)...
But you are mixing up some things... Remember one of the core concepts of MVVM is to make the viewmodel testable and remove all view related code off from the viewmodel. So no dependencies to WPF at all. So MenuItem looks like a WPF MenuItem and should not be in your ViewModel.
Instead you could consider to make a MenuItemViewModel which binds to the MenuItem in the View. And it I could see an ObservableCollection<MenuItemViewModel> TestMenu instead of your sorted list.
In your method LoadTestHistoryCallback you would instanciate (could be done via DI) the MenuItemViewModel and add it to the TestMenu Collection. The MenuItemViewModel could have status property which could be assigned from outside or internaly. (It can also have some additional logic, hey its a viewmodel).
In the View you could then bind it to a list with a template representing the MenuItem via DataBinding.
<Menu DockPanel.Dock="Top" ItemsSource="{Binding Path=MenuItems}" />
So remember ViewModel can also contain ViewModels or collections of viewmodel.
Use the rich databinding api from WPF.
Work with bindable Properties like ObservebaleCollections or Properties that are extended with PropertyChanged notification.
HTH
P.S: You can then have a click ICommand in the MenuItemViewModel and execute actions or better use the EventAggregator or Messenger to notify other ViewModels ...(but that's a story for another question =)... )
You have applied the theory of MVVM correctly by moving that code to the ViewModel however just keep in mind that the View should only provide the "structure" of the display.
What is displayed is provided by the model in the ViewModel.
With that in mind separate out the menu parts from the ViewModel method and put them in the View, but leave the Test object creation parts (Binding ViewModel objects to View structure is what it's about).
Within your PopulateTestMenu method the menus and menu structure need to be specified in the View while the data populating them needs to be created and formatted in the ViewModel.
In the View you will bind the appropriate object parts to the menu structure, and the ViewModel will automatically fill it in with the model objects when the model is bound to the view.
Looking at the code, it appears that your Test object is your ViewModel, and the Menu and MenuItem structure needs to be created in the View, then you specify the binding of the specific properties of the Test object to the specific structure parts of the Menu within the View.

EF Update is not Updating the GridView

I'm still in the learning Phase of WPF, EF and MVVM and now I got the following problem. I can delete and insert new items in my DataGridView but I don't know how to update my items.
All I do is select an emptyrow which already has a primary key and then I put the data into it. It's working (updating database) but the GridView is not refreshing. I Need to restart the program first to see my updated data.
My Execute Command to Update my Database. I'm in the ViewModel class
public void ExecuteUpdate(object obj)
{
try
{
SelectedIndex.Child_Update(new Farbe { FarbauswahlNr = SelectedIndex.FarbauswahlNr, Kurztext = SelectedIndex.Kurztext, Ressource = SelectedIndex.Ressource, Vari1 = SelectedIndex.Vari1, Vari2 = SelectedIndex.Vari2 });
//ListeAktualisieren --> Refreshing the List
ListeAktualisieren();
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
Here is my Refresh Method which SHOULD Refresh the GridView. I'm in the ViewModel class
public void ListeAktualisieren()
{
farbliste.ListeAktualisieren(db);
farbliste.Model = farbliste.Model.Concat(farbliste.Addlist).ToList();
Model = farbliste.Model;
farbliste.Addlist.Clear();
}
The method is calling my Business List which also got a Refresh Method. Reading from my database here. I'm in the Business List class
public void ListeAktualisieren(TestDBEntities db)
{
Model.Clear();
foreach (var item in db.Farben)
{
//Insert and delete working
add = new Farbe { FarbauswahlNr = item.FarbauswahlNr, Kurztext = item.Kurztext, Ressource = item.Ressource, Vari1 = Convert.ToBoolean(item.Var1), Vari2 = item.Vari2 };
Addlist.Add(add);
}
}
Model is the Source of my GridView which is not Refreshing changed data when Updated but is showing new data rows when inserting or deleting.
You need Observablecollections and Classes with implemented INotifyPropertyChanged. Add the new element to the Observablecollection by insert and raise the event propertychanged by a change.
The rest should be done by WPF.
Edit: The Sourcecollection for the DataGrid needs to be the Observablecollection.
Edit2: To be nice I put the result of the comments here ;-)
Each row of the DataGrid is an element of the collection. Each cell of one row listens to a PropertyChangedEvent of its element (the String is Casesensitive so be carefull). If the getter of the property isn't called after the propertychangedevent the binding didn't receive the event.
This piece of Code can help asure that you don't call with nonexistent strings:
private void VerifyPropertyName(string PropertyName)
{
if (string.IsNullOrEmpty(PropertyName))
return;
if (TypeDescriptor.GetProperties(this)(PropertyName) == null) {
string msg = "Ungültiger PropertyName: " + PropertyName;
if (this.ThrowOnInvalidPropertyName) {
throw new isgException(msg);
} else {
Debug.Fail(msg);
}
}
}
Try adding this to your binding section
ItemsSource="{Binding Path=Model, UpdateSourceTrigger= PropertyChanged"}

Why doesn't my combobox display text?

I'am working in a GTK-sharp application. I have this code but combobox1 doesn't display any item. Why not?
ListStore store = new ListStore(typeof(myclass));
store.AppendValue(new myclass("hola",7));
store.AppendValue(new myclass("hola2",8));
store.AppendValue(new myclass("hola3",2));
combobox1.Model = store;
The class myclass overrides ToString()
What you are looking for is custom Gtk.CellRenderer:
private void MyClassRenderer(CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter)
{
MyClass myclass = model.GetValue(iter, 0) as MyClass;
(cell as CellRendererText).Text = myclass.ToString();
}
With some additional code in the setup method like this:
CellRendererText myClassCell = new CellRendererText();
combobox1.PackStart(myClassCell, true);
combobox1.SetCellDataFunc(myClassCell, MyClassRenderer);
ListStore store = new ListStore(typeof(MyClass));
store.AppendValues(new MyClass("hola",7));
store.AppendValues(new MyClass("hola2",8));
store.AppendValues(new MyClass("hola3",2));
combobox1.Model = store;
Make sure the SetCellDataFunc method is called after PackStart method.
Job done! :)
I'm not really sure, but make sure the listbox key and values are mapped to the fields in the class. I think it needs to be specific. After setting the value, make sure to do the final databind like: control.DataBind();
In general, C# binding goes like: 1) automatic colum generation/manually map all fields to keys and values 2). set the field 3. and call the bind() function.

Adding User input to List then populating Gridview

I have an 3 tier application (DAL, BBL, UI)
BBL at the moment do Nothing just a pass-thru
I have a grid view and for simplicity's sake one text box(TB) and one drop down list(DDL).
and Two submit buttons.
(I changed my Custom Class to Object. just for this example)
First Submit button adds the TB.text & DDL.SelectedValue to a Object X in the UI.
the BBL takes that object X to adds it to a List(X) in the BBL.
Then the BBL should populate the Gridview with the List(X). (with ajax partial page load)
the second Submit should send the full List(X) to the database.
The problem im having is when I click the first Submit(the local) I dont get new Rows just keep over writing the same row. what am I Missing?
in the UI class
private businesslogic blogic = new businesslogic();
protected void B1_local_Click(object sender, EventArgs e)
{
object x = new object();
x.id = Convert.ToInt32(TB_1.Text);
x.var1 = Convert.ToInt32(DDL_1.SelectedValue);
blogic.addrowtolist(x);
Gridview1.DataSource = blogic.grablist();
Gridview1.Databind();
}
in the BBL class
public List<object> locallist = new List<object>();
public void addrowtolist(object x)
{
locallist.Add(x);
}
public List<object> grablist()
{
return locallist;
}
With every postback, you're loading a new BL with a new (empty) List. To see your List grow, you're going to need to save it somewhere that persists (doesn't disappear) between one request and the next.
I would recommend putting your List in a Session key
Session["items"] = blogic.locallist;
and then pulling it out and sending it to a second BL constructor on postback. This is probably simplest, but not always the correct approach.
http://msdn.microsoft.com/en-us/library/z1hkazw7.aspx

Categories