i have a problem with DataBinding on Xamarin.Forms and i hope you can help me out.
I have two classes calles angebot (quotation) and adresse (address) looks like this:
public class angebot
{
public string ADRESSE { get; set; }
public string ANGEBOT { get; set; }
// ... and more
}
public class adresse
{
public string ADRESSE { get; set; }
public string NAME { get; set; }
// ...and more
}
These classes are used on another class for data holding:
private IEnumerable<recDataClass> recData = null;
private class recDataClass
{
public Database.Tabellen.angebot angebotData = null;
public Database.Tabellen.adresse adresseData = null;
}
After filled the data classes with data i joined them togeteher in the recData class.
recData =angData.Join (adrData,
angebot => angebot.ADRESSE,
adresse => (adresse.ADRESSE),
(angebot, adresse) => new recDataClass {
angebotData = angebot,
adresseData = adresse
});
Till this step everything is working really fine. recData holds the correct data.
Now i want to set the databinding for my xaml
recDataPage.BindingContext = recData.ElementAt(index);
Now to my question. How do i set the databinding in my xaml correctly to show my data?
<Entry Text="{Binding ANGEBOT}" x:Name="enAngebot" Keyboard="Text" WidthRequest="350" VerticalOptions="Center" HorizontalOptions="StartAndExpand" />
Text="{Binding ANGEBOT}"
Text="{Binding recData.angebotData.ANGEBOT}"
Text="{Binding angebotData.ANGEBOT}"
are not working
What i'm doing wrong?
Thx forward
DataBinding only works on property, so you should change the fileds on recDataClass to property like:
private class recDataClass
{
public Database.Tabellen.angebot angebotData {get;set;}
public Database.Tabellen.adresse adresseData {get;set;}
}
Related
I am working to build an app which will use the code behind to supply new text via bindings when you click the next button. Each time I set the page to load on the virtual phone it times out the hot reload... The worst part is that I am not getting any errors either. Any ideas?
Content Page:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="List.MainPage">
<ContentPage.Content>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<Label Text="{Binding TitleText}" />
<ScrollView VerticalOptions="FillAndExpand">
<StackLayout>
<Label Text="{Binding EngText}" />
<Label Text="{Binding ItText}" />
</StackLayout>
</ScrollView>
<Button Text="Next Page" Clicked="OnNavigateButtonClicked" />
</StackLayout>
</ContentPage.Content>
Code Behind:
using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace List
{
public partial class MainPage : ContentPage
{
List<MainPage> Contacts { get; set; }
public string TitleText { get; set; }
public string EngText { get; set; }
public string ItText { get; set; }
int ndx = 0;
public MainPage()
{
InitializeComponent();
Contacts = new List<MainPage>
{
// repeat this for as many contacts as you need
new MainPage
{
TitleText = "Title1",
EngText = "EngText1",
ItText = "ItText1"
},
new MainPage
{
TitleText = "Title2",
EngText = "EngText2",
ItText = "ItText2"
},
};
// display the first contact
BindingContext = Contacts[ndx];
}
void OnNavigateButtonClicked(object sender, EventArgs e)
{
// increment your index
ndx++;
// check that we haven't gone too far
if (ndx < Contacts.Count)
{
BindingContext = Contacts[ndx];
}
}
}
}
you are using the same class MainPage for your UI and your data. The constructor of MainPage creates 2 new instances of `MainPage, each of which call their constructor and create 2 more instances, which recurses forever until you use all the memory and crash
you need two different classes, one for your UI and one for your data
public class Data
{
public string TitleText { get; set; }
public string EngText { get; set; }
public string ItText { get; set; }
}
public partial class MainPage : ContentPage
{
List<Data> Contacts { get; set; }
int ndx = 0;
public MainPage()
{
InitializeComponent();
Contacts = new List<Data>
{
// repeat this for as many contacts as you need
new Data
{
TitleText = "Title1",
EngText = "EngText1",
ItText = "ItText1"
},
new Data
{
TitleText = "Title2",
EngText = "EngText2",
ItText = "ItText2"
},
};
I have an ObservableRangeCollection that consists of a few items. How can I conduct a search in the ViewModel of the Page?
Edit:
ObservableCollection in ViewModel:
private ObservableRangeCollection<Smetka> _smetki = new ObservableRangeCollection<Smetka>();
public ObservableRangeCollection<Smetka> Smetki
{
get { return _smetki; }
set { SetProperty(ref _smetki, value); }
}
And this is the CollectionView in the View:
<CollectionView ItemsSource="{Binding Smetki}" BackgroundColor="{DynamicResource SecondaryColor}"
VerticalScrollBarVisibility="Never" HorizontalScrollBarVisibility="Never"
SelectionMode="Single" SelectionChangedCommand="{Binding LaunchDetailPage}"
SelectedItem="{Binding SelectedSmetka}" Grid.Row="1">
</CollectionView>
I have been searching for a VM search example for a while now. I would really appreciate it if someone could provide me with one.
Edit 2:
This is what the app looks like normally.
When Search Criteria is given the ObservableRangleCollection should display only the Smetkas, who meet the Criteria.
After removing the Search Criteria it should display all the Smetkas, who meet the new criteria. If the String is empty or whitespace it should display all of them.
Do you mean you want to retrieve the item from your list ?
If yes,just use LINQ,for example:
public class PLU
{
public int ID { get; set; }
public string name { get; set; }
public double price { get; set; }
public int quantity {get;set;}
}
public static ObservableCollection<PLU> PLUList = new ObservableCollection<PLU>();
retrieve like:
PLU item = PLUList.Where(z => z.ID == 12).FirstOrDefault();
Update:
string filter = "9";//the filter you input
var searchItems = new ObservableCollection<Smetka>(Smetki.Where((smetka) => smetka.Id.Contains(filter))); // Assuming the property is Id.
then you could replace the Smetki with searchItems .
I have a xaml file with with an entry. I am binding the specific entry to a specific viewmodel. But the viewmodel expects a Navigator. How can I pass the navigator from the xaml file to the viewmodel?
<Entry Text="{Binding Signature, Mode=TwoWay}">
<Entry.BindingContext>
<vm:SignaturePopupViewModel>
// I need to pass a navigator..
</vm:SignaturePopupViewModel>
</Entry.BindingContext>
</Entry>
The viewmodel expects a navigation object. I use it to pop the page to go back to the previous page after running some code logic.
public SignaturePopupViewModel(INavigation navigation = null)
{
Navigation = navigation;
SendSignatureCommand = new Command(async () =>
{
await SendSignature();
await Navigation.PopAsync();
});
}
You do not need to use INavigation navigation in your SignaturePopupViewModel in your constructor to achieve the Navigation.
Just use a simple way is
await Application.Current.MainPage.Navigation.PopModalAsync(); Or
await Application.Current.MainPage.Navigation.PopAsync()
like following code.
public class SignaturePopupViewModel
{
public ICommand SendSignatureCommand { protected set; get; }
public SignaturePopupViewModel( )
{
SendSignatureCommand = new Command(async () =>
{
await SendSignature();
// if you use the MainPage = new NavigationPage( new MainPage()); in
//App.xaml.cs use following code.
await Application.Current.MainPage.Navigation.PopAsync();
// if not, just use await Application.Current.MainPage.Navigation.PopModalAsync();
});
}
}
Could you create an instance of the SignaturePopupVM in the ViewModel of that page and then bind the Text to that property instead?
VM:
SignaturePopupViewModel SignaturePopupVMInstance { get; private set; }
public ParentVM()//Constructor
{
SignaturePopupVMInstance = new SignaturePopupViewModel(new Navigator());
}
Xaml:
<Entry Text="{Binding SignaturePopupVMInstance.Signature, Mode=TwoWay}"/>
Edit:
public class TabPageVM{
public ChildVM TheVMForTabOne { get; set; }
public AnotherChildVM TheVMForTabTwo { get; set; }
public TabVM TheVMForTabThree { get; set; }
public TabPageVM(){
TheVMForTabOne = new ChildVM(/*parameters*/);
TheVMForTabTwo = new AnotherChildVM(/*parameters*/);
TheVMForTabThree = new TabVM(/*parameters*/);
}
}
Xaml for tabpage:
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:Views="clr-namespace:App.ViewsForMyTabs"
x:Class="App.TabPageView"
BarBackgroundColor="#EEEEEE"
BarTextColor="Black"
BindingContext="{Binding TheTabbedPageVMInstance}">
<TabbedPage.Children>
<Views:TheViewForTabOne x:Name="TabOneView"
BindingContext="{Binding TheVMForTabOne}"/>
<Views:TheViewForTabTwo x:Name="TabTwoView"
BindingContext="{Binding TheVMforTabTwo}"/>
<Views:TheViewForTabThree x:Name="TabThreeView"
BindingContext="{Binding TheVMforTabThree}"/>
</TabbedPage.Children>
</TabbedPage>
Lets say TheViewForTabOne has the button on it that takes you to the new page. The VM for that view "TheVMForTabOne" would have something like this:
public class ChildVM{
public SignaturePopupViewModel SignaturePopupVMInstance { get; set; }
public Command NavigateToNewPageWithEntry { get; private set; }
public ChildVM(){
SignaturePopupVMInstance = new SignaturePopupViewModel(/*parameters*/);
NavigateToNewPageWithEntry = new Command(() =>{
//Navigate to new page with SignaturePopupVMInstance as the BindingContext
}
}
}
TheViewForTabOne
...
<Label Text="{Binding SignaturePopupVMInstance.Signature}"/>
<Button Command="{Binding NavigateToNewPageWithEntry}"/>
...
I'm making a shopping app, it has account for the user, and when the user purchases a product, it should be added to a listview in his account. So I tried to put a static object of the User Class that has a list of Products, and whenever the user clicks the buying button, it should be added to the list. At the same time, the user.xaml is binding to the same object. But it doesn't work. What's the error in my method?
Are there any better ideas to do this?
here's the static field in the App.xaml.cs file
private IDataService _dataService;
public static User TheUser;
public App(IDataService dataService)
{
TheUser = new User();
InitializeComponent();
var unity = new UnityContainer();
unity.RegisterType<IDataService, DataServices>();
ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(unity));
_dataService = dataService;
MainPage = new NavigationPage(new MainPage());
}
and here's the User.xaml.cs property
public User User
{
get { return App.TheUser; }
set
{
if(App.TheUser!= null)
App.TheUser = value;
}
//User class
public class User : Base //Base class implements INotifyPropertyChanged
{
public int Id { get; set; }
public string Name { get; set; }
public ObservableCollection<Product> Products = new ObservableCollection<Product>();
}
public class Base : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Here's the User.Xaml file
<StackLayout>
<AbsoluteLayout>
<Image AbsoluteLayout.LayoutBounds="1 , 0 ,96 ,96" AbsoluteLayout.LayoutFlags="PositionProportional"/>
<Label AbsoluteLayout.LayoutBounds="0 , 50 , 100 , 20" AbsoluteLayout.LayoutFlags="XProportional" Text="First Name"/>
<Label AbsoluteLayout.LayoutBounds="0 , 100 , 100 , 20" AbsoluteLayout.LayoutFlags="XProportional" Text="Last Name"/>
</AbsoluteLayout>
<ListView x:Name="UserListView"
SelectedItem="{Binding SelectedItemCommand}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Spacing="3" FlowDirection="RightToLeft" >
<Image Source="{Binding ProductMainImage}" Aspect="AspectFill" Margin="3" HeightRequest="300" />
<Label Text="{Binding Name ,StringFormat=' الاسم : {0}'}"/>
<Label Text="{Binding Price ,StringFormat=' السعر : {0}'}"/>
<Label Text="{Binding Description ,StringFormat=' الوصف : {0}'}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
ok so the property notification doesn't happen automatically. You need to invoke that code to raise the event. This should takes care of the code, but without seeing the xaml, I don't know if the binding is setup correctly.
public User User
{
get { return App.TheUser; }
set
{
if(App.TheUser!= null)
App.TheUser = value;
}
//User class
public class User : Base //Base class implements INotifyPropertyChanged
{
private int _id
public int Id {
get{
return this._id;
}
set{
this._id = value;
OnPropertyChanged("Id");
}
}
private string _name;
public string Name {
get{
return this._name;
}
set{
this._name = value;
OnPropertyChanged("Name");
}
}
private ObservableCollection<Product> _products;
public ObservableCollection<Product> Products
{
get{
return this._products;
}
set{
this._products = value;
OnPropertyChanged("Products");
}
}
}
}
so your listview is not bound to anything...
<ListView x:Name="UserListView"
ItemsSource={Binding Products}
SelectedItem="{Binding SelectedItemCommand}">
<ItemsControl DockPanel.Dock="Right" x:Name="Actions">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="Action"
HorizontalAlignment="Right"
Content="{Binding Label}"
Margin="3" Width="30"></Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
The above view binds with this viewmodel
public class DeploymentInputViewModel<T> : PropertyChangedBase
{
public BindableCollection<InputActionViewModel> Actions {get;set;}
}
I see my buttons. But when clicking it nothing happen.
The viewModels for InputActionViewModel:
public abstract class InputActionViewModel{
public InputActionViewModel()
{
}
public virtual Task Action()
{
return Task.FromResult<object>(null);
}
public string ActionToolTip { get; set; }
public string Label { get; set; }
public object Value { get; set; }
}
and also
public class InputCertificateActionViewModel : InputActionViewModel
{
[Import]
private IShell _shell;
[Import]
private IWindowsDialogs _dialogs;
private readonly IDeploymentSettingInputViewModel vm;
public InputCertificateActionViewModel(IDeploymentSettingInputViewModel vm)
{
this.vm = vm;
Label = "...";
ActionToolTip = "Pick a Certificate";
}
public bool IsManagementCertificate {get;set;}
public bool IsDeploymentCertificate { get; set; }
public async override Task Action()
{
if(IsManagementCertificate)
{
var subs = await _shell.IdentityModel.GetEnabledSubscriptionsAsync();
foreach(var sub in subs)
{
using (ManagementClient client = CloudContext.Clients.CreateManagementClient(sub.GetCredentials()))
{
var cert = _dialogs.SelectItemDialog("Select a certificate", "Pick one", true,
(await client.ManagementCertificates.ListAsync()).Select(c =>
new SelectItem(c.Thumbprint, Encoding.Default.GetString(c.PublicKey), c, (s) => c.Thumbprint.Contains(s))).ToArray())
.Tag as ManagementCertificateListResponse.SubscriptionCertificate;
this.vm.Value = cert.Thumbprint;
}
}
}else if(IsDeploymentCertificate)
{
}
}
}
I am adding actionViewModels by inserting directly into the observable code at startup.
haveActions.Actions.Add(DI.BuildUp(new InputCertificateActionViewModel(vm)
{
IsDeploymentCertificate = certAttribute.IsDeploymentCertificate,
IsManagementCertificate = certAttribute.IsManagementCertificate,
}));
haveActions is an instance of InputCertificateActionViewModel
Couldn't fit this all in a comment:
I can't have a peek at the Caliburn.Micro at the moment, but it might be something related to calling your method Action.
At a guess though, I'd say that by convention Caliburn.Micro expects to find a method that matches the Action<T> delegate to use for it's Actions, so your public virtual Task Action() won't be located and bound.
Have a quick check by defining a new method with a compatible signature, e.g public void MyMethod() and checking to see if it's located correctly and will function.
If that is the problem, you'll probably want to have a look at the IResult and Coroutines part of the Caliburn.Micro documentation, which looks like it will help you implement your desired behaviour.