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.
Related
There are two ways that I tried to add children to a Grid. One is working but it is not prefered to me because I have ViewModel class. And I want to apply it in that class. So the another way is not working correctly, actually, it is not added as children to Grid. The last way is applied in ViewModel class. Here is two ways that I applided:
1- Working way.
public LaserLib fiber1 = new LaserLib();
private void MarkInit()
{
fiber1.Init(); // that is my specialized laser library.
myGrid.Children.Add(fiber1); // when I do this, I can see the fiber1 condition in grid.
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
MarkInit();
}
But, I have many of this and I want to appy to my viewModel.
What I tried:
2- Not worked. Way 1:
public class ViewModels
{
public ObservableCollection<MarkingManualStatuses> markingManualStatuses { get; set;}
= new ObservableCollection<MarkingManualStatuses(Enumerable.Range(0,4).Select(i => new MarkingManualStatuses()));
}
public class MarkingManualStatuses : INotifyPropertyChanged
{
public LaserLib Tmc { get; set; }
public void TmcInitialize()
{
Tmc = new LaserLib ();
Tmc.Init();
}
}
I run TmcInitialize() function at MainWindow_Onloaded
Everything is fine for now. My LaserLib gets initialize. it works. But, I cannot add it to grid.
Here is where I tried to add at ManualPage:
private void listViewMarkingInfos_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
TmcViewerGrid.Children.Clear();
int index = listViewMarkingInfos.SelectedIndex;
ContainerMarkingDockPanel.DataContext = viewModels.markingManualStatuses[index];
TmcViewerGrid.Children.Add(viewModels.markingManualStatuses[index].Tmc); // this is not working
ContainerMarkingDockPanel.Visibility = Visibility.Visible;
}
3. Not worked. Way 2:
I have added
Grid property
in
MarkingManualStatuses
It sounds bad but anyway, I just wanted to see if it works or not.
In viewModel:
public class MarkingManualStatuses : INotifyPropertyChanged
{
public LaserLib Tmc { get; set; }
public Grid myGrid {get;set;}
public void TmcInitialize()
{
Tmc = new LaserLib ();
Tmc.Init();
myGrid = new Grid();
myGrid.Children.Add(Tmc);
}
}
In Manual_Page:
private void listViewMarkingInfos_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
TmcViewerGrid.Children.Clear();
int index = listViewMarkingInfos.SelectedIndex;
ContainerMarkingDockPanel.DataContext = viewModels.markingManualStatuses[index];
TmcViewerGrid = viewModels.markingManualStatuses[index].myGrid; // this is also not working.
ContainerMarkingDockPanel.Visibility = Visibility.Visible;
}
I have tried two ways but it didn't work. Any suggestions to solve this?
And maybe I missed a small point that I forget, I am not sure, but Thank you all.
I have been trying to solve the following problem for a very long time but unfortunately, I am unable to get it fixed.
I have a button which I want to disable it in another page .cd
This is how my code looks like:
<StackLayout>
<Button Text="Click" IsEnabled="{Binding IsButtonEnabled}" Command="{Binding OnEnabledButtonClicked}"/>
</StackLayout>
public class MainPageViewModel : BaseViewModel
{
bool _isButtonEnabled;
public bool IsButtonEnabled
{
get => _isButtonEnabled;
set
{
_isButtonEnabled = value;
OnPropertyChanged(nameof(IsButtonEnabled));
}
}
public Command OnEnabledButtonClicked
{
get
{
return new Command( () =>
{
IsButtonEnabled = true;
}
}
}
}
And this is the class where I want to change the value of VM's button.
public class Page1 {
class page1() {
InitializeComponent();
}
public void OnDisabledButtonClicked(object sender, EventArgs e) {
/// IsButtonEnabled = false;
}
}
I have already tried different ways but still no result.
It would be a big help for me if someone provides me a solution for it.
Thanks in advance
Before I gave you an answer, I would like to point out few things.
It's common to suffix your command with Command : EnabledButtonCommand.
I can see that your command is async while you don't await anything. It's bad.
Why would you want to set IsButtonEnabled in the code behind instead of in the method executed by the command (in the ViewModel) ?
Where do you set the DataContext ? Do you use Prism or anything else to associate the ViewModel to your page's DataContext ? If you don't, you need to do this :
public class MyPage()
{
private MyViewModel _viewModel = new MyViewModel();
public MyPage()
{
InitializeComponent();
DataContext = _viewModel;
}
public void OnDisabledButtonClicked(object sender, EventArgs e)
{
_viewModel.IsButtonEnabled = false;
}
}
If your ViewModel was set in Xaml or elsewhere (while navigating, with Prism, etc)
public class MyPage()
{
private MyViewModel _viewModel;
public MyPage()
{
InitializeComponent();
_viewModel = DataContext as MyViewModel;
}
public void OnDisabledButtonClicked(object sender, EventArgs e)
{
_myViewModel.IsButtonEnabled = false;
}
}
A last word : if a button is not enabled, the command/click event won't be available to user until the button is enabled again.
You can test that with a button with its command binded to the given command and another button with IsEnabled binded to your boolean.
The code I gave may have things wrong as I answer in browser without using an EDI.
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!
I'm developing a Windows application (UWP) that has two pages, I want the best practice to pass parameters between pages.
it's my scenario:
We have two pages, each open and remain at the middle of the screen and a Button on each page, which send the message to the other page when we click on it.
I also want to pass information continuously and repeatedly.
in Page1.cs:
Page2 page2;
public Page1()
{
this.InitializeComponent();
CreatPage2();
}
// creat page 2
private async void CreatPage2()
{
var NewWindow = CoreApplication.CreateNewView();
int NewWindowid = 0;
await NewWindow.Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
Frame newframe = new Frame();
newframe.Navigate(typeof(Page2), this);
Window.Current.Content = newframe;
Window.Current.Activate();
ApplicationView.GetForCurrentView().Title = "page2";
NewWindowid = ApplicationView.GetForCurrentView().Id;
});
await Windows.UI.ViewManagement.ApplicationViewSwitcher.TryShowAsStandaloneAsync(NewWindowid);
}
//Button
private void ChangeP2_Click(object sender, RoutedEventArgs e)
{
// send a message to the texblock in the page2
page2.TexBlock2.Text=$"From page1 :{e.ToString()}";
// change text color of the texblock in the page2
page2.Foreground= new SolidColorBrush(Windows.UI.Colors.Red);
}
in Page2.cs:
Page1 page1;
protected override void OnNavigatedTo(NavigationEventArgs e)
{
page1 = e.Parameter as Page1;
base.OnNavigatedTo(e);
}
public Page2()
{
this.InitializeComponent();
}
//Button
private void ChangeP1_Click(object sender, RoutedEventArgs e)
{
// send a message to the texblock in the page1
page1.TexBlock1.Text=$"From page2 :{e.ToString()}";
// change text color of the texblock in the page1
page1.Foreground= new SolidColorBrush(Windows.UI.Colors.Red);
}
the above code just work for the page2 to the page1. (it can change the textblock of pagea).
Please help me, I can't find a solution that work on two pages
Naah… the best way is to use a standard pattern that consist of an app ViewModel class, which contains all the common app data that you want to use in the logic layer.
I always do it like this:
1) I use the MainPage automatically created as the "shell" of the app, with a property that is the AppViewModel.
The MainPage (and thus the AppViewModel) can be accessed from everywhere in the app, by setting itself as a static field in its own class.
This is the code, simpler than you think:
public sealed partial class MainPage : Page
{
public AppViewModel ViewModel { get; set; } = new AppViewModel();
public static MainPage Current { get; set; }
public MainPage()
{
this.InitializeComponent();
Current = this;
}
}
2) The AppViewModel itself is a class that must implement the INotifyPropertyChanged interface, in order to enable bindable properties and functions.
It is common, among developers, to create a base class that implements it and then derive all the classes that needs bindable properties from it.
Here it is:
public class BaseBind : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
protected bool SetProperty<T>(ref T storage, T value,
[CallerMemberName] String propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
}
Then you derive AppViewModel class (and all the other model and viewmodel classes) from it… populating it with all the common properties that you need to share across pages.
I have even added a derived property, in order to show how you can share even multiple data types at once, and a function:
public class AppViewModel : BaseBind
{
public AppViewModel()
{
// ...
}
// All common app data
private string sampleCommonString;
public String SampleCommonString
{
get { return sampleCommonString; }
set { SetProperty(ref sampleCommonString, value); OnPropertyChanged(nameof(SampleDerivedProperty1)); OnPropertyChanged(nameof(SampleDerivedProperty2)); }
}
public String SampleDerivedProperty1 => "return something based on SampleCommonString";
public String SampleDerivedProperty2
{
get
{
<<evaluate SampleCommonString>>
return "Same thing as SampleDerivedProperty1, but more explicit";
}
}
// This is a property that you can use for functions and internal logic… but it CAN'T be binded
public String SampleNOTBindableProperty { get; set; }
public void SampleFunction()
{
// Insert code here.
// The function has to be with NO parameters, in order to work with simple {x:Bind} markup.
// If your function has to access some specific data, you can create a new bindable (or non) property, just as the ones above, and memorize the data there.
}
}
3) Then, in order to access all this from another Page, just create an AppViewModel field in that page, as seen below:
public sealed partial class SecondPage : Page
{
public AppViewModel ViewModel => MainPage.Current.ViewModel;
public SecondPage()
{
this.InitializeComponent();
}
}
...and you can easily bind XAML controls properties to the AppViewModel itself:
<TextBlock Text="{x:Bind ViewModel.SampleCommonString, Mode=OneWay}"/>
<Button Content="Sample content" Click="{x:Bind ViewModel.SampleFunction}"/>
(Mode=OneWay is for real-time binding, in order that the property is immediately updated even in the UI, while Mode=TwoWay is used for those properties that can be edited from the control itself, by the user, in order to interact with app logic).
Hope this helped.
Best regards and happy new year.
I'm struggling with WinForms data binding. The issue is that the controls should be bound to a model but they just throw ArgumentException without any reasonable reason. It's thrown exactly when a TabPage shows the control in question. This is the following Exception.Message: Unable to link property or Mpn column in DataSource.
So, I can't figure out what's going wrong. It seems to be ok.
It's worth to mention that I'm implementing a "forcing" MVVM to WinForms and the pattern was working fine until now (lol!). I already know that MVVM goes better with WPF and Silverlight, so please, focus on the issue.
I leave the code below and some explanation about how they are composed:
View: It has a TabControl with many tabs page inside and those TabPage's have controls such as: Labels, TextBox, ComboBox and so on.
public sealed partial class ProductView : ViewBase
public ProductView(ProductViewModel viewModel) : base(viewModel) => InitializeComponent();
public sealed async override void BindViewModel()
{
ProductViewModel pvm = ViewModel as ProductViewModel;
await Task.Run(() => { while (pvm.Loading) ; });
DataSourceUpdateMode updateMode = DataSourceUpdateMode.OnPropertyChanged;
// Product Tab Data
materialTextField_mpn.DataBindings.Add("Text", pvm, nameof(pvm.Product.Mpn), false, updateMode);
}
private void ProductView_Load(object sender, EventArgs e){
(ViewModel as ProductViewModel).Load.Execute(null);
BindViewModel();
}
}
ViewModel
public sealed class ProductViewModel : ViewModelBase
{
private bool loading;
private int product_id;
private readonly IRepository<ProductModel> repository;
private ICommand load;
public ProductViewModel(IRepository<ProductModel> repository) => this.repository = repository;
public ProductModel Product { get; private set; }
private async void LoadData()
{
Loading = true;
product_id = 0; // Hardcoded for testing purposes.
Product = product_id > 0 ? await repository.GetByIdAsync(product_id) : new ProductModel()
{
ID = 0,
Mpn = string.Empty
};
Loading = false;
}
Model
public class ProductModel : ModelBase
{
private string mpn;
public string Mpn {
get => mpn;
set
{
if (mpn == value)
return;
mpn = value;
OnPropertyChanged(nameof(mpn));
}
}
}
Many thanks in advance!
UPDATE & SOLUTION
After sleeping for a while and coming back to code. I realized that the Data Binding was wrong. Excatly this line:
materialTextField_mpn.DataBindings.Add("Text", pvm, nameof(pvm.Product.Mpn), false, updateMode);
Just change for this one:
materialTextField_mpn.DataBindings.Add("Text", pvm.Product, nameof(pvm.Product.Mpn), false, updateMode);
Don't be a dummy like me lol!