I'm, having a Listview, with bunch of "Modules" in one ObservableCollection, which has been set as the ItemsSource. This collection has been set as:
public static ObservableCollection<Module> Modules { get; set; } in App.xaml.cs to make it global.
When I click the checkbox in the list, it will launch this function:
private void ModuleCheckbox_CheckedChanged(object sender, CheckedChangedEventArgs e)
{
var checkbox = (CheckBox)sender;
var item = (Module)checkbox.BindingContext;
item.ModuleIsChecked = checkbox.IsChecked;
foreach(Module m in App.Modules)
{
Console.WriteLine(m.Name + " Value: " + m.ModuleIsChecked);
}
}
As you can see, I'm trying to set the property: ModuleIsChecked to be the same, as the checkbox, which in this case should be true or false. Now, when I click the checkbox, and the foreach-loop goes trough, the values are correct. When I go to the new "page" by using the
Navigation.PushAsync(new NewSite()); function and run the loop again, none of the Modules have ModuleIsChecked value as set before. How is this possible?
Module has the following attributes:
public string Description { get; set; }
public bool IsVisible { get; set; }
public bool ModuleIsChecked { get; set; }
public ObservableCollection<Question> QuestionList { get; set; }
edit as requested, here's the code at the new page, where I run through the same list just to see, if my selections have been "stuck" into that ObservableCollection:
public partial class NewSite : ContentPage
{
public Questions()
{
InitializeComponent();
BindingContext = new App();
foreach(Module m in App.Modules)
{
Console.WriteLine("Module: {0} ModuleisChecked: {1}", m.Name, m.ModuleIsChecked);
}
And here, the Console shows, that all of the values are False. Any ideas?
Thank you to Jason in the comments! (I don't know how to link you here, sorry!)
The problem lies in here:
BindingContext = new App();
As Jason said, that creates new instance of the App instead of using the existing one, which I just edited. Commenting this out and I was able to get this working as intended. Thank you!
Can be marked as solved, thanks!
Related
I have a problem. I created a Database with a table based on the following class:
public class Device
{
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
}
In my App.xaml.cs I do the following:
static List<Device> knownDeviceList;
public App()
{
InitializeComponent();
MyHandler();
MainPage = new NavigationPage(new Page1());
}
public Task MyHandler()
{
return GetKnownDevices();
}
private async Task Get()
{
KnownDeviceList = deviceDatabaseController.GetDevice();
}
public static List<KnownDevice> KnownDeviceList
{
get
{
if (knownDeviceList == null)
{
knownDeviceList = new List<KnownDevice>();
}
return knownDeviceList;
}
set
{
knownDeviceList = value;
}
}
After that code automatically runs, I have a filled KnownDeviceList.
Now in Page1 I made a ListView with a ViewModel that shows the devices from KnownDeviceList using bindings. On that same page I made a button to add a new device, so it takes you to Page2 using a NavigationPage. But when I write the new device to the database, I use: Navigation.PopToRootAsync(); to go back, but now I want the ListView to refresh and KnownDeviceList to get the devices from the database again, so my new device is in the ListView.
How can I achieve this, because I have no idea what to do after I added it!?
I problem here is that you are changing the collection. But not notifying the UI about it.
List<> will not notify the collection change on its own. Where as ObservableCollection does notify collection changes, like Add, Remove, etc. Hence the name ObservableCollection.
Using ObservableCollection<KnownDevice> instead of List<KnownDevice> could solve this issue.
I am litle stuck here.
So this is my class that i initialize my ObservableCollection:
public class ServicosMenu : BindableObject
{
private Rota _oldSer;
public AppService lo = new AppService();
public ObservableCollection<Rota> ListSer { get; set; }
public ObservableCollection<Rota> OneSer { get; set; }//foi introduido pra testar 1 so servico
public List<Rota> y = new List<Rota>();
public RssRotas rts = new RssRotas();
public CalendarPopUp cal = new CalendarPopUp();
public ServicosMenu()
{
rts.data = cal.datacal;
GetListSerAsync();
ListSer = new ObservableCollection<Rota>();
OneSer = new ObservableCollection<Rota>();
}
public void settt(string value)
{
rts.data = value;
}.....
And in the other class this is the code:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Servicos : ContentPage
{
public Servicos (string data)
{
InitializeComponent();
}
public void ListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
var vm = BindingContext as ServicosMenu;
var ser = e.Item as Rota;
vm.HideOrShowService(ser);
vm.OneSer.Add(ser); //this is where it gives me the error
}
private async Task Button_Clicked_DetalhesAsync(object sender, ItemTappedEventArgs e)
{
await Navigation.PushAsync(new Detalhes());
}
So my problem is that the ObservableCollection is doing a "reset" every time I call the view Details, because it has BindingContext to the class ServicoMenu. So every time I call the view, it calls the constructor and it initialize again the collection and I lost the value that I was trying to add.
How can I fix this so it do not "reset" the value?
Thanks for any help!!
EDIT1:
As you can see my view class Detalhes already got the value of the item selected, the think is to bind with the xaml listview.
This is my xaml:
And in the app nothing...
EDIT2:
and when i touch the button to go to the details page on the app, it do not work and i get this strange think in the output:
Damm, it was hard to understand this, what i did was just 2 things diferent from the EDIT2 up in the question.
1º - I just switch this line in c# class of Detalhes : BindingContext = Rote; (and put it after the "InitializeComponent();" )
2º - in the XAML page of Detalhes, in the part that i have the " ItemsSource="{Binding Name}" it actually work just with the dot, like this: ItemsSource="{Binding .}"
#Bijington, #Ian, #David thanks for the help trying to work this in my head, it maybe nothing of the other world for most experience programmers but i´m just now enter the job market and getting to understand the all dynamicof the xamarin.
I have a ListPicker and when this is opened, by default the first item is selected.
In my app, user select an item on ListPicker, this item is sent to my database. The next time that user opens the picker, automatically the selected item is the same (previous). So, I need no selected item.
The ListPicker is populated from a collection. I tried according to other answers (list.SelectedIndex = -1;), but doesn't work.
My code:
public ObservableCollection<observacao> obsObservacao { get; set; }
public class observacao
{
public string descricao { get; set; }
public double valor { get; set; }
public string valoradicional { get; set; }
}
pickerPagto1.ItemsSource = obsObservacao;
pickerPagto1.UpdateLayout();
The Problem:
private void botaoObs_Click(object sender, RoutedEventArgs e)
{
testarObs = "1";
pickerPagto1.Open();
}
As others have noted the ListPicker control must always have a selected item.
The way around this is to add a default first item to the list. You havent posted the code where you populate your collection, but this might be a good generic solution:
protected ObservableCollection<observacao> _obsObservacao;
public ObservableCollection<observacao> obsObservacao
{
get { return _obsObservacao;}
set {
_obsObservacao = value;
observacao noSelection = new observacao();
noSelection.descricao ="Nothing Selected";
_obsObservacao.Insert(0, noSelection);
}
}
Try to set first item like "Select one of..." - and add condition, when this is selected, for not to write into database.
Big thanks to the people who helped me out yesterday, however, after many hours of trying to fix this on my own, I can't find the solution.
I have my taskStructure class with it's list shown below:
public partial class Form1 : Form
{
public class taskStructure
{
public string taskName { get; set; }
public string taskDescription { get; set; }
public int Priority { get; set; }
public string dateAndTime { get; set; }
}
public List<taskStructure> TasksArray = new List<taskStructure>(); //Delcared a list data structure
And on my addTask form I have this to assign the rich text boxes to the appropriate variables:
private void createTaskBtn_Click(object sender, EventArgs e)
{
Form1 welcomeForm = new Form1();
welcomeForm.TasksArray.Add(new Form1.taskStructure
{
taskName = taskNameRTB.Text,
taskDescription = taskDescRTB.Text
});
//Test outputs
MessageBox.Show(welcomeForm.TasksArray[0].taskName);
MessageBox.Show(welcomeForm.TasksArray[0].taskDescription);
MessageBox.Show(welcomeForm.TasksArray[1].taskName);
MessageBox.Show(welcomeForm.TasksArray[1].taskDescription);
this.Close();
The inputs from the first set of [0] tests come up fine, but [1] throws the 'ArgumentOutOfRangeException',
So I did this:
private void createTaskBtn_Click(object sender, EventArgs e)
{
Form1 welcomeForm = new Form1();
welcomeForm.TasksArray.Add(new Form1.taskStructure
{
taskName = taskNameRTB.Text,
taskDescription = taskDescRTB.Text
});
welcomeForm.TasksArray.Add(new Form1.taskStructure
{
taskName = taskNameRTB.Text,
taskDescription = taskDescRTB.Text
});
//Test outputs
MessageBox.Show(welcomeForm.TasksArray[0].taskName);
MessageBox.Show(welcomeForm.TasksArray[0].taskDescription);
MessageBox.Show(welcomeForm.TasksArray[1].taskName);
MessageBox.Show(welcomeForm.TasksArray[1].taskDescription);
this.Close();
And now it works, however, the values I enter for [0] also are in [1].
I have also tried a foreach to get them to output:
foreach (taskStructure task in TasksArray)
{
MessageBox.Show(task);
}
But I get a: The best overloaded method match for 'System.Windows.Forms.MessageBox.Show(string)' has some invalid arguments
So my questions are: How do I add multiple items without them overwriting the first task that I enter? and how do I get it to output all of them to see if they are being stored correctly?
Basically: I want to add a task, save it to the list, add another task, save that to the list etc. etc. and then output them all to the user.
Kind Regards,
Kieran
Using the provided code, I am getting the expected behaviour of different values per item.
Please check that when you are testing the code, that you change the values that you are retrieving from taskNameRTB.Text and taskDescRTB.Text
I tested the code with:
welcomeForm.TasksArray.Add(new Form1.taskStructure
{
taskName = "SomeName1",
taskDescription = "SomeDesc1"
});
welcomeForm.TasksArray.Add(new Form1.taskStructure
{
taskName = "SomeName2",
taskDescription = "SomeDesc2"
});
Below is an example of overriding the ToString() method for the taskStructure class, this helps with outputting the information to the user.
public class taskStructure
{
public string taskName { get; set; }
public string taskDescription { get; set; }
public int Priority { get; set; }
public string dateAndTime { get; set; }
public override string ToString()
{
// This can be changed to output any form of string that you want your object to represent
StringBuilder output = new StringBuilder();
output.AppendLine(String.Format("taskName: {0}", taskName));
output.AppendLine(String.Format("taskDescription: {0}", taskDescription));
output.AppendLine(String.Format("Priority: {0}", Priority));
output.AppendLine(String.Format("dateAndTime: {0}", dateAndTime));
return output.ToString();
}
}
That ToString() then allows the below code to replace the different MessabeBoxshow() that you have
foreach (Form1.taskStructure singleTask in welcomeForm.TasksArray)
{
MessageBox.Show(singleTask.ToString());
}
I don't have enough reputation to comment, so I have to post this as an answer. I just wanted to add that if you want to add tasks to the existing list on every click of the button (which I assume you do), you should move this line
Form1 welcomeForm = new Form1();
outside of the button click event handler, for example to the onload event handler, because now on every click you create a new form which creates a new empty list and what you've added on the previous click will be lost.
Edit: Try that
public partial class addTask : Form
{
Form1 welcomeForm;
public addTask() { InitializeComponent(); }
private void addTask_Load(object sender, EventArgs e)
{
welcomeForm = new Form1();
}
}
The issue has now been fixed and everything works as expected my problem was this:
Form1 welcomeForm
and in my Form_Load event handler
welcomeForm = new Form1();
Every time I was adding new tasks to the list, is was creating a new form to go over it, thus it was not saving to the list correctly. So it went from this:
welcomeForm.TasksArray.Add(new taskStructure
{
taskName = taskNameRTB.Text,
taskDescription = taskDescRTB.Text
});
To:
taskStructure.TasksArray.Add(new taskStructure
{
taskName = taskNameRTB.Text,
taskDescription = taskDescRTB.Text
});
foreach (taskStructure singletask in taskStructure.TasksArray)
{
MessageBox.Show(singletask.ToString());
}
I have added that class and the list to a separate class file to help keep the code looking cleaner, and thus changed the code accordingly ^. The tasks now save correctly, and the list works as expected. I would like to really thank everyone for the help and advice, and because of this help I will not hesitate to use this again as I have learnt a lot from my two questions alone!
I am using MVVM Light in a (pretty simple) WPF project.
I have a list of versions, and for each of them there is a button "activate" and "archive". Only one version can be active.
When clicking on "activate", the software must archive the currently active version, and activate the selected one.
How would you modelize this ? I'm currently using a pretty ugly solution : the selected version re-instantiates the "active version" and archives it, so obviously the previously-active version isn't "refreshed".
The main window contains a list of versions, displayed in a ListBox (see this question).
public class MainWindowViewModel : ViewModelBase
{
public MainWindowViewModel()
{
this.InstalledVersions = InstalledVersionViewModel.GetInstalledVersions();
}
public ObservableCollection<InstalledVersionViewModel> InstalledVersions { get; set; }
}
The InstalledVersionViewModel is (simplified) like this :
public class InstalledVersionViewModel : ViewModelBase
{
public InstalledVersionViewModel()
{
this.HandleActivateVersionCommand = new RelayCommand<RoutedEventArgs>(e => { this.ActivateVersion(); });
this.HandleArchiveVersionCommand = new RelayCommand<RoutedEventArgs>(e => { this.ArchiveVersion(); });
}
public string FolderPath { get; set; }
public RelayCommand<RoutedEventArgs> HandleActivateVersionCommand { get; private set; }
public RelayCommand<RoutedEventArgs> HandleArchiveVersionCommand { get; private set; }
public string VersionNumber { get; set; }
public static InstalledVersionViewModel GetCurrentVersion()
{
return GetVersionInfos(baseInstallPath); // returns the currently-active version
}
public static ObservableCollection<InstalledVersionViewModel> GetInstalledVersions()
{
var list = new ObservableCollection<InstalledVersionViewModel>();
// snip : fill the list from detected versions
return list;
}
private void ActivateVersion()
{
// snip
GetCurrentVersion().Archive();
// snip
}
private void ArchiveVersion()
{
// snip
}
}
The problem is in the ActivateVersion() method : I'm getting a new version instance to archive it, so obviously the version instance in the list is never aware of this change. But I don't know how to change the behavior to archive the version in the list instead. I'm pretty sure there should be either some kind of messaging system, a wrapper or an overarching structure, but I can't quite put my finger on it.
Thanks !
To me, it should be handled in the MainViewModel. For instance, add a property IsActive to your InstalledVersionViewModel, and subscribe to the PropertyChanged event from your MainViewModel. When the event is raised, browse your InstalledVersions list to find the previously active item, and call the Archive method on it.
To subscribe to the event, simply browse your list after creating it:
public MainWindowViewModel()
{
this.InstalledVersions = InstalledVersionViewModel.GetInstalledVersions();
foreach (var version in this.InstalledVersions)
{
version.PropertyChanged += this.VersionPropertyChanged;
}
}
Then, in the event, check which property has been changed:
private void VersionPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsActive")
{
var changedVersion = (Version)sender;
// Checks that the version has been activated
if (changedVersion.IsActive)
{
// Finds the previously active version and archive it
foreach (var version in this.InstalledVersions)
{
if (version.IsActive && version != changedVersion)
{
version.Archive();
}
}
}
}
}